This is the mail archive of the libc-ports@sources.redhat.com mailing list for the libc-ports project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC][PATCH] tst-longjmp_chk FAIL on Thumb-2 glibc


Hi all,

I have executed glibc testsuite on the following environment.

[Test Environment]
 CPU: Cortex-A8
 kernel: linux 2.6.29
 binutils: 2.20.51.0.11
 gcc: 4.5.1
 glibc: 2.11.2

Especially while building glibc itself, I passed following options.

[additional glibc build options]
 -fno-omit-frame-pointer -funwind-tables -mthumb -march=armv7-a

And the following testcase was FAIL as follows.

[Test Code]
debug/tst-longjmp_chk.c

This testcase tests FORTIFY_SOURCE functionalities for
setjmp/longjmp.

[Test Log]
GCONV_PATH=/home/ryosei/rpmdir/BUILD/glibc-2.11.2/objdir/iconvdata
LC_ALL=C
/home/ryosei/rpmdir/BUILD/glibc-2.11.2/objdir/elf/ld-linux.so.3
--library-path
(snip)

/home/ryosei/rpmdir/BUILD/glibc-2.11.2/objdir/debug/tst-longjmp_chk  >
/home/ryosei/rpmdir/BUILD/glibc-2.11.2/objdir/debug/tst-longjmp_chk.out
make[2]: [/home/ryosei/rpmdir/BUILD/glibc-2.11.2/objdir/debug/tst-longjmp_chk.out] Error 1 (ignored)

I have confirmed that it was FAIL because Segmentation fault was
happended while executing test code.
And I have also confirmed that glibc-2.13 has the same issue.

gdb backtrace information is as follows.

[gdb log]
(gdb) r
Starting program: /tmp/ryosei/tst-longjmp_chk

Program received signal SIGSEGV, Segmentation fault.
0x40114bba in _Unwind_VRS_Pop (context=0xbe880278, regclass=<value optimized out>,
    discriminator=<value optimized out>, representation=<value optimized out>)
    at ../../../libgcc/../gcc/config/arm/unwind-arm.c:314
warning: Source file is more recent than executable.
314                   vrs->core.r[i] = *(ptr++);
(gdb) bt
#0  0x40114bba in _Unwind_VRS_Pop (context=0xbe880278, regclass=<value optimized out>,
    discriminator=<value optimized out>, representation=<value optimized out>)
    at ../../../libgcc/../gcc/config/arm/unwind-arm.c:314
#1  0x401151c6 in __gnu_unwind_execute (context=0xbe880278, uws=0xbe88022c)
    at ../../../libgcc/../gcc/config/arm/pr-support.c:153
#2  0x4011483a in __gnu_unwind_pr_common (state=<value optimized out>,ucbp=0xbe8804b8,
    context=<value optimized out>, id=0) at ../../../libgcc/../gcc/config/arm/unwind-arm.c:1224
#3  0x40114b18 in __aeabi_unwind_cpp_pr0 (state=<value optimized out>, ucbp=<value optimized out>,
    context=<value optimized out>) at ../../../libgcc/../gcc/config/arm/unwind-arm.c:1246
#4  0x40114f2e in __gnu_Unwind_Backtrace (trace=0x400c129d<backtrace_helper>,
    trace_argument=0xbe880584, entry_vrs=<value optimized out>)
    at ../../../libgcc/../gcc/config/arm/unwind-arm.c:996
#5  0x401150fa in ___Unwind_Backtrace () at ../../../libgcc/../gcc/config/arm/libunwind.S:334
#6  0x4001e85c in __elf_set___libc_subfreeres_element_free_mem__ () from /devel/lib/ld-linux.so.3
#7  0x4001e85c in __elf_set___libc_subfreeres_element_free_mem__ () from /devel/lib/ld-linux.so.3
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) print ptr
$1 = (_uw *) 0xba

We can see Segmentation fault is happened inside ARM libgcc.
This is because the program accesses to ptr, the value of which is
0xba.

The value of ptr, which is 0xba, comes as follows.

ARM libgcc unwinds __longjmp_chk() frame based on the
following unwind information.

[libc.so unwind information]
$ readelf -u libc.so.6

Unwind table index '.ARM.exidx' at offset 0xd93ac contains 2144 entries:

(snip)

0xa4b8c <__longjmp_chk>: 0x8097840b
  Compact model 0
  0x97      vsp = r7
  0x84 0x0b pop {r4, r5, r7, r14}


With respect to above ARM unwind information, r7 is a Thumb-2 frame
pointer and SP is calculated based on r7.
But r7 actually holds system call number in this case as r7 is also a
register to pass that number in case of ARM Linux EABI.

Following is the actual code, which is ____longjmp_chk() and is called
from __longjmp_chk().
And it'll jumps to __fortify_fail() with B(Branch) instruction
if fail usage is detected.

