Bug 205831 - Invalid use of x86 INT instruction should generate SIGILL, not SIGSEGV
Summary: Invalid use of x86 INT instruction should generate SIGILL, not SIGSEGV
Status: RESOLVED WILL_NOT_FIX
Alias: None
Product: Platform Specific/Hardware
Classification: Unclassified
Component: x86-64 (show other bugs)
Hardware: All Linux
: P1 normal
Assignee: platform_x86_64@kernel-bugs.osdl.org
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-12-10 21:18 UTC by Zack Weinberg
Modified: 2019-12-23 23:33 UTC (History)
2 users (show)

See Also:
Kernel Version: 5.3.0
Subsystem:
Regression: No
Bisected commit-id:


Attachments
test program (2.84 KB, text/plain)
2019-12-10 21:18 UTC, Zack Weinberg
Details

Description Zack Weinberg 2019-12-10 21:18:15 UTC
Created attachment 286247 [details]
test program

The only supported uses of the x86 INT instruction from a normal user-space program on Linux are INT 3 (breakpoint) and INT 0x80 (32-bit old-style system call).  The other 254 possible immediate operands to INT are forbidden and should, logically, result in delivery of SIGILL, with si_code set to something sensible (any of ILL_ILLTRP, ILL_ILLOPN, or ILL_PRVOPC would be justifiable).

What actually happens is that you get a SIGSEGV, with si_code set to SI_KERNEL and all other fields of the siginfo_t zero.  This is, at least, a potential source of user confusion (see for instance https://stackoverflow.com/q/59254571/388520 ) and could also make life harder for people writing user-mode DOS emulators and such, so I'm calling it a bug.  I suspect the issue is that the #GP fault handler is unaware that it should be handling this case specially.

The attached test program demonstrates the problem; it executes INT with all 256 possible immediate operands (and all the registers, except RSP, zeroed, to avoid accidentally making a valid system call) and prints information about each signal it receives.

I have not tried it (and the assembly language in the test program would need to be modified) but I expect the exact same bug exists for 32-bit processes and/or 32-bit x86 kernels.
Comment 1 Andy Lutomirski 2019-12-11 18:20:14 UTC
I don't think we can just go changing signal numbers, and I also disagree that SIGILL makes any sense here.

Unfortunately, the design and documentation is nonsense, but the fields in the ABI to look at are in the ucontext: both the trap number and error code are there.
Comment 2 Zack Weinberg 2019-12-11 18:51:22 UTC
I can see an argument that this can't be changed now for backward compatibility's sake.  I don't agree with it - I would argue that any program that would be broken by the proposed change is already buggy - but I see where you're coming from.

I do _not_ see why you think SIGILL doesn't make sense.  This is not an invalid access to memory, it's an attempt to execute an instruction that is forbidden to user space processes.  That is exactly what SIGILL/ILL_PRVOPC is supposed to mean.  "It's a #GP fault, so we report it as a SIGSEGV" only makes sense if you insist on wearing x86-colored glasses.

Also, needing to look at the ucontext to understand what's going on, when the kernel _could_ be exposing all of the necessary information in the siginfo, is a shitty thing to do to user space programmers.  ucontext is both too poorly documented, and way too machine-specific, to make people bother with if there's any alternative.
Comment 3 H. Peter Anvin 2019-12-13 21:14:26 UTC
On 2019-12-11 10:51, bugzilla-daemon@bugzilla.kernel.org wrote:
> https://bugzilla.kernel.org/show_bug.cgi?id=205831
> 
> --- Comment #2 from Zack Weinberg (zackw@panix.com) ---
> I can see an argument that this can't be changed now for backward
> compatibility's sake.  I don't agree with it - I would argue that any program
> that would be broken by the proposed change is already buggy - but I see
> where
> you're coming from.
> 
> I do _not_ see why you think SIGILL doesn't make sense.  This is not an
> invalid
> access to memory, it's an attempt to execute an instruction that is forbidden
> to user space processes.  That is exactly what SIGILL/ILL_PRVOPC is supposed
> to
> mean.  "It's a #GP fault, so we report it as a SIGSEGV" only makes sense if
> you
> insist on wearing x86-colored glasses.
> 
> Also, needing to look at the ucontext to understand what's going on, when the
> kernel _could_ be exposing all of the necessary information in the siginfo,
> is
> a shitty thing to do to user space programmers.  ucontext is both too poorly
> documented, and way too machine-specific, to make people bother with if
> there's
> any alternative.
> 

Uhm, INT is an x86 instruction, and it is NOT an illegal instruction; some INT
instructions are in fact permitted from user space. So the idea that this is
somehow "x86-colored glasses" is silly.

So,

1. First of all, we don't change a 28-year-old ABI which would break userspace.

2. SIGILL, on x86, typically doesn't provide information about the faulting
instruction as it is not available to the kernel without getting it out of
user space memory anyway. (In fact, x86 always reports ILL_ILLOPN

3. "Too poorly documented and way too machine-specific" -- how on Earth do you
expect to get information about an x86-specific instruction without
machine-specific knowledge?

However, please feel free to contribute documentation; it is always appreciated.

	-hpa
Comment 4 Andy Lutomirski 2019-12-23 08:01:00 UTC
INT is, in fact, highly unusual among x86 instructions in that the exception that the CPU generates is highly informative.  So, in theory, the kernel *could* detect that a #GP fault is due to INT and do something clever with it, but I also don't see the point at all.

Also, making INT work differently from every other #GP seems confusing at best.

Can someone with appropriate permissions please close this bug?
Comment 5 Borislav Petkov 2019-12-23 09:54:21 UTC
.
Comment 6 Zack Weinberg 2019-12-23 16:04:53 UTC
> Uhm, INT is an x86 instruction, and it is NOT an illegal instruction; some
> INT
> instructions are in fact permitted from user space. So the idea that this is
> somehow "x86-colored glasses" is silly.

I do not find this line of reasoning persuasive, but I also don't care enough to argue about it further.  However ...

> please feel free to contribute documentation; it is always appreciated.

... yeah, I think I _will_ write a patch for the signal(7) manpage that explains that for historical reasons, on some architectures, some operations that are forbidden to user space are reported using SIGSEGV instead of SIGINT and the siginfo_t is useless.  In the BUGS section.  Y'all OK with that?
Comment 7 Andy Lutomirski 2019-12-23 23:33:14 UTC
(In reply to Zack Weinberg from comment #6)
> > Uhm, INT is an x86 instruction, and it is NOT an illegal instruction; some
> > INT
> > instructions are in fact permitted from user space. So the idea that this
> is
> > somehow "x86-colored glasses" is silly.
> 
> I do not find this line of reasoning persuasive, but I also don't care
> enough to argue about it further.  However ...
> 
> > please feel free to contribute documentation; it is always appreciated.
> 
> ... yeah, I think I _will_ write a patch for the signal(7) manpage that
> explains that for historical reasons, on some architectures, some operations
> that are forbidden to user space are reported using SIGSEGV instead of
> SIGINT and the siginfo_t is useless.  In the BUGS section.  Y'all OK with
> that?

If you call it "historical reasons", I will NAK that patch.

Let's be crystal clear here.  The CPU generates one type of fault (#UD) for unrecognized instructions and generates a different type of fault (#GP) for many other things, including various cases of insufficient privilege.  Other causes of #GP include dereferencing a bad pointer for certain values of "bad".

Sending SIGILL for #UD makes sense.  Sending SIGILL for #GP, in general, would be utter nonsense.

So you're welcome to explain that SIGSEGV also includes certain privilege violations, and documenting the error_code and trap_nr fields would be nice, but the only historical thing here is the odd location of the error_code and trap_nr fields.

Note You need to log in before you can comment on or make changes to this bug.