Fix bug in finding another thread's TCB.
Change-Id: I06c86ca0c077b464fc6c9fbdf5b89889a26da5fb
diff --git a/libc/bionic/sys_thread_properties.cpp b/libc/bionic/sys_thread_properties.cpp
index 24d7551..d1a73b7 100644
--- a/libc/bionic/sys_thread_properties.cpp
+++ b/libc/bionic/sys_thread_properties.cpp
@@ -39,6 +39,11 @@
#include <sys/uio.h>
#include <sys/user.h>
+#if defined(__i386__)
+#include <asm/ldt.h>
+#endif
+
+#include "private/ErrnoRestorer.h"
#include "private/bionic_elf_tls.h"
#include "private/bionic_globals.h"
#include "private/bionic_tls.h"
@@ -72,21 +77,30 @@
// Find the thread-pointer register for the given thread.
void** tp_reg = nullptr;
-
-#if defined(__x86_64__) || defined(__i386__)
+#if defined(__x86_64__)
+ {
+ ErrnoRestorer errno_restorer;
+ errno = 0;
+ uintptr_t fs_base = ptrace(PTRACE_PEEKUSER, tid, offsetof(user_regs_struct, fs_base), nullptr);
+ if (errno == 0) {
+ tp_reg = reinterpret_cast<void**>(fs_base);
+ }
+ }
+#elif defined(__i386__)
struct user_regs_struct regs;
struct iovec pt_iov = {
.iov_base = ®s,
.iov_len = sizeof(regs),
};
+
if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) == 0) {
-#if defined(__x86_64__)
- tp_reg = reinterpret_cast<void**>(regs.fs);
-#elif defined(__i386__)
- tp_reg = reinterpret_cast<void**>(regs.xgs);
-#endif
+ struct user_desc u_info;
+ u_info.entry_number = regs.xgs >> 3;
+ if (ptrace(PTRACE_GET_THREAD_AREA, tid, u_info.entry_number, &u_info) == 0) {
+ tp_reg = reinterpret_cast<void**>(u_info.base_addr);
+ }
}
-#elif defined(__aarch64__) || defined(__arm__)
+#elif defined(__aarch64__)
uint64_t reg;
struct iovec pt_iov {
.iov_base = ®, .iov_len = sizeof(reg),
@@ -95,6 +109,11 @@
if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_TLS, &pt_iov) == 0) {
tp_reg = reinterpret_cast<void**>(reg);
}
+#elif defined(__arm__)
+ if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &tp_reg) != 0) {
+ // Reset the tp_reg if ptrace was unsuccessful.
+ tp_reg = nullptr;
+ }
#endif
if (tp_reg == nullptr) {
diff --git a/tests/Android.bp b/tests/Android.bp
index bf921c8..9b95c61 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -991,6 +991,8 @@
"ld_config_test_helper_lib1",
"ld_config_test_helper_lib2",
"ld_config_test_helper_lib3",
+ "tls_properties_helper",
+ "thread_exit_cb_helper",
],
}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index ef4fddd..4b86faf 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -80,7 +80,7 @@
}
cc_test {
- name: "thread_exit_cb_helper.cpp",
+ name: "thread_exit_cb_helper",
defaults: ["bionic_testlib_defaults"],
srcs: ["thread_exit_cb_helper.cpp"],
cflags: ["-fno-emulated-tls"],
diff --git a/tests/libs/tls_properties_helper.cpp b/tests/libs/tls_properties_helper.cpp
index 3f8d118..5982de4 100644
--- a/tests/libs/tls_properties_helper.cpp
+++ b/tests/libs/tls_properties_helper.cpp
@@ -34,8 +34,19 @@
#include <assert.h>
#include <dlfcn.h>
+#include <elf.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
#include <stdio.h>
-#include <unistd.h> // for gettid
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <unistd.h>
// Helper binary to use TLS-related functions in thread_properties
@@ -59,20 +70,54 @@
// Tests iterate_dynamic tls chunks.
// Export a var from the shared so.
__thread char large_tls_var[4 * 1024 * 1024];
+// found_count has to be Global variable so that the non-capturing lambda
+// can access it.
+int found_count = 0;
void test_iter_tls() {
void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
-
- int i = 0;
- auto cb = [&](void* dtls_begin, void* dtls_end, size_t dso_id, void* arg) {
- printf("iterate_cb i = %d\n", i++);
+ large_tls_var[1025] = 'a';
+ auto cb = +[](void* dtls_begin, void* dtls_end, size_t dso_id, void* arg) {
+ if (&large_tls_var >= dtls_begin && &large_tls_var < dtls_end) ++found_count;
};
__libc_iterate_dynamic_tls(gettid(), cb, nullptr);
+
+ // It should be found exactly once.
+ assert(found_count == 1);
printf("done_iterate_dynamic_tls\n");
}
+void* parent_addr = nullptr;
+void test_iterate_another_thread_tls() {
+ large_tls_var[1025] = 'b';
+ parent_addr = &large_tls_var;
+ found_count = 0;
+
+ pid_t pid = fork();
+ assert(pid != -1);
+ int status;
+ if (pid) {
+ // Parent.
+ assert(pid == wait(&status));
+ assert(0 == status);
+ } else {
+ // Child.
+ pid_t parent_pid = getppid();
+ assert(0 == ptrace(PTRACE_ATTACH, parent_pid));
+ assert(parent_pid == waitpid(parent_pid, &status, 0));
+
+ auto cb = +[](void* dtls_begin, void* dtls_end, size_t dso_id, void* arg) {
+ if (parent_addr >= dtls_begin && parent_addr < dtls_end) ++found_count;
+ };
+ __libc_iterate_dynamic_tls(parent_pid, cb, nullptr);
+ // It should be found exactly once.
+ assert(found_count == 1);
+ printf("done_iterate_another_thread_tls\n");
+ }
+}
int main() {
test_static_tls_bounds();
test_iter_tls();
+ test_iterate_another_thread_tls();
return 0;
}