Re: [regression] x86/signal/64: Fix SS handling for signals delivered to 64-bit programs breaks dosemu

From: Andy Lutomirski
Date: Wed Sep 02 2015 - 17:39:56 EST


On Wed, Sep 2, 2015 at 2:01 PM, Stas Sergeev <stsp@xxxxxxx> wrote:
> 02.09.2015 22:06, Andy Lutomirski ÐÐÑÐÑ:
>
>> On Wed, Sep 2, 2015 at 11:23 AM, Stas Sergeev <stsp@xxxxxxx> wrote:
>>>
>>> 02.09.2015 21:17, Andy Lutomirski ÐÐÑÐÑ:
>>>>
>>>> On Wed, Sep 2, 2015 at 10:46 AM, Stas Sergeev <stsp@xxxxxxx> wrote:
>>>>>
>>>>> 02.09.2015 17:21, Andy Lutomirski ÐÐÑÐÑ:
>>>>>>>>
>>>>>>>> This should work for old DOSEMU. It's a bit gross, but it has the
>>>>>>>> nice benefit that everyone (even things that aren't DOSEMU) gain the
>>>>>>>> ability to catch signals thrown from bogus SS contexts, which
>>>>>>>> probably
>>>>>>>> improves debugability. It's also nice to not have the SA flag.
>>>>>>>
>>>>>>> Pros:
>>>>>>> - No new SA flag
>>>>>>> - May improve debugability in some unknown scenario where people
>>>>>>> do not want to just use the new flag to get their things improved
>>>>>>>
>>>>>>> Cons:
>>>>>>> - Does not allow to cleanly use siglongjmp(), as then there is a risk
>>>>>>> to jump to 64bit code with bad SS
>>>>>>
>>>>>> What's the issue here? I don't understand.
>>>>>>
>>>>>> On musl, (sig)longjmp just restores rsp, rbx, rbp, and r12-r15, so it
>>>>>> won't be affected. AFAIK all implementations of siglongjmp are likely
>>>>>> to call sigprocmask or similar, and that will clobber SS. I'm not
>>>>>> aware of an implementation of siglongjmp that uses sigreturn.
>>>>>
>>>>> I am not saying siglongjmp() will be affected.
>>>>> Quite the opposite: it won't, which is bad. :)
>>>>> If you have always correct SS, you can use siglongjmp(). If you have
>>>>> broken SS at times, siglongjmp() will be an asking for troubles, as
>>>>> it exactly does not restore SS.
>>>>> dosemu could do a good use of siglongjmp() to get back to 64bit code
>>>>> from its sighandler.
>>>>
>>>> This seems like it would be relying unpleasantly heavily on libc
>>>> internals.
>>>
>>> Could you please clarify?
>>> If kernel always passes the right SS to the sighandler, then what's
>>> the problem?
>>
>> What's the exact siglongjmp usage you have in mind? Signal context
>> isn't normally involved AFAIK.
>
> dosemu needs 2 return pathes:
> 1. to DOS code
> 2. to 64bit code (dosemu is not all in a sighandler, right?)
>
> How it is currently achieved:
> dosemu1:
> 1. sigreturn() + iret (to DOS)
> 2. modify sigcontext -> sigreturn() (to 64bit asm helper)
>
> dosemu2:
> 1. sigreturn() + iret (to DOS)
> 2. modify sigcontext -> sigreturn() -> longjmp() (to 64bit C-coded)

So you're modifying sigcontext such that it returns to a C function
that calls longjmp?

>
> How dosemu2 is supposed to do this:
> 1. sigreturn() (to DOS)
> 2. siglongjmp() (to 64bit C-coded)

This should work fine on any kernel, right? The main problem will be
that you presumably need to remember the old context so you can go
back to DOS, I assume. So SS needs to be there somewhere.

>
>>>>>>> - Async signals can silently "validate" SS behind your back
>>>>>>
>>>>>> True, and that's unfortunate. But async signals without SA_SAVE_SS
>>>>>> set with the other approach have exactly the same problem.
>>>>>
>>>>> Yes, and as such, they should be blocked.
>>>>> You could improve on that and on siglongjmp().
>>>>> And on TLS in the future.
>>>>
>>>> *I* can't do anything to siglongjmp because that's almost entirely
>>>> outside the kernel. :-/
>>>
>>> Except for passing the SS=__USER_DS to the sighandler, for which we
>>> discussed the new SA_hyz?
>>
>> I'm still not understanding what you're looking for. If you
>> siglongjmp out of a signal handler, the hardware SS value is
>> irrelevant, at least on 64-bit binaries, because siglongjmp is just
>> going to replace it.
>
> Hmm? IIRC you've just said this:
> ---
> On musl, (sig)longjmp just restores rsp, rbx, rbp, and r12-r15, so it won't
> be affected.
> ---
> So why would siglongjmp() replace it?

Because siglongjmp calls sigprocmask, which uses SYSCALL, which clobbers SS.

>
>>>>>>> Is the new SA flag such a big deal here to even bother?
>>>>>>
>>>>>> Not really, but given that the new behavior seems clearly better
>>>>>> behaved than the old, it would be nice to be able to have the good
>>>>>> behavior, or at least most of it, be the default.
>>>>>
>>>>> Surely, but how about then having the heuristics you suggest,
>>>>> only if the new SA_hyz is not set? And when it is set, have a
>>>>> properly defined and predictable behaviour. Then it seems like
>>>>> we'll get all the possible wishes covered.
>>>>
>>>> That could work. The result is quite similar to explicitly setting
>>>> UC_STRICT_RESTORE_SS.
>>>
>>> I am much more bothered with delivering the right SS than with
>>> restoring it on sigreturn().
>>
>> For 64-bit delivery, ignoring backwards compatibility, delivering
>> signals with ss = __USER_DS would be the right solution, I think: it's
>> trivial and it works. Because of backwards compatibility, we need to
>
> ... add the SA_hyz flag.
> I don't understand why do you constantly ignore that part as
> if it was never spelled. Lets discuss the proposal as a whole, rather
> than with the random bits thrown away. The flag is exactly for
> backward compatibility, so why do you present it as a problem
> without the context of the new flag?

For backwards compat, we either need the default behavior to be
unchanged, or we need the default behavior to be something that works
with existing dosemu. For existing dosemu, the only interesting cases
(I think) are signal delivery from *valid* 16-bit context, in which
case we need to preserve SS so that the signal handler can read it out
with mov ..., %ss, and sigreturn to 64-bit mode for the IRET
trampoline. For sigreturn, IIUC old dosemu will replace the saved CS
with a 64-bit code segment selector and won't touch the saved SS
because it doesn't know about the saved SS. Those dosemu versions
don't care what SS actually contains after sigreturn, because they're
immediately going to change it again using IRET. So we just need to
make sure we return without faulting.

New dosemu2 would like to sigreturn directly back to 16-bit mode, so
it needs the kernel to honor the saved ss value and restore it,
possibly changed by dosemu.

We obviously can't require old dosemu to set an SA flag to keep
working. But, if we can get away with it, I think it's somewhat
preferable not to require new DOSEMU to set an SA flag either.

This has one major benefit at least: if new dosemu loads some random
library that installs some async signal handler (SIGALRM for example),
everything will work with regard to CS and SS. If SIGALRM hits 16-bit
code, CS and SS get saved, the signal handler gets invoked in 64-bit
mode, and sigreturn restores the old state.

Of course, FS and GS still screw this up.

--Andy
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/