Skip to content

Instantly share code, notes, and snippets.

@sergeyklay
Last active July 6, 2021 10:57
Show Gist options
  • Save sergeyklay/6714b13efe18df0bd3e1838bdfb736f6 to your computer and use it in GitHub Desktop.
Save sergeyklay/6714b13efe18df0bd3e1838bdfb736f6 to your computer and use it in GitHub Desktop.
GCC/Clang -fno-omit-frame-pointer and -fsanitize=address

Description

Frame pointer omission does make debugging significantly harder. Local variables are harder to locate and stack traces are much harder to reconstruct without a frame pointer to help out. Also, accessing parameters can get more expensive since they are far away from the top of the stack and may require more expensive addressing modes.

The -fno-omit-frame-pointer option direct the compiler to generate code that maintains and uses stack frame pointer for all functions so that a debugger can still produce a stack backtrace even with optimizations flags.

Irrespective if you use the flag, not every function needs a frame pointer in the first place, so you can't always expect a difference with the flag.

Also, whether the function has a frame pointer is an implementation detail. Compilers can differ in implementation details (and usually they do).

Arch

Intel(R) Xeon(R) CPU E5-2628L v3

Code

UseAfterFree.cpp

int main(int argc, char **argv) {
  int *array = new int[100];
  delete [] array;
  return array[argc];  // BOOM
}

Clang

$ clang --version
Ubuntu clang version 12.0.0-++20200928064922+37ef2255b64-1~exp1~20200928165602.178
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ diff -c <(clang -S UseAfterFree.cpp -Os -fno-omit-frame-pointer -o -) <(clang -S UseAfterFree.cpp -Os -o -)

Diff

***************
*** 7,31 ****
  # %bb.0:
        pushq   %rbp
        .cfi_def_cfa_offset 16
-       .cfi_offset %rbp, -16
-       movq    %rsp, %rbp
-       .cfi_def_cfa_register %rbp
-       pushq   %r14
        pushq   %rbx
!       .cfi_offset %rbx, -32
!       .cfi_offset %r14, -24
!       movl    %edi, %r14d
        movl    $400, %edi                      # imm = 0x190
        callq   _Znam
        movq    %rax, %rbx
        movq    %rax, %rdi
        callq   _ZdaPv
!       movslq  %r14d, %rax
        movl    (%rbx,%rax,4), %eax
        popq    %rbx
!       popq    %r14
        popq    %rbp
!       .cfi_def_cfa %rsp, 8
        retq
  .Lfunc_end0:
        .size   main, .Lfunc_end0-main
--- 7,32 ----
  # %bb.0:
        pushq   %rbp
        .cfi_def_cfa_offset 16
        pushq   %rbx
!       .cfi_def_cfa_offset 24
!       pushq   %rax
!       .cfi_def_cfa_offset 32
!       .cfi_offset %rbx, -24
!       .cfi_offset %rbp, -16
!       movl    %edi, %ebp
        movl    $400, %edi                      # imm = 0x190
        callq   _Znam
        movq    %rax, %rbx
        movq    %rax, %rdi
        callq   _ZdaPv
!       movslq  %ebp, %rax
        movl    (%rbx,%rax,4), %eax
+       addq    $8, %rsp
+       .cfi_def_cfa_offset 24
        popq    %rbx
!       .cfi_def_cfa_offset 16
        popq    %rbp
!       .cfi_def_cfa_offset 8
        retq
  .Lfunc_end0:
        .size   main, .Lfunc_end0-main

AddressSanitizer

$ clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer UseAfterFree.cpp
$ ./a.out
=================================================================
==1374==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000044 at pc 0x0000004c990f bp 0x7ffd3adc4a70 sp 0x7ffd3adc4a68
READ of size 4 at 0x614000000044 thread T0
    #0 0x4c990e in main /tmp/UseAfterFree.cpp:4:10
    #1 0x7f4fd4eb00b2 in __libc_start_main /build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c:308:16
    #2 0x41c2ad in _start (/tmp/a.out+0x41c2ad)

