Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
ramosian-glider opened this issue Aug 31, 2015 · 4 comments

Comments

@ramosian-glider
Copy link
Member

Originally reported on Google Code with ID 37

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).

Reported by samsonov@google.com on 2012-02-08 13:35:04

@ramosian-glider
Copy link
Member Author

or 3) instrumenting all noreturn calls with __tsan_unpoison_stack or some such. 
I'll give it a try. 

(and thanks for the repro)

Reported by konstantin.s.serebryany on 2012-02-08 15:42:11

@ramosian-glider
Copy link
Member Author

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];                                                   


}                                      

Reported by konstantin.s.serebryany on 2012-02-08 18:45:54

@ramosian-glider
Copy link
Member Author

This particular test is fixed by r150101 / r150102.
Will see if it fixes more complicated cases. 

Reported by konstantin.s.serebryany on 2012-02-08 21:41:50

  • Status changed: Fixed

@ramosian-glider
Copy link
Member Author

Adding Project:AddressSanitizer as part of GitHub migration.

Reported by ramosian.glider on 2015-07-30 09:12:58

  • Labels added: ProjectAddressSanitizer

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

No branches or pull requests

1 participant