Просмотр исходного кода

libsanitizer: arm/ucontext: fix VFP save/restore to use uc_regspace area

The ARM {get,set}context implementations were imported from glibc in
2013 (commit a8dc90eaa, uClibc patch series "libc: add
{get,set,swap,make}context to ARM", see
https://lists.uclibc.org/pipermail/uclibc/2013-January/047400.html).
At that time glibc's own getcontext.S / setcontext.S also lacked the
`add r0, r4, #UCONTEXT_REGSPACE` (resp. `add r0, r0, #UCONTEXT_REGSPACE`)
instruction.  glibc later added it -- current master has it, see
https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/arm/getcontext.S
https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/arm/setcontext.S
-- but uClibc-ng never picked up the fix.

Without that instruction:

* in __getcontext, r0 is clobbered by the preceding `bl sigprocmask`
  call (so r0 == sigprocmask's return value, typically 0). The next
  instruction `stc p11, cr8, [r0], #64` then writes 64 bytes of VFP
  state to address 0 - SIGSEGV.

* in __setcontext, r0 still points at the start of `ucontext_t`
  (i.e. uc_flags), so `ldc p11, cr8, [r0], #64` loads 64 bytes of
  garbage (uc_flags + uc_link + uc_stack + start of uc_mcontext) into
  d8-d15, then the next 4 bytes are interpreted as fpscr - leaving
  the FPU in an undefined state on every setcontext().

The bug is invisible on soft-float builds: the VFP and iWMMXt save
blocks are gated by compile-time `__VFP_FP__` / `__IWMMXT__` defines,
so on a soft-float toolchain (no VFP, no iWMMXt) the entire block
compiles out and r0 is simply unused after sigprocmask.  The bug only
surfaces once VFP or iWMMXt is enabled.

This is reproducible on cortex-a5 (and any other VFP target) with
gcc.dg/asan/c-c++-common/asan/swapcontext-test-1.c from the GCC
testsuite, which crashes inside /lib/libc.so.0 with

    AddressSanitizer: SEGV on unknown address 0x00000000
    The signal is caused by a WRITE memory access.
    #0 0xb67a25c0  (/lib/libc.so.0+0x115c0)

where 0x115c0 lands exactly on the `stc p11, cr8, [r0], #64` in
__getcontext.

Fix: restore the missing `add r0, r4, #UCONTEXT_REGSPACE` /
`add r0, r0, #UCONTEXT_REGSPACE` instruction in both files so that r0
points at uc_regspace[] (the dedicated VFP/iWMMXt save area at the end
of ucontext_t, see <sys/ucontext.h>: `unsigned long uc_regspace[128]
__attribute__((__aligned__(8)));`).

This matches what glibc does today and is the minimum invasive fix.
The HWCAP runtime-detection that current glibc adds on top is not
strictly necessary here because uClibc-ng's ucontext code is already
gated by compile-time `__VFP_FP__` / `__IWMMXT__` defines.

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
Ramin Moussavi 3 недель назад
Родитель
Сommit
cb6793f4c0
2 измененных файлов с 9 добавлено и 0 удалено
  1. 5 0
      libc/sysdeps/linux/arm/getcontext.S
  2. 4 0
      libc/sysdeps/linux/arm/setcontext.S

+ 5 - 0
libc/sysdeps/linux/arm/getcontext.S

@@ -47,6 +47,11 @@ ENTRY(__getcontext)
 	add     r2, r4, #UCONTEXT_SIGMASK
 	bl      PLTJMP(sigprocmask)
 
+	/* sigprocmask clobbered r0; point it back at the VFP/iWMMXt
+	   save area inside ucontext_t (uc_regspace[]) before storing
+	   any coprocessor state there.  */
+	add     r0, r4, #UCONTEXT_REGSPACE
+
 #if defined __UCLIBC_HAS_FLOATS__ && ! defined __UCLIBC_HAS_SOFT_FLOAT__
 # ifdef __VFP_FP__
 	/* Store the VFP registers.  */

+ 4 - 0
libc/sysdeps/linux/arm/setcontext.S

@@ -26,6 +26,10 @@
 
 ENTRY(__setcontext)
 	mov	r4, r0
+	/* Point r0 at the VFP/iWMMXt save area (uc_regspace[]) before
+	   restoring any coprocessor state.  Without this r0 still points
+	   at the start of ucontext_t and we would read uc_flags etc.  */
+	add     r0, r0, #UCONTEXT_REGSPACE
 
 #if defined __UCLIBC_HAS_FLOATS__ && ! defined __UCLIBC_HAS_SOFT_FLOAT__
 # ifdef __VFP_FP__