Allocate a small guard region around the shadow call stack.
This lets us do two things:
1) Make setjmp and longjmp compatible with shadow call stack.
To avoid leaking the shadow call stack address into memory, only the
lower log2(SCS_SIZE) bits of x18 are stored to jmp_buf. This requires
allocating an additional guard page so that we're guaranteed to be
able to allocate a sufficiently aligned SCS.
2) SCS overflow detection. Overflows now result in a SIGSEGV instead
of corrupting the allocation that comes after it.
Change-Id: I04d6634f96162bf625684672a87fba8b402b7fd1
Test: bionic-unit-tests
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 720a3ae..6f632e8 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -39,6 +39,7 @@
#include <async_safe/log.h>
+#include "private/bionic_constants.h"
#include "private/bionic_defs.h"
#include "private/bionic_macros.h"
#include "private/bionic_ssp.h"
@@ -112,14 +113,19 @@
static void __init_shadow_call_stack(pthread_internal_t* thread __unused) {
#ifdef __aarch64__
- // Allocate the stack and store its address in register x18.
- // TODO(pcc): We ought to allocate a guard region here and then allocate the SCS at a random
- // location within it. This will provide greater security since it would mean that an attacker who
- // can read the pthread_internal_t won't be able to discover the address of the SCS. However,
- // doing so is blocked on a solution to b/118642754.
- char* scs = reinterpret_cast<char*>(
- mmap(nullptr, SCS_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0));
- thread->shadow_call_stack_guard_region = scs;
+ // Allocate the stack and store its address in register x18. The address is aligned to SCS_SIZE so
+ // that we only need to store the lower log2(SCS_SIZE) bits in jmp_buf.
+ // TODO(pcc): We ought to allocate a larger guard region here and then allocate the SCS at a
+ // random location within it. This will provide greater security since it would mean that an
+ // attacker who can read the pthread_internal_t won't be able to discover the address of the SCS.
+ // However, doing so is blocked on a solution to b/118642754.
+ char* scs_guard_region = reinterpret_cast<char*>(
+ mmap(nullptr, SCS_GUARD_REGION_SIZE, 0, MAP_PRIVATE | MAP_ANON, -1, 0));
+ thread->shadow_call_stack_guard_region = scs_guard_region;
+
+ char* scs =
+ reinterpret_cast<char*>(align_up(reinterpret_cast<uintptr_t>(scs_guard_region), SCS_SIZE));
+ mprotect(scs, SCS_SIZE, PROT_READ | PROT_WRITE);
__asm__ __volatile__("mov x18, %0" ::"r"(scs));
#endif
}
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index a884be5..2d4d6cf 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -33,6 +33,7 @@
#include <string.h>
#include <sys/mman.h>
+#include "private/bionic_constants.h"
#include "private/bionic_defs.h"
#include "private/ScopedSignalBlocker.h"
#include "pthread_internal.h"
@@ -105,7 +106,7 @@
#ifdef __aarch64__
// Free the shadow call stack and guard pages.
- munmap(thread->shadow_call_stack_guard_region, SCS_SIZE);
+ munmap(thread->shadow_call_stack_guard_region, SCS_GUARD_REGION_SIZE);
#endif
ThreadJoinState old_state = THREAD_NOT_JOINED;
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 3d58121..81b885a 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -209,9 +209,6 @@
// Leave room for a guard page in the internally created signal stacks.
#define SIGNAL_STACK_SIZE (SIGNAL_STACK_SIZE_WITHOUT_GUARD + PTHREAD_GUARD_SIZE)
-// Size of the shadow call stack.
-#define SCS_SIZE (8 * 1024)
-
// Needed by fork.
__LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare();
__LIBC_HIDDEN__ extern void __bionic_atfork_run_child();