[glibc-ports-2.13/sysdeps/unix/sysv/linux/arm/eabi/____longjmp_chk.S]
 (snip)
     51  #define CHECK_SP(reg)                           \
     52          cfi_remember_state;                     \
     53          cmp     sp, reg;                        \
     54          bls     .Lok;                           \
     55          mov     r5, r0;                         \
     56          cfi_undefined (r5);                     \
     57          mov     r7, #SYS_ify(sigaltstack);      \
     58          cfi_undefined (r7);                     \
     59          mov     r0, #0;                         \
     60          sub     sp, sp, #16; /* >= sizeof (stack_t) */ \
     61          cfi_adjust_cfa_offset (16);             \
     62          cfi_remember_state;                     \
     63          mov     r1, sp;                         \
     64          swi     #0;                             \
     65          cmp     r0, #0;                         \
     66          bne     .Lok2;                          \
     67          ldr     r1, [sp, #4];                   \
     68          tst     r1, #1;                         \
     69          beq     .Lfail;                         \
     70          ldr     r2, [sp, #0];                   \
     71          ldr     r3, [sp, #8];                   \
     72          add     r2, r2, r3;                     \
     73          sub     r2, r2, reg;                    \
     74          cmp     r2, r3;                         \
     75          bhi     .Lok2;                          \
     76  .Lfail:                                         \
     77          CALL_FAIL                               \
     78          cfi_restore_state;                      \
     79  .Lok2:                                          \
     80          mov     r0, r5;                         \
     81          cfi_restore_state;                      \
     82  .Lok:
     83
     84  #include <__longjmp.S>

Please see line 57 above.
In this case sigaltstack, the system call number of which is 0xba,
is called inside ____longjmp_chk().
And r7 is updated but not restored.

So that ARM unwinder tried to pop from the address(0xba) and
Segmentation fault is occurred.

And as a related issue information
line 60 above is also not good for ARM unwinding, I suppose.

Because sp is updated by assembly code but it is not taken care
in an ARM unwind information.

For example, following is the ARM unwind information of __longjmp_chk()
if we did not pass -fno-omit-frame-pointer option while building
glibc.

[libc.so unwind information]
$ readelf -u libc.so.6

Unwind table index '.ARM.exidx' at offset 0xd271c contains 2217 entries:

(snip)
0x9fd30 <__longjmp_chk>: 0x80b108a9
  Compact model 0
  0xb1 0x08 pop {r3}
  0xa9      pop {r4, r5r14}

If ____longjmp_chk() detects FAIL and call backtrace(),
it might cause an issue as sp is updated by assembly code but it is not
taken care in above ARM unwind information.

Following is a solution to fix this issue.

This patch saves and restores original r7 value, which is fp value in
case of Thumb-2, and also adjusts sp value when ____longjmp_chk()
detects FAIL and calls backtrace().

We have verified that tst-longjmp_chk is PASS after applying this
patch.

glibc makecheck has been done for both ARM and Thumb-2 glibc.
No additional FAILs are found.

Could you please give me some comments??

Best Regards,
Ryosei Takagi

2011-03-25  Ryosei Takagi  <ryosei@sm.sony.co.jp>

	* sysdeps/unix/sysv/linux/arm/eabi/____longjmp_chk.S:
	Save and restore r7, and also adjust sp.

Index: b/sysdeps/unix/sysv/linux/arm/eabi/____longjmp_chk.S
===================================================================
--- a/sysdeps/unix/sysv/linux/arm/eabi/____longjmp_chk.S
+++ b/sysdeps/unix/sysv/linux/arm/eabi/____longjmp_chk.S
@@ -52,13 +52,15 @@ longjmp_msg:
 	cfi_remember_state;			\
 	cmp	sp, reg;			\
 	bls	.Lok;				\
+	str	r7, [sp, #-4]!;			\
+	cfi_adjust_cfa_offset (4);		\
+	cfi_rel_offset (r7, 0);			\
 	mov	r5, r0;				\
 	cfi_undefined (r5);			\
 	mov	r7, #SYS_ify(sigaltstack);	\
-	cfi_undefined (r7);			\
 	mov	r0, #0;				\
-	sub	sp, sp, #16; /* >= sizeof (stack_t) */ \
-	cfi_adjust_cfa_offset (16);		\
+	sub	sp, sp, #12; /* == sizeof (stack_t) */ \
+	cfi_adjust_cfa_offset (12);		\
 	cfi_remember_state;			\
 	mov	r1, sp;				\
 	swi	#0;				\
@@ -74,6 +76,11 @@ longjmp_msg:
 	cmp	r2, r3;				\
 	bhi	.Lok2;				\
 .Lfail:						\
+	add	sp, sp, #12;			\
+	cfi_adjust_cfa_offset (-12);		\
+	ldr	r7, [sp], #4;			\
+	cfi_adjust_cfa_offset (-4);		\
+	cfi_restore (r7);			\
 	CALL_FAIL				\
 	cfi_restore_state;			\
 .Lok2:						\


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]