Fix the way to get main thread stack start address.
For previous way to get the stack using the [stack] string from
/proc/self/task/<pid>/maps is not enough. On x86/x86_64, if an
alternative signal stack is used while a task switch happens,
the [stack] indicator may no longer be correct.
Instead, stack_start from /proc/self/stat which is always inside
the main stack, is used to find the main stack in /proc/self/maps.
Change-Id: Ieb010e71518b57560d541cd3b3563e5aa9660750
Signed-off-by: Nitzan Mor-sarid <nitzan.mor-sarid@intel.com>
Signed-off-by: Mingwei Shi <mingwei.shi@intel.com>
diff --git a/libc/bionic/pthread_attr.cpp b/libc/bionic/pthread_attr.cpp
index 7ad3431..cfa58fc 100644
--- a/libc/bionic/pthread_attr.cpp
+++ b/libc/bionic/pthread_attr.cpp
@@ -114,6 +114,36 @@
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) {
ErrnoRestorer errno_restorer;
@@ -127,20 +157,19 @@
stack_limit.rlim_cur = 8 * 1024 * 1024;
}
- // It shouldn't matter which thread we are because we're just looking for "[stack]", but
- // valgrind seems to mess with the stack enough that the kernel will report "[stack:pid]"
- // instead if you look in /proc/self/maps, so we need to look in /proc/pid/task/pid/maps.
- char path[64];
- snprintf(path, sizeof(path), "/proc/self/task/%d/maps", getpid());
- FILE* fp = fopen(path, "re");
- if (fp == NULL) {
- return errno;
+ // Ask the kernel where our main thread's stack started.
+ uintptr_t startstack = __get_main_stack_startstack();
+
+ // Hunt for the region that contains that address.
+ FILE* fp = fopen("/proc/self/maps", "re");
+ if (fp == nullptr) {
+ __libc_fatal("couldn't open /proc/self/maps");
}
char line[BUFSIZ];
while (fgets(line, sizeof(line), fp) != NULL) {
- if (ends_with(line, " [stack]\n")) {
- uintptr_t lo, hi;
- if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &lo, &hi) == 2) {
+ uintptr_t lo, hi;
+ if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &lo, &hi) == 2) {
+ if (lo <= startstack && startstack <= hi) {
*stack_size = stack_limit.rlim_cur;
*stack_base = reinterpret_cast<void*>(hi - *stack_size);
fclose(fp);
@@ -148,7 +177,7 @@
}
}
}
- __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) {