Merge "Fix the way to get main thread stack start address."
This commit is contained in:
		@@ -114,6 +114,36 @@ int pthread_attr_setstack(pthread_attr_t* attr, void* stack_base, size_t stack_s
 | 
				
			|||||||
  return 0;
 | 
					  return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uintptr_t __get_main_stack_startstack() {
 | 
				
			||||||
 | 
					  FILE* fp = fopen("/proc/self/stat", "re");
 | 
				
			||||||
 | 
					  if (fp == nullptr) {
 | 
				
			||||||
 | 
					    __libc_fatal("couldn't open /proc/self/stat: %s", strerror(errno));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  char line[BUFSIZ];
 | 
				
			||||||
 | 
					  if (fgets(line, sizeof(line), fp) == nullptr) {
 | 
				
			||||||
 | 
					    __libc_fatal("couldn't read /proc/self/stat: %s", strerror(errno));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fclose(fp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // See man 5 proc. There's no reason comm can't contain ' ' or ')',
 | 
				
			||||||
 | 
					  // so we search backwards for the end of it. We're looking for this field:
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  //  startstack %lu (28) The address of the start (i.e., bottom) of the stack.
 | 
				
			||||||
 | 
					  uintptr_t startstack = 0;
 | 
				
			||||||
 | 
					  const char* end_of_comm = strrchr(line, ')');
 | 
				
			||||||
 | 
					  if (sscanf(end_of_comm + 1, " %*c "
 | 
				
			||||||
 | 
					             "%*d %*d %*d %*d %*d "
 | 
				
			||||||
 | 
					             "%*u %*u %*u %*u %*u %*u %*u "
 | 
				
			||||||
 | 
					             "%*d %*d %*d %*d %*d %*d "
 | 
				
			||||||
 | 
					             "%*u %*u %*d %*u %*u %*u %" SCNuPTR, &startstack) != 1) {
 | 
				
			||||||
 | 
					    __libc_fatal("couldn't parse /proc/self/stat");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return startstack;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __pthread_attr_getstack_main_thread(void** stack_base, size_t* stack_size) {
 | 
					static int __pthread_attr_getstack_main_thread(void** stack_base, size_t* stack_size) {
 | 
				
			||||||
  ErrnoRestorer errno_restorer;
 | 
					  ErrnoRestorer errno_restorer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -127,20 +157,19 @@ static int __pthread_attr_getstack_main_thread(void** stack_base, size_t* stack_
 | 
				
			|||||||
    stack_limit.rlim_cur = 8 * 1024 * 1024;
 | 
					    stack_limit.rlim_cur = 8 * 1024 * 1024;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // It shouldn't matter which thread we are because we're just looking for "[stack]", but
 | 
					  // Ask the kernel where our main thread's stack started.
 | 
				
			||||||
  // valgrind seems to mess with the stack enough that the kernel will report "[stack:pid]"
 | 
					  uintptr_t startstack = __get_main_stack_startstack();
 | 
				
			||||||
  // instead if you look in /proc/self/maps, so we need to look in /proc/pid/task/pid/maps.
 | 
					
 | 
				
			||||||
  char path[64];
 | 
					  // Hunt for the region that contains that address.
 | 
				
			||||||
  snprintf(path, sizeof(path), "/proc/self/task/%d/maps", getpid());
 | 
					  FILE* fp = fopen("/proc/self/maps", "re");
 | 
				
			||||||
  FILE* fp = fopen(path, "re");
 | 
					  if (fp == nullptr) {
 | 
				
			||||||
  if (fp == NULL) {
 | 
					    __libc_fatal("couldn't open /proc/self/maps");
 | 
				
			||||||
    return errno;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  char line[BUFSIZ];
 | 
					  char line[BUFSIZ];
 | 
				
			||||||
  while (fgets(line, sizeof(line), fp) != NULL) {
 | 
					  while (fgets(line, sizeof(line), fp) != NULL) {
 | 
				
			||||||
    if (ends_with(line, " [stack]\n")) {
 | 
					 | 
				
			||||||
    uintptr_t lo, hi;
 | 
					    uintptr_t lo, hi;
 | 
				
			||||||
    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &lo, &hi) == 2) {
 | 
					    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &lo, &hi) == 2) {
 | 
				
			||||||
 | 
					      if (lo <= startstack && startstack <= hi) {
 | 
				
			||||||
        *stack_size = stack_limit.rlim_cur;
 | 
					        *stack_size = stack_limit.rlim_cur;
 | 
				
			||||||
        *stack_base = reinterpret_cast<void*>(hi - *stack_size);
 | 
					        *stack_base = reinterpret_cast<void*>(hi - *stack_size);
 | 
				
			||||||
        fclose(fp);
 | 
					        fclose(fp);
 | 
				
			||||||
@@ -148,7 +177,7 @@ static int __pthread_attr_getstack_main_thread(void** stack_base, size_t* stack_
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  __libc_fatal("No [stack] line found in \"%s\"!", path);
 | 
					  __libc_fatal("Stack not found in /proc/self/maps");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int pthread_attr_getstack(const pthread_attr_t* attr, void** stack_base, size_t* stack_size) {
 | 
					int pthread_attr_getstack(const pthread_attr_t* attr, void** stack_base, size_t* stack_size) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1220,6 +1220,67 @@ TEST(pthread, pthread_attr_getstack__main_thread) {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct GetStackSignalHandlerArg {
 | 
				
			||||||
 | 
					  volatile bool done;
 | 
				
			||||||
 | 
					  void* signal_handler_sp;
 | 
				
			||||||
 | 
					  void* main_stack_base;
 | 
				
			||||||
 | 
					  size_t main_stack_size;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static GetStackSignalHandlerArg getstack_signal_handler_arg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void getstack_signal_handler(int sig) {
 | 
				
			||||||
 | 
					  ASSERT_EQ(SIGUSR1, sig);
 | 
				
			||||||
 | 
					  // Use sleep() to make current thread be switched out by the kernel to provoke the error.
 | 
				
			||||||
 | 
					  sleep(1);
 | 
				
			||||||
 | 
					  pthread_attr_t attr;
 | 
				
			||||||
 | 
					  ASSERT_EQ(0, pthread_getattr_np(pthread_self(), &attr));
 | 
				
			||||||
 | 
					  void* stack_base;
 | 
				
			||||||
 | 
					  size_t stack_size;
 | 
				
			||||||
 | 
					  ASSERT_EQ(0, pthread_attr_getstack(&attr, &stack_base, &stack_size));
 | 
				
			||||||
 | 
					  getstack_signal_handler_arg.signal_handler_sp = &attr;
 | 
				
			||||||
 | 
					  getstack_signal_handler_arg.main_stack_base = stack_base;
 | 
				
			||||||
 | 
					  getstack_signal_handler_arg.main_stack_size = stack_size;
 | 
				
			||||||
 | 
					  getstack_signal_handler_arg.done = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The previous code obtained the main thread's stack by reading the entry in
 | 
				
			||||||
 | 
					// /proc/self/task/<pid>/maps that was labeled [stack]. Unfortunately, on x86/x86_64, the kernel
 | 
				
			||||||
 | 
					// relies on sp0 in task state segment(tss) to label the stack map with [stack]. If the kernel
 | 
				
			||||||
 | 
					// switches a process while the main thread is in an alternate stack, then the kernel will label
 | 
				
			||||||
 | 
					// the wrong map with [stack]. This test verifies that when the above situation happens, the main
 | 
				
			||||||
 | 
					// thread's stack is found correctly.
 | 
				
			||||||
 | 
					TEST(pthread, pthread_attr_getstack_in_signal_handler) {
 | 
				
			||||||
 | 
					  const size_t sig_stack_size = 16 * 1024;
 | 
				
			||||||
 | 
					  void* sig_stack = mmap(NULL, sig_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
 | 
				
			||||||
 | 
					                         -1, 0);
 | 
				
			||||||
 | 
					  ASSERT_NE(MAP_FAILED, sig_stack);
 | 
				
			||||||
 | 
					  stack_t ss;
 | 
				
			||||||
 | 
					  ss.ss_sp = sig_stack;
 | 
				
			||||||
 | 
					  ss.ss_size = sig_stack_size;
 | 
				
			||||||
 | 
					  ss.ss_flags = 0;
 | 
				
			||||||
 | 
					  stack_t oss;
 | 
				
			||||||
 | 
					  ASSERT_EQ(0, sigaltstack(&ss, &oss));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ScopedSignalHandler handler(SIGUSR1, getstack_signal_handler, SA_ONSTACK);
 | 
				
			||||||
 | 
					  getstack_signal_handler_arg.done = false;
 | 
				
			||||||
 | 
					  kill(getpid(), SIGUSR1);
 | 
				
			||||||
 | 
					  ASSERT_EQ(true, getstack_signal_handler_arg.done);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Verify if the stack used by the signal handler is the alternate stack just registered.
 | 
				
			||||||
 | 
					  ASSERT_LE(sig_stack, getstack_signal_handler_arg.signal_handler_sp);
 | 
				
			||||||
 | 
					  ASSERT_GE(reinterpret_cast<char*>(sig_stack) + sig_stack_size,
 | 
				
			||||||
 | 
					            getstack_signal_handler_arg.signal_handler_sp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Verify if the main thread's stack got in the signal handler is correct.
 | 
				
			||||||
 | 
					  ASSERT_LE(getstack_signal_handler_arg.main_stack_base, &ss);
 | 
				
			||||||
 | 
					  ASSERT_GE(reinterpret_cast<char*>(getstack_signal_handler_arg.main_stack_base) +
 | 
				
			||||||
 | 
					            getstack_signal_handler_arg.main_stack_size, reinterpret_cast<void*>(&ss));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ASSERT_EQ(0, sigaltstack(&oss, nullptr));
 | 
				
			||||||
 | 
					  ASSERT_EQ(0, munmap(sig_stack, sig_stack_size));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void pthread_attr_getstack_18908062_helper(void*) {
 | 
					static void pthread_attr_getstack_18908062_helper(void*) {
 | 
				
			||||||
  char local_variable;
 | 
					  char local_variable;
 | 
				
			||||||
  pthread_attr_t attributes;
 | 
					  pthread_attr_t attributes;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user