0

Here's a minimal example of how I'm rasing the stack-use-after-return error:

int *ptr; // global definition
// stack allocate and initialize`ptr` 
void alloc() {
  int local[10];
  ptr = &local[0];
}
// make a `return`
int ret_after_use() {
    return ptr[10];
}
int main() {
  alloc();
  int i = ret_after_use();
  ptr = &i; // error: use after return
  return 0;
}

As expected, Asan catches this problem and raises the following message (raw) -

=================================================================
==1011387==ERROR: AddressSanitizer: stack-use-after-return on address 0x7f3ada500048 at pc 0x564ebacaa22c bp 0x7ffe3f0635d0 sp 0x7ffe3f0635c8
READ of size 4 at 0x7f3ada500048 thread T0
    #0 0x564ebacaa22b in ret_after_use /home/aissy/c_cpp/understanding/UBs/stack_use_after_return.c:17:9
    #1 0x564ebacaa4c9 in main /home/aissy/c_cpp/understanding/UBs/stack_use_after_return.c:31:11
    #2 0x7f3adc23c78f in __libc_start_call_main /usr/src/debug/glibc-git/glibc/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #3 0x7f3adc23c849 in __libc_start_main@GLIBC_2.2.5 /usr/src/debug/glibc-git/glibc/csu/../csu/libc-start.c:360:3
    #4 0x564ebabad084 in _start /home/aissy/aur/glibc-git/src/glibc/csu/../sysdeps/x86_64/start.S:115

Address 0x7f3ada500048 is located in stack of thread T0 at offset 72 in frame
    #0 0x564ebacaa04f in alloc /home/aissy/c_cpp/understanding/UBs/stack_use_after_return.c:7

  This frame has 1 object(s):
    [32, 72) 'local' (line 8) <== Memory access at offset 72 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-return /home/aissy/c_cpp/understanding/UBs/stack_use_after_return.c:17:9 in ret_after_use
Shadow bytes around the buggy address:
  0x0fe7db497fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe7db497fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe7db497fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe7db497fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe7db497ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0fe7db498000: f5 f5 f5 f5 f5 f5 f5 f5 f5[f5]f5 f5 f5 f5 f5 f5
  0x0fe7db498010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe7db498020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe7db498030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe7db498040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0fe7db498050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
==1011387==ABORTING

The part I'm interested about is the Shadow bytes around the buggy address: part, particularly what I'm confused about is the addresses that are being shown here to indicate redzones, use-after-free etc. I don't understand where these addresses are coming from, nor how to read from those addresses in either my C program or a debugger like GDB.

I assume the block of memory or stack these addresses are referring to is not the address to the stack (by looking at sp and bp, they are not what it seems like).

Starting with the first of stack-after-return (f5), namely 0x0ffb70398000, I want to know - a) Where are these addresses coming from? b) How to read from those addresses (the contents) in a debugger or my C program itself?

  • The `alloc()` function does illegal code. You store the address of internal data, when the function ends that data no more exists so `ptr` points on an invalid address. – dalfaB May 22 '23 at 07:31
  • @dalfaB yes, that's right! But I intentionally wanted it to be part of this way (also the example on the official Asan github page shows this example: https://github.com/google/sanitizers/wiki/AddressSanitizerExampleUseAfterReturn). I want this code to be intentionally buggy to raise errors by the sanitizers. – user1094822 May 22 '23 at 07:35
  • I suggest you read [this related answer](https://stackoverflow.com/a/61719265/20270), as well as [AddressSanitizerAlgorithm](https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm) which it links. As for the shadow bytes themselves, your program should never try to access these directly. – Hasturkun May 22 '23 at 08:22
  • > your program should never try to access these directly is there any way to access it at all, any way perform IO to them? – user1094822 May 22 '23 at 09:44
  • You could print those out in gdb, figuring the addresses out as documented (or by using the API from gdb, e.g. calling `__asan_get_shadow_mapping`). You shouldn't modify the shadow mapping directly. Any attempt by instrumented code to access this _will_ result in a crash, so don't do that. – Hasturkun May 22 '23 at 19:18

1 Answers1

0

Shadow memory is a part of process address space and can be accessed via the following mapping:

addr -> shadow_start + (addr >> 3)

The value of shadow_start depends on the platform and can be located in asan_mapping.h header. In particular for x86_64 it's 0x00007fff8000.

Each byte of shadow memory holds information about corresponding 8-byte double word in your program's memory. Asan displays it to you so that you are aware of the state of memory area which surrounds the problematic address.

yugr
  • 19,769
  • 3
  • 51
  • 96