viralcode / address-sanitizer

Automatically exported from code.google.com/p/address-sanitizer
1 stars 0 forks source link

ASan doesn't unpoison stack of subprocesses that share the same memory #37

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Currently ASan unpoisons the shadow for stack variables at function exit.
If a process creates subprocess with shared memory, and the subprocess
finishes with _exit(), we never unpoison shadow stack of this
process. Later this stack can be reused, that would lead to false
positives. Ugly reproducer for this:

$ cat clone.cc
#include <malloc.h>
#include <sched.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

const int kStackSize = 1 << 20;
char *child_stack;

//__attribute__((no_address_safety_analysis))
int t1(void *arg) {
  volatile char x[32] = {0};
  fprintf(stderr, "array starts at %p\n", x);
  volatile char *ch = x + 32;
  _exit(1);
}

//__attribute__((no_address_safety_analysis))
int t2(void *arg) {
  volatile char x[96] = {0};
  fprintf(stderr, "array starts at %p\n", x);
  volatile char *ch = x + 32;
  fprintf(stderr, "char at %p = %d\n", ch, (int)*ch);
  _exit(1);
}

void run_process(int (*fn)(void*)) {
  pid_t clone_pid = clone(fn, child_stack, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0);
  sleep(2);
  waitpid(clone_pid, NULL, 0);
  fprintf(stderr, "Worker %d exited!\n", clone_pid);
}

int main() {
  // stack grows down
  child_stack = (char*)malloc(kStackSize) + kStackSize;
  fprintf(stderr, "Workers' stack starts at %p\n", child_stack);
  run_process(t1);
  run_process(t2);
  return 0;
}

$ clang++ -faddress-sanitizer clone.cc
$ ./a.out
Workers' stack starts at 0x7fcb069d1080
array starts at 0x7fcb069d0fc0
Worker 7227 exited!
array starts at 0x7fcb069d0f80
=================================================================
==7228== ERROR: AddressSanitizer stack-buffer-overflow on address 
0x7fcb069d0fa0 at pc 0x404fec bp 0x7fcb069d0dd0 sp 0x7fcb069d0dc8
READ of size 1 at 0x7fcb069d0fa0 thread T0
    #0 0x404fec (/home/samsonov/llvm-project/llvm/projects/compiler-rt/lib/asan/tmp/clone/a.out+0x404fec)
0x7fcb069d0fa0 is located 1048352 bytes inside of 1048576-byte region 
[0x7fcb068d1080,0x7fcb069d1080)
allocated by thread T0 here:
    #0 0x406592 (/home/samsonov/llvm-project/llvm/projects/compiler-rt/lib/asan/tmp/clone/a.out+0x406592)
    #1 0x40537e (/home/samsonov/llvm-project/llvm/projects/compiler-rt/lib/asan/tmp/clone/a.out+0x40537e)
    #2 0x7fcb06af0c4d (/home/samsonov/llvm-project/llvm/projects/compiler-rt/lib/asan/tmp/clone/a.out+0x7fcb06af0c4d)
==7228== ABORTING
Stats: 1M malloced (1M for red zones) by 1 calls
Stats: 0M realloced by 0 calls
Stats: 0M freed by 0 calls
Stats: 0M really freed by 0 calls
Stats: 4M (1025 full pages) mmaped in 1 calls
  mmaps   by size class: 21:2; 
  mallocs by size class: 21:1; 
  frees   by size class: 
  rfrees  by size class: 
Stats: malloc large: 1 small slow: 0
Shadow byte and word:
  0x1ff960d3a1f4: f2
  0x1ff960d3a1f0: 00 f4 f4 f4 f2 f2 f2 f2
More shadow bytes:
  0x1ff960d3a1d0: 00 00 00 00 00 00 00 00
  0x1ff960d3a1d8: 00 00 00 00 f1 f1 f1 f1
  0x1ff960d3a1e0: 04 f4 f4 f4 f2 f2 f2 f2
  0x1ff960d3a1e8: 00 f4 f4 f4 f2 f2 f2 f2
=>0x1ff960d3a1f0: 00 f4 f4 f4 f2 f2 f2 f2
  0x1ff960d3a1f8: 00 00 00 00 f2 f2 f2 f2
  0x1ff960d3a200: 00 f4 f4 f4 f3 f3 f3 f3
  0x1ff960d3a208: 00 00 00 00 00 00 00 00
  0x1ff960d3a210: fa fa fa fa fa fa fa fa
Worker 7228 exited!

If we exit normally (not via _exit()) or add
__attribute__((no_address_safety_analysis)) to the
subprocess callbacks, error reports go away.
However, this may not be feasible if subprocess calls
many functions from different sources. This can also
be solved by:

1) intercepting clone (but there can be syscalls).
2) manually unpoisoning stack of subprocess in a parent process, when
the former exits (using asan interface).

Original issue reported on code.google.com by samso...@google.com on 8 Feb 2012 at 1:35

GoogleCodeExporter commented 9 years ago
or 3) instrumenting all noreturn calls with __tsan_unpoison_stack or some such. 
I'll give it a try. 

(and thanks for the repro)

Original comment by konstant...@gmail.com on 8 Feb 2012 at 3:42

GoogleCodeExporter commented 9 years ago
shorter repro:

#include <sched.h>                                                              

#include <stdio.h>                                                              

#include <sys/syscall.h>                                                        

#include <sys/types.h>                                                          

#include <sys/wait.h>                                                           

#include <unistd.h>                                                             

int Child(void *arg) {                                                          

  char x[32] = {0};                                                                                                                                                                                 
  fprintf(stderr, "Child:  %p\n", x);                                                                                                                                                               
  volatile char *ch = x + 32;                                                                                                                                                                       
  _exit(1);                                                                                                                                                                                         
}                                                                               

int main(int argc, char **argv) {                                               

  const int kStackSize = 1 << 20;                                                                                                                                                                   
  char child_stack[kStackSize + 1];                                                                                                                                                                 
  char *sp = child_stack + kStackSize;  // Stack grows down.                                                                                                                                        
  fprintf(stderr, "Parent: %p\n", sp);                                                                                                                                                              
  pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0);                                                                                                                        
  waitpid(clone_pid, NULL, 0);                                                                                                                                                                      
  for (int i = 0; i < kStackSize; i++)                                                                                                                                                              
    child_stack[i] = i;                                                                                                                                                                             
  return child_stack[argc - 1];                                                                                                                                                                     
}                                      

Original comment by konstant...@gmail.com on 8 Feb 2012 at 6:45

GoogleCodeExporter commented 9 years ago
This particular test is fixed by r150101 / r150102.
Will see if it fixes more complicated cases. 

Original comment by konstant...@gmail.com on 8 Feb 2012 at 9:41