Merge "Improve pthread_create failure handling." am: e27d29cfaf

am: 87c0bddba9

Change-Id: Ieb9ccb4484be2f83f7df47cd96ff4682e75ee7e2
This commit is contained in:
Elliott Hughes 2017-09-19 23:54:58 +00:00 committed by android-build-merger
commit 30c907352c
5 changed files with 65 additions and 20 deletions

View file

@ -468,19 +468,6 @@ static int open_log_socket() {
return log_fd; return log_fd;
} }
static clockid_t log_clockid() {
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
ScopedPthreadMutexLocker locker(&mutex);
static CachedProperty ro_logd_timestamp("ro.logd.timestamp");
static CachedProperty persist_logd_timestamp("persist.logd.timestamp");
char ch = persist_logd_timestamp.Get()[0];
if (ch == '\0') ch = ro_logd_timestamp.Get()[0];
return (tolower(ch) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
}
struct log_time { // Wire format struct log_time { // Wire format
uint32_t tv_sec; uint32_t tv_sec;
uint32_t tv_nsec; uint32_t tv_nsec;
@ -501,7 +488,7 @@ int async_safe_write_log(int priority, const char* tag, const char* msg) {
vec[1].iov_base = &tid; vec[1].iov_base = &tid;
vec[1].iov_len = sizeof(tid); vec[1].iov_len = sizeof(tid);
timespec ts; timespec ts;
clock_gettime(log_clockid(), &ts); clock_gettime(CLOCK_REALTIME, &ts);
log_time realtime_ts; log_time realtime_ts;
realtime_ts.tv_sec = ts.tv_sec; realtime_ts.tv_sec = ts.tv_sec;
realtime_ts.tv_nsec = ts.tv_nsec; realtime_ts.tv_nsec = ts.tv_nsec;

View file

@ -70,7 +70,7 @@ void __libc_init_main_thread(KernelArgumentBlock& args) {
// set up before we call any function that might get a stack check inserted. // set up before we call any function that might get a stack check inserted.
// TLS also needs to be set up before errno (and therefore syscalls) can be used. // TLS also needs to be set up before errno (and therefore syscalls) can be used.
__set_tls(main_thread.tls); __set_tls(main_thread.tls);
__init_tls(&main_thread); if (!__init_tls(&main_thread)) async_safe_fatal("failed to initialize TLS: %s", strerror(errno));
// Tell the kernel to clear our tid field when we exit, so we're like any other pthread. // Tell the kernel to clear our tid field when we exit, so we're like any other pthread.
// As a side-effect, this tells us our pid (which is the same as the main thread's tid). // As a side-effect, this tells us our pid (which is the same as the main thread's tid).

View file

@ -50,7 +50,7 @@ void __init_user_desc(struct user_desc*, bool, void*);
#endif #endif
// This code is used both by each new pthread and the code that initializes the main thread. // This code is used both by each new pthread and the code that initializes the main thread.
void __init_tls(pthread_internal_t* thread) { bool __init_tls(pthread_internal_t* thread) {
// Slot 0 must point to itself. The x86 Linux kernel reads the TLS from %fs:0. // Slot 0 must point to itself. The x86 Linux kernel reads the TLS from %fs:0.
thread->tls[TLS_SLOT_SELF] = thread->tls; thread->tls[TLS_SLOT_SELF] = thread->tls;
thread->tls[TLS_SLOT_THREAD_ID] = thread; thread->tls[TLS_SLOT_THREAD_ID] = thread;
@ -59,16 +59,25 @@ void __init_tls(pthread_internal_t* thread) {
size_t allocation_size = BIONIC_TLS_SIZE + (2 * PTHREAD_GUARD_SIZE); size_t allocation_size = BIONIC_TLS_SIZE + (2 * PTHREAD_GUARD_SIZE);
void* allocation = mmap(nullptr, allocation_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); void* allocation = mmap(nullptr, allocation_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (allocation == MAP_FAILED) { if (allocation == MAP_FAILED) {
async_safe_fatal("failed to allocate TLS: %s", strerror(errno)); async_safe_format_log(ANDROID_LOG_WARN, "libc",
"pthread_create failed: couldn't allocate TLS: %s", strerror(errno));
return false;
} }
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, allocation, allocation_size, "bionic TLS guard"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, allocation, allocation_size, "bionic TLS guard");
// Carve out the writable TLS section.
thread->bionic_tls = reinterpret_cast<bionic_tls*>(static_cast<char*>(allocation) + thread->bionic_tls = reinterpret_cast<bionic_tls*>(static_cast<char*>(allocation) +
PTHREAD_GUARD_SIZE); PTHREAD_GUARD_SIZE);
if (mprotect(thread->bionic_tls, BIONIC_TLS_SIZE, PROT_READ | PROT_WRITE) != 0) { if (mprotect(thread->bionic_tls, BIONIC_TLS_SIZE, PROT_READ | PROT_WRITE) != 0) {
async_safe_fatal("failed to mprotect TLS: %s", strerror(errno)); async_safe_format_log(ANDROID_LOG_WARN, "libc",
"pthread_create failed: couldn't mprotect TLS: %s", strerror(errno));
munmap(allocation, allocation_size);
return false;
} }
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, thread->bionic_tls, BIONIC_TLS_SIZE, "bionic TLS"); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, thread->bionic_tls, BIONIC_TLS_SIZE, "bionic TLS");
return true;
} }
void __init_thread_stack_guard(pthread_internal_t* thread) { void __init_thread_stack_guard(pthread_internal_t* thread) {
@ -194,7 +203,10 @@ static int __allocate_thread(pthread_attr_t* attr, pthread_internal_t** threadp,
thread->mmap_size = mmap_size; thread->mmap_size = mmap_size;
thread->attr = *attr; thread->attr = *attr;
__init_tls(thread); if (!__init_tls(thread)) {
if (thread->mmap_size != 0) munmap(thread->attr.stack_base, thread->mmap_size);
return EAGAIN;
}
__init_thread_stack_guard(thread); __init_thread_stack_guard(thread);
*threadp = thread; *threadp = thread;

View file

@ -115,7 +115,7 @@ class pthread_internal_t {
}; };
__LIBC_HIDDEN__ int __init_thread(pthread_internal_t* thread); __LIBC_HIDDEN__ int __init_thread(pthread_internal_t* thread);
__LIBC_HIDDEN__ void __init_tls(pthread_internal_t* thread); __LIBC_HIDDEN__ bool __init_tls(pthread_internal_t* thread);
__LIBC_HIDDEN__ void __init_thread_stack_guard(pthread_internal_t* thread); __LIBC_HIDDEN__ void __init_thread_stack_guard(pthread_internal_t* thread);
__LIBC_HIDDEN__ void __init_alternate_signal_stack(pthread_internal_t*); __LIBC_HIDDEN__ void __init_alternate_signal_stack(pthread_internal_t*);

View file

@ -2100,3 +2100,49 @@ TEST(pthread, pthread_spinlock_smoke) {
ASSERT_EQ(0, pthread_spin_unlock(&lock)); ASSERT_EQ(0, pthread_spin_unlock(&lock));
ASSERT_EQ(0, pthread_spin_destroy(&lock)); ASSERT_EQ(0, pthread_spin_destroy(&lock));
} }
TEST(pthread, pthread_attr_setdetachstate) {
pthread_attr_t attr;
ASSERT_EQ(0, pthread_attr_init(&attr));
ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE));
ASSERT_EQ(EINVAL, pthread_attr_setdetachstate(&attr, 123));
}
TEST(pthread, pthread_create__mmap_failures) {
pthread_attr_t attr;
ASSERT_EQ(0, pthread_attr_init(&attr));
ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
const auto kPageSize = sysconf(_SC_PAGE_SIZE);
// Use up all the VMAs. By default this is 64Ki.
std::vector<void*> pages;
int prot = PROT_NONE;
while (true) {
void* page = mmap(nullptr, kPageSize, prot, MAP_ANON|MAP_PRIVATE, -1, 0);
if (page == MAP_FAILED) break;
pages.push_back(page);
prot = (prot == PROT_NONE) ? PROT_READ : PROT_NONE;
}
// Try creating threads, freeing up a page each time we fail.
size_t EAGAIN_count = 0;
size_t i = 0;
for (; i < pages.size(); ++i) {
pthread_t t;
int status = pthread_create(&t, &attr, IdFn, nullptr);
if (status != EAGAIN) break;
++EAGAIN_count;
ASSERT_EQ(0, munmap(pages[i], kPageSize));
}
// Creating a thread uses at least six VMAs: the stack, the TLS, and a guard each side of both.
// So we should have seen at least six failures.
ASSERT_GE(EAGAIN_count, 6U);
for (; i < pages.size(); ++i) {
ASSERT_EQ(0, munmap(pages[i], kPageSize));
}
}