Skip to content

Fast unwinding doesn't work with thumb. #640

Closed
@chefmax

Description

  • Compile ASan library with -mthumb option.
  • cat test.c
#include <stdlib.h>

__attribute__((noinline))
int baz (int N) {
  int *p = (int *) malloc(10 * sizeof(int));
  return p[N];
}

__attribute__((noinline))
int bar (int N) {
  return baz(N);
}

__attribute__((noinline))
int foo (int N) {
  return bar(N);
}

int main () {
  return foo(10);
}

  • compile test.c with -mthumb:
$ clang -target armv7l-tizen-linux-gnueabi  -march=armv7-a   -mfloat-abi=softfp -mfpu=neon --sysroot=$TOOLCHAIN_ROOT/armv7l-tizen-linux-gnueabi/sys-root/ -B$TOOLCHAIN_ROOT/ -fsanitize=address 3.c -S -mthumb -fno-omit-frame-pointer
  • Run on target:
-sh-4.1# ASAN_OPTIONS=allow_addr2line=true  ./a.out 
=================================================================
==809==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xb4d00ff8 at pc 0x000ac12d bp 0xbdceebf8 sp 0xbdceebf4
READ of size 4 at 0xb4d00ff8 thread T0
    #0 0xac12b in baz /tmp/3.c:6
    #1 0xac143 in bar /tmp/3.c:12
    #2 0xac157 in foo /tmp/3.c:18
    #3 0xac16b in main /tmp/3.c:23
    #4 0xb6094867 in __libc_start_main /home/max/build/v6/sources/glibc/csu/libc-start.c:289

0xb4d00ff8 is located 0 bytes to the right of 40-byte region [0xb4d00fd0,0xb4d00ff8)
allocated by thread T0 here:
    #0 0x96cfd in __interceptor_malloc /home/max/src/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:52

SUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/3.c:6 in baz
Shadow bytes around the buggy address:
  0x369a01a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x369a01b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x369a01c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x369a01d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x369a01e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x369a01f0: fa fa fa fa fa fa fa fa fa fa 00 00 00 00 00[fa]
  0x369a0200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x369a0210: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x369a0220: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x369a0230: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x369a0240: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==809==ABORTING

After some investigation, I found out, that in Thumb mode we use r7 register as fp (that corresponds to ARMv6 architecture) and r14 as lr. Now, looking to __interceptor_malloc prologue:

00096ca4 <__interceptor_malloc>:
   96ca4:       e92d 47f0       stmdb   sp!, {r4, r5, r6, r7, r8, r9, sl, lr}
   96ca8:       af03            add     r7, sp, #12
   96caa:       f5ad 6d84       sub.w   sp, sp, #1056   ; 0x420
   96cae:       4680            mov     r8, r0
   96cb0:       a804            add     r0, sp, #16

Here, we have three extra words between saved fp and lr (r8, r9, sl), thus corresponding code from BufferedStackTrace::FastUnwindStack extracts wrong value for pc in unwinding loop:

uhwptr pc1 = frame[1];

Moreover, it's not guaranteed that we would have exactly three words between fp and lr, consider foo prologue:

000ac148 <foo>:
   ac148:       b580            push    {r7, lr}
   ac14a:       466f            mov     r7, sp
   ac14c:       b088            sub     sp, #32

So, for Thumb, we don't actually know from which slot we can extract the saved lr: from fp[1], fp[2], fp[3] or from fp[4].

Right now, I don't see a general solution for the issue, but IMHO it would be nice to extract at least first lr value to find out malloc's location in user code.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions