I found the answer after some research of the undocumented properties of the legend. The most direct answer to the problem in the question is to set the position of the legend title back to its place. This can be done with the property Position, that is part of the hidden property NodeChildren, of the legend title.
The legend box is effectively just a small axes object, so the position units are normalized relative to the axes box. If we look at it just after the creation of the legend we get:
[hleg,att] = legend('show');
title(hleg,'my title')
hleg.Title.NodeChildren.Position
ans =
0 0 0
Which is where we see the legend in the question. Now we can move it back to its correct place with:
hleg.Title.NodeChildren.Position = [0.5 1.5 0];
Here I placed it at the center of the horizontal axis (x = 0.5), above the vertical axis (y = 1.5), and the depth axis is irrelevant so it's zero (z = 0):
If you are not bothered by the option that the legend will interfere with the data, (e.g. your legend is outside the data axes) then you can stop here. If not, continue to read...
We could set the background color to white:
hleg.Title.NodeChildren.BackgroundColor = 'w';
But it doesn't look good because the background is only for the text box, and is not aligned with the legend box. So I prefer to enlarge the legend box to include the title and move the legend attributes down to make some space. It takes more effort (because we need to calculate the right position for all elements), but it can be done in the following way:
x = 1:0.1:10;
plot(x,sin(x),x,cos(x))
[hleg,icons,plots] = legend('show');
title(hleg,'my title')
hleg.Title.Visible = 'on';
% the addition in height needed for the title:
title_hight = hleg.Position(4)/numel(plots);
hleg.Position([2 4]) = [hleg.Position(2)-title_hight hleg.Position(4)+title_hight];
% calculate new position for the elements in the legeng:
new_pos = fliplr(0.5/(numel(plots)+1):1/(numel(plots)+1):1);
hleg.Title.NodeChildren.Position = [0.5 new_pos(1) 0];
% set the text to the right position:
leg_txt = findobj(icons,'Type','Text');
txt_pos = cell2mat({leg_txt.Position}.');
txt_pos(:,2) = new_pos(2:end);
set(leg_txt,{'Position'},mat2cell(txt_pos,[1 1],3));
% set the icons to the right position:
leg_att = findobj(icons,'Type','Line');
set(leg_att,{'YData'},mat2cell(repmat(repelem(new_pos(2:end).',...
numel(plots)),1,2),ones(numel(plots)*2,1),2))
The code above is also generalized for more the one data series, although I guess that there are cases where this will fail to give the right solution and will need some fine tunings.