0x614000000044 is located 4 bytes inside of 400-byte region [0x614000000040,0x6140000001d0)
freed by thread T0 here:
    #0 0x4c791d in operator delete[](void*) (/tmp/a.out+0x4c791d)
    #1 0x4c98de in main /tmp/UseAfterFree.cpp:3:3
    #2 0x7f4fd4eb00b2 in __libc_start_main /build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c:308:16

previously allocated by thread T0 here:
    #0 0x4c70cd in operator new[](unsigned long) (/mnt/c/work/dpcode/c/frame-poiner/a.out+0x4c70cd)
    #1 0x4c98d3 in main /tmp/UseAfterFree.cpp:2:16
    #2 0x7f4fd4eb00b2 in __libc_start_main /build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c:308:16

SUMMARY: AddressSanitizer: heap-use-after-free /tmp/UseAfterFree.cpp:4:10 in main
Shadow bytes around the buggy address:
  0x0c287fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c287fff8000: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
  0x0c287fff8010: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff8030: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa
  0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8050: 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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  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
  Shadow gap:              cc
==1374==ABORTING

GCC

$ gcc --version
gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ diff -c <(gcc -S UseAfterFree.cpp -Os -fno-omit-frame-pointer -o -) <(gcc -S UseAfterFree.cpp -Os -o -)

Diff

***************
*** 10,32 ****
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
-       movq    %rsp, %rbp
-       .cfi_def_cfa_register 6
-       pushq   %r12
        pushq   %rbx
!       .cfi_offset 12, -24
!       .cfi_offset 3, -32
        movslq  %edi, %rbx
        movl    $400, %edi
        call    _Znam@PLT
!       movq    %rax, %r12
        movq    %rax, %rdi
        call    _ZdaPv@PLT
!       movl    (%r12,%rbx,4), %eax
        popq    %rbx
!       popq    %r12
        popq    %rbp
!       .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
  .LFE0:
--- 10,33 ----
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        pushq   %rbx
!       .cfi_def_cfa_offset 24
!       .cfi_offset 3, -24
        movslq  %edi, %rbx
        movl    $400, %edi
+       pushq   %rax
+       .cfi_def_cfa_offset 32
        call    _Znam@PLT
!       movq    %rax, %rbp
        movq    %rax, %rdi
        call    _ZdaPv@PLT
!       movl    0(%rbp,%rbx,4), %eax
!       popq    %rdx
!       .cfi_def_cfa_offset 24
        popq    %rbx
!       .cfi_def_cfa_offset 16
        popq    %rbp
!       .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
  .LFE0:

AddressSanitizer

$ g++ -O1 -g -fsanitize=address -fno-omit-frame-pointer UseAfterFree.cpp
$ ./a.out
=================================================================
==1409==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000044 at pc 0x564c3b87921d bp 0x7fff1fef2490 sp 0x7fff1fef2480
READ of size 4 at 0x614000000044 thread T0
    #0 0x564c3b87921c in main /tmp/UseAfterFree.cpp:4
    #1 0x7f7c8b8840b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #2 0x564c3b87910d in _start (/tmp/a.out+0x110d)

0x614000000044 is located 4 bytes inside of 400-byte region [0x614000000040,0x6140000001d0)
freed by thread T0 here:
    #0 0x7f7c8bb5faaf in operator delete[](void*) (/lib/x86_64-linux-gnu/libasan.so.5+0x110aaf)
    #1 0x564c3b8791ea in main /tmp/UseAfterFree.cpp:3
    #2 0x7f7c8b8840b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

previously allocated by thread T0 here:
    #0 0x7f7c8bb5eb47 in operator new[](unsigned long) (/lib/x86_64-linux-gnu/libasan.so.5+0x10fb47)
    #1 0x564c3b8791df in main /tmp/UseAfterFree.cpp:2
    #2 0x7f7c8b8840b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-use-after-free /tmp/UseAfterFree.cpp:4 in main
Shadow bytes around the buggy address:
  0x0c287fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c287fff8000: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
  0x0c287fff8010: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff8030: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa
  0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8050: 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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  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
  Shadow gap:              cc
==1409==ABORTING
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment