64-bit syscall ABI issue



When arguments of types narrower than a register are passed to a C
function in a register, the ABI typically requires that they be
sign-extended or zero-extended to the full width of the register. The
compiler, generating code for the called function, may presume that the
registers have been properly extended (and GCC is getting increasingly
good at avoiding redundant sign and zero extensions). In turn, it may
generate instructions for which the processor presumes the values for
properly extended - for example, on MIPS64, 32-bit arithmetic instructions
are documented as yielding unpredicatable results if the operands are not
sign-extended to 64 bits.

Consider, for example,
<http://sourceware.org/bugzilla/show_bug.cgi?id=4459>. Here glibc has
passed an improperly extended value to a syscall, so the syscall
implementation (written in C) receives a register value not conforming to
the ABI, and undefined behavior in the kernel duly ensues. Depending on
the particular form the undefined behavior takes for a given function,
compiler and CPU implementation, security issues might arise if sanity
checks of the arguments to a syscall fail to allow for values that can be
passed in registers but are beyond those permitted by the ABI.

What should the kernel syscall ABI be in such cases (any case where the
syscall implementations expect arguments narrower than registers, so
mainly 32-bit arguments on 64-bit platforms)? There are two obvious
possibilities:

(a) The upper bits of 32-bit syscall arguments are undefined, the kernel
should deal with this.

(b) The upper bits of 32-bit syscall arguments must be extended according
to the ABI, the kernel should detect invalid register values and treat
them as erroneous syscall arguments (probably returning EINVAL).


In either case, the kernel needs a way to handle the improperly extended
syscall arguments. Possibilities include:

* For (a), a new compiler option (or function attribute) to change the ABI
so that improperly extended arguments are valid; the compiler would then
generate the necessary code to convert them to properly extended values in
registers.

* Code at the assembly level, before syscalls get passed to their C
implementations, that uses a table of which arguments to which syscalls
are narrower than registers and either extends (for (a)) or returns EINVAL
for improperly extended values (for (b)).

* Making the C syscall implementations take register-sized arguments, with
some macros to check they are properly extended (for (b)) or reduce them
in width to variables of the narrower type (for (a)).


If (a), existing glibc is fine for 32-bit arguments on all targets - but
cases which glibc passes a 64-bit argument and the kernel expects a 32-bit
one (e.g. passing size_t where the kernel expects int) would have such
arguments silently truncated to 32 bits.

If (b) (which I prefer), MIPS64 (only) would need a new glibc that
properly sign-extends 32-bit syscall arguments to 64-bit values, in order
to work with a kernel that detects improper extension. This mainly
affects -1 as a uid/gid argument - a case we see is currently broken
anyway. Such a glibc should work on older kernels as well, and this need
for a glibc change should not affect platforms where unsigned 32-bit
values are zero-extended rather than sign-extended to 64 bits.

--
Joseph S. Myers
joseph@xxxxxxxxxxxxxxxx
-
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/



Relevant Pages

  • Dynamic System Calls & System Call Hijacking
    ... 'cause they don't wanna take your brand new syscall into the ... - Can't recompile the kernel, otherwise you gonna lose RedHat guarantee? ... Sure, it's a loadable kernel module, who wants to modify the kernel? ... Or you can register your system call over an existing one: ...
    (Linux-Kernel)
  • Re: [patch 00/13] Syslets, "Threadlets", generic AIO support, v3
    ... or cache flushes, or tlb flushes, or floating point. ... cache-related penalty for encoding the syscall number in the syscall ... the userspace programmer the use of the FPU in threadlets, ... it's a coprocessor register that is incorrectly emulated by ...
    (Linux-Kernel)
  • Re: [RFC][PATCH] linux-2.6.2-rc2_vsyscall-gtod_B1.patch
    ... >> require that every single optimized syscall needs to be handled special. ... > will work with future kernel optimisations. ... A fast call path that does not waste a register. ... send the line "unsubscribe linux-kernel" in ...
    (Linux-Kernel)
  • Re: CONFIG_HAVE_ARCH_TRACEHOOK and you
    ... to call the syscall tracing hooks if TIF_SYSCALL_TRACE isn't set. ... the register allocation ends up as: ... necessary for things like strace. ...
    (Linux-Kernel)
  • Re: [RFC][PATCH] linux-2.6.2-rc2_vsyscall-gtod_B1.patch
    ... > With the x86-64 optimized vsyscall the syscall number does ... A fast call path that does not waste a register. ... send the line "unsubscribe linux-kernel" in ...
    (Linux-Kernel)