diff --git a/libc/Android.bp b/libc/Android.bp index 3c56ed76552..1fd12a71d83 100644 --- a/libc/Android.bp +++ b/libc/Android.bp @@ -45,6 +45,8 @@ libc_common_flags = [ // ever touch 0, 1, or 2 bytes into a call to memset, which was never going // to amortize.) "-fno-builtin", + + "-DH_MALLOC_PREFIX", ] soong_config_module_type { @@ -187,22 +189,26 @@ cc_defaults { cc_defaults { name: "libc_native_allocator_defaults", - whole_static_libs: [ - "libscudo", - ], - cflags: [ - "-DUSE_SCUDO", - ], header_libs: ["gwp_asan_headers"], - product_variables: { - malloc_low_memory: { - cflags: ["-UUSE_SCUDO"], - whole_static_libs: [ + whole_static_libs: ["libscudo"], + + multilib: { + lib32: { + cflags: ["-DUSE_SCUDO"], + exclude_static_libs: [ "libjemalloc5", "libc_jemalloc_wrapper", ], - exclude_static_libs: [ - "libscudo", + }, + lib64: { + srcs: [":libc_h_malloc_wrapper_src"], + cflags: [ + "-DH_MALLOC_PREFIX", + "-DUSE_H_MALLOC", + "-DUSE_SCUDO", + ], + whole_static_libs: [ + "libhardened_malloc", ], }, }, @@ -213,12 +219,20 @@ cc_defaults { cc_library_static { name: "libc_jemalloc_wrapper", defaults: ["libc_defaults"], - srcs: ["bionic/jemalloc_wrapper.cpp"], + multilib: { + lib32: { + // Used to pull in the jemalloc/hardened_malloc include directory so that if the + // library is removed, the include directory is also removed. + srcs: ["bionic/jemalloc_wrapper.cpp"], + static_libs: ["libjemalloc5"], + }, + lib64: { + srcs: [":libc_h_malloc_wrapper_src"], + static_libs: ["libhardened_malloc"], + }, + }, cflags: ["-fvisibility=hidden"], - // Used to pull in the jemalloc include directory so that if the - // library is removed, the include directory is also removed. - static_libs: ["libjemalloc5"], } // ======================================================== @@ -939,6 +953,7 @@ cc_library_static { "bionic/exec.cpp", "bionic/execinfo.cpp", "bionic/exit.cpp", + "bionic/explicit_bzero.cpp", "bionic/faccessat.cpp", "bionic/fchmod.cpp", "bionic/fchmodat.cpp", @@ -1535,6 +1550,11 @@ cc_library_static { ], } +filegroup { + name: "libc_h_malloc_wrapper_src", + srcs: ["bionic/h_malloc_wrapper.cpp"], +} + filegroup { name: "libc_sources_shared", srcs: [ diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT index 3622ca4f450..fcecbfa4cea 100644 --- a/libc/SYSCALLS.TXT +++ b/libc/SYSCALLS.TXT @@ -73,6 +73,7 @@ tgkill(pid_t tgid, pid_t tid, int sig) all __brk:brk(void*) all execve(const char*, char* const*, char* const*) all +__execveat:execveat(int, const char*, char* const*, char* const*, int) all __ptrace:ptrace(int request, int pid, void* addr, void* data) all # diff --git a/libc/bionic/__cxa_thread_atexit_impl.cpp b/libc/bionic/__cxa_thread_atexit_impl.cpp index 99077c101df..74608513ef0 100644 --- a/libc/bionic/__cxa_thread_atexit_impl.cpp +++ b/libc/bionic/__cxa_thread_atexit_impl.cpp @@ -13,15 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include #include #include +#include #include "pthread_internal.h" class thread_local_dtor { public: - void (*func) (void *); + uintptr_t func; void *arg; void *dso_handle; // unused... thread_local_dtor* next; @@ -35,7 +37,7 @@ __BIONIC_WEAK_FOR_NATIVE_BRIDGE int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) { thread_local_dtor* dtor = new thread_local_dtor(); - dtor->func = func; + dtor->func = __libc_globals->dtor_cookie ^ reinterpret_cast(func); dtor->arg = arg; dtor->dso_handle = dso_handle; @@ -54,7 +56,7 @@ extern "C" __LIBC_HIDDEN__ void __cxa_thread_finalize() { thread_local_dtor* current = thread->thread_local_dtors; thread->thread_local_dtors = current->next; - current->func(current->arg); + (reinterpret_cast(__libc_globals->dtor_cookie ^ current->func))(current->arg); if (__loader_remove_thread_local_dtor != nullptr) { __loader_remove_thread_local_dtor(current->dso_handle); } diff --git a/libc/bionic/__libc_init_main_thread.cpp b/libc/bionic/__libc_init_main_thread.cpp index a057b03293b..5c76f04839c 100644 --- a/libc/bionic/__libc_init_main_thread.cpp +++ b/libc/bionic/__libc_init_main_thread.cpp @@ -28,6 +28,9 @@ #include "libc_init_common.h" +#include +#include + #include #include "private/KernelArgumentBlock.h" @@ -35,17 +38,23 @@ #include "private/bionic_defs.h" #include "private/bionic_elf_tls.h" #include "private/bionic_globals.h" -#include "private/bionic_ssp.h" #include "pthread_internal.h" extern "C" pid_t __getpid(); extern "C" int __set_tid_address(int* tid_address); // Declared in "private/bionic_ssp.h". -uintptr_t __stack_chk_guard = 0; +__attribute__((aligned(max_android_page_size()))) +uintptr_t __stack_chk_guard[max_android_page_size() / sizeof(uintptr_t)] = {0}; BIONIC_USED_BEFORE_LINKER_RELOCATES static pthread_internal_t main_thread; +#if __LP64__ +static const uintptr_t canary_mask = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ? + 0xffffffffffffff00UL : + 0x00ffffffffffffffUL; +#endif + // Setup for the main thread. For dynamic executables, this is called by the // linker _before_ libc is mapped in memory. This means that all writes to // globals from this function will apply to linker-private copies and will not @@ -108,10 +117,20 @@ void __init_tcb_dtv(bionic_tcb* tcb) { // Note in particular that it is not possible to return from any existing // stack frame with stack protector enabled after this function is called. extern "C" void android_reset_stack_guards() { + if (mprotect(__stack_chk_guard, sizeof(__stack_chk_guard), PROT_READ|PROT_WRITE) == -1) { + async_safe_fatal("mprotect __stack_chk_guard: %s", strerror(errno)); + } // The TLS stack guard is set from the global, so ensure that we've initialized the global // before we initialize the TLS. Dynamic executables will initialize their copy of the global // stack protector from the one in the main thread's TLS. - __libc_arc4random_buf_or_die(&__stack_chk_guard, sizeof(__stack_chk_guard)); + __libc_arc4random_buf_or_die(&__stack_chk_guard[0], sizeof(__stack_chk_guard[0])); +#if __LP64__ + // Sacrifice 8 bits of entropy on 64-bit to mitigate non-terminated C string overflows + __stack_chk_guard[0] &= canary_mask; +#endif + if (mprotect(__stack_chk_guard, sizeof(__stack_chk_guard), PROT_READ) == -1) { + async_safe_fatal("mprotect __stack_chk_guard: %s", strerror(errno)); + } __init_tcb_stack_guard(__get_bionic_tcb()); } diff --git a/libc/bionic/android_mallopt.cpp b/libc/bionic/android_mallopt.cpp index 79e40723765..d6d0257b1f8 100644 --- a/libc/bionic/android_mallopt.cpp +++ b/libc/bionic/android_mallopt.cpp @@ -66,14 +66,6 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) { *reinterpret_cast(arg) = atomic_load(&__libc_globals->decay_time_enabled); return true; } - if (opcode == M_INITIALIZE_GWP_ASAN) { - if (arg == nullptr || arg_size != sizeof(android_mallopt_gwp_asan_options_t)) { - errno = EINVAL; - return false; - } - - return EnableGwpAsan(*reinterpret_cast(arg)); - } if (opcode == M_MEMTAG_STACK_IS_ON) { if (arg == nullptr || arg_size != sizeof(bool)) { errno = EINVAL; diff --git a/libc/bionic/bionic_systrace.cpp b/libc/bionic/bionic_systrace.cpp index ff222936ccc..7bed3cdfe1f 100644 --- a/libc/bionic/bionic_systrace.cpp +++ b/libc/bionic/bionic_systrace.cpp @@ -29,8 +29,6 @@ #include #include // For ATRACE_TAG_BIONIC. -#define WRITE_OFFSET 32 - static Lock g_lock; static CachedProperty g_debug_atrace_tags_enableflags("debug.atrace.tags.enableflags"); static uint64_t g_tags; @@ -61,15 +59,9 @@ static void trace_begin_internal(const char* message) { return; } - // If bionic tracing has been enabled, then write the message to the - // kernel trace_marker. - int length = strlen(message); - char buf[length + WRITE_OFFSET]; - size_t len = async_safe_format_buffer(buf, length + WRITE_OFFSET, "B|%d|%s", getpid(), message); - // Tracing may stop just after checking property and before writing the message. // So the write is acceptable to fail. See b/20666100. - TEMP_FAILURE_RETRY(write(trace_marker_fd, buf, len)); + async_safe_format_fd(trace_marker_fd, "B|%d|%s", getpid(), message); } void bionic_trace_begin(const char* message) { diff --git a/libc/bionic/brk.cpp b/libc/bionic/brk.cpp index 566c33a7a6d..ef93055139b 100644 --- a/libc/bionic/brk.cpp +++ b/libc/bionic/brk.cpp @@ -29,48 +29,16 @@ #include #include -#if defined(__LP64__) -static void* __bionic_brk; -#else -void* __bionic_brk; // Accidentally exported by the NDK. +#if !defined(__LP64__) +void* __bionic_brk = reinterpret_cast(-1); // Accidentally exported by the NDK. #endif -extern "C" void* __brk(void* __addr); - -int brk(void* end_data) { - __bionic_brk = __brk(end_data); - if (__bionic_brk < end_data) { - errno = ENOMEM; - return -1; - } - return 0; +int brk(void*) { + errno = ENOMEM; + return -1; } -void* sbrk(ptrdiff_t increment) { - // Initialize __bionic_brk if necessary. - if (__bionic_brk == nullptr) { - __bionic_brk = __brk(nullptr); - } - - // Don't ask the kernel if we already know the answer. - if (increment == 0) { - return __bionic_brk; - } - - // Avoid overflow. - uintptr_t old_brk = reinterpret_cast(__bionic_brk); - if ((increment > 0 && static_cast(increment) > (UINTPTR_MAX - old_brk)) || - (increment < 0 && static_cast(-increment) > old_brk)) { - errno = ENOMEM; - return reinterpret_cast(-1); - } - - void* desired_brk = reinterpret_cast(old_brk + increment); - __bionic_brk = __brk(desired_brk); - if (__bionic_brk < desired_brk) { - errno = ENOMEM; - return reinterpret_cast(-1); - } - - return reinterpret_cast(old_brk); +void* sbrk(ptrdiff_t) { + errno = ENOMEM; + return reinterpret_cast(-1); } diff --git a/libc/bionic/exec.cpp b/libc/bionic/exec.cpp index 56544d03905..e76047da876 100644 --- a/libc/bionic/exec.cpp +++ b/libc/bionic/exec.cpp @@ -181,3 +181,9 @@ int fexecve(int fd, char* const* argv, char* const* envp) { if (errno == ENOENT) errno = EBADF; return -1; } + +extern "C" int __execveat(int dirfd, const char* pathname, char* const* argv, char* const* envp, int flags); + +int execveat(int dirfd, const char* pathname, char* const* argv, char* const* envp, int flags) { + return __execveat(dirfd, pathname, argv, envp, flags); +} diff --git a/libc/bionic/explicit_bzero.cpp b/libc/bionic/explicit_bzero.cpp new file mode 100644 index 00000000000..e3cc182d68d --- /dev/null +++ b/libc/bionic/explicit_bzero.cpp @@ -0,0 +1,6 @@ +#include + +void explicit_bzero(void* _Nonnull s, size_t n) { + void *ptr = memset(s, 0, n); + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +} diff --git a/libc/bionic/getentropy.cpp b/libc/bionic/getentropy.cpp index 5272c34f8d9..bd96be0d41a 100644 --- a/libc/bionic/getentropy.cpp +++ b/libc/bionic/getentropy.cpp @@ -34,22 +34,6 @@ #include "private/ScopedFd.h" -static int getentropy_urandom(void* buffer, size_t buffer_size, int saved_errno) { - ScopedFd fd(TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_NOFOLLOW | O_CLOEXEC, 0))); - if (fd.get() == -1) return -1; - - size_t collected = 0; - while (collected < buffer_size) { - ssize_t count = TEMP_FAILURE_RETRY(read(fd.get(), static_cast(buffer) + collected, - buffer_size - collected)); - if (count == -1) return -1; - collected += count; - } - - errno = saved_errno; - return 0; -} - int getentropy(void* buffer, size_t buffer_size) { if (buffer_size > GETENTROPY_MAX) { errno = EINVAL; @@ -60,22 +44,10 @@ int getentropy(void* buffer, size_t buffer_size) { size_t collected = 0; while (collected < buffer_size) { - long count = TEMP_FAILURE_RETRY(getrandom(static_cast(buffer) + collected, - buffer_size - collected, GRND_NONBLOCK)); + long count = TEMP_FAILURE_RETRY( + getrandom(static_cast(buffer) + collected, buffer_size - collected, 0)); if (count == -1) { - // One of several things could have gone wrong: - // EAGAIN: there isn't enough entropy right now. - // ENOSYS/EINVAL: getrandom(2) or GRND_NONBLOCK isn't supported. - // EFAULT: `buffer` is invalid. - // Realistically we're here because of EAGAIN, - // for which /dev/urandom is the solution --- - // it'll return low entropy randomness where getrandom() won't, - // but we fall back /dev/urandom for all cases because it can't hurt, - // and we don't need to optimize the EFAULT case. - // See https://man7.org/linux/man-pages/man7/random.7.html for getrandom() - // vs /dev/random vs /dev/urandom. - // See http://b/33059407 and http://b/67015565. - return getentropy_urandom(buffer, buffer_size, saved_errno); + return -1; } collected += count; } diff --git a/libc/bionic/gwp_asan_wrappers.h b/libc/bionic/gwp_asan_wrappers.h index 219da9fc580..9053d4aa4b8 100644 --- a/libc/bionic/gwp_asan_wrappers.h +++ b/libc/bionic/gwp_asan_wrappers.h @@ -35,13 +35,6 @@ #include "private/bionic_globals.h" #include "private/bionic_malloc_dispatch.h" -// Enable GWP-ASan, used by android_mallopt. Should always be called in a -// single-threaded context. -bool EnableGwpAsan(const android_mallopt_gwp_asan_options_t& options); - -// Hooks for libc to possibly install GWP-ASan. -bool MaybeInitGwpAsanFromLibc(libc_globals* globals); - // Returns whether GWP-ASan is the provided dispatch table pointer. Used in // heapprofd's signal-initialization sequence to determine the intermediate // dispatch pointer to use when initing. diff --git a/libc/bionic/h_malloc_wrapper.cpp b/libc/bionic/h_malloc_wrapper.cpp new file mode 100644 index 00000000000..5fb0968c277 --- /dev/null +++ b/libc/bionic/h_malloc_wrapper.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +#include + +#include "h_malloc.h" + +__BEGIN_DECLS +int h_malloc_info(int options, FILE* fp); +__END_DECLS + +int h_malloc_info(int options, FILE* fp) { + if (options != 0) { + errno = EINVAL; + return -1; + } + + fflush(fp); + int fd = fileno(fp); + MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\""); + + // Dump all of the large allocations in the arenas. + for (size_t i = 0; i < h_mallinfo_narenas(); i++) { + struct mallinfo mi = h_mallinfo_arena_info(i); + if (mi.hblkhd != 0) { + MallocXmlElem arena_elem(fd, "heap", "nr=\"%d\"", i); + { + MallocXmlElem(fd, "allocated-large").Contents("%zu", mi.ordblks); + MallocXmlElem(fd, "allocated-huge").Contents("%zu", mi.uordblks); + MallocXmlElem(fd, "allocated-bins").Contents("%zu", mi.fsmblks); + + size_t total = 0; + for (size_t j = 0; j < h_mallinfo_nbins(); j++) { + struct mallinfo mi = h_mallinfo_bin_info(i, j); + if (mi.ordblks != 0) { + MallocXmlElem bin_elem(fd, "bin", "nr=\"%d\"", j); + MallocXmlElem(fd, "allocated").Contents("%zu", mi.ordblks); + MallocXmlElem(fd, "nmalloc").Contents("%zu", mi.uordblks); + MallocXmlElem(fd, "ndalloc").Contents("%zu", mi.fordblks); + total += mi.ordblks; + } + } + MallocXmlElem(fd, "bins-total").Contents("%zu", total); + } + } + } + + return 0; +} diff --git a/libc/bionic/heap_tagging.cpp b/libc/bionic/heap_tagging.cpp index c7319708c06..6b29f8e8f27 100644 --- a/libc/bionic/heap_tagging.cpp +++ b/libc/bionic/heap_tagging.cpp @@ -36,6 +36,8 @@ #include #include +extern "C" void h_malloc_disable_memory_tagging(); + extern "C" void scudo_malloc_disable_memory_tagging(); extern "C" void scudo_malloc_set_track_allocation_stacks(int); @@ -73,19 +75,26 @@ void SetDefaultHeapTaggingLevel() { }; }); -#if defined(USE_SCUDO) && !__has_feature(hwaddress_sanitizer) + switch (heap_tagging_level) { case M_HEAP_TAGGING_LEVEL_TBI: case M_HEAP_TAGGING_LEVEL_NONE: +#if defined(USE_SCUDO) scudo_malloc_disable_memory_tagging(); +#endif +#if defined(USE_H_MALLOC) + h_malloc_disable_memory_tagging(); +#endif break; case M_HEAP_TAGGING_LEVEL_SYNC: +#if defined(USE_SCUDO) scudo_malloc_set_track_allocation_stacks(1); +#endif break; default: break; } -#endif // USE_SCUDO + #endif // aarch64 } @@ -106,12 +115,31 @@ static bool set_tcf_on_all_threads(int tcf) { pthread_mutex_t g_heap_tagging_lock = PTHREAD_MUTEX_INITIALIZER; +static bool block_heap_tagging_level_downgrade; + +// Requires `g_heap_tagging_lock` to be held. +bool BlockHeapTaggingLevelDowngrade() { + if (block_heap_tagging_level_downgrade) { + return false; + } + block_heap_tagging_level_downgrade = true; + return true; +} + // Requires `g_heap_tagging_lock` to be held. bool SetHeapTaggingLevel(HeapTaggingLevel tag_level) { if (tag_level == heap_tagging_level) { return true; } + if (block_heap_tagging_level_downgrade) { + // allow switching between SYNC and ASYNC, but don't allow disabling memory tagging + if (tag_level < heap_tagging_level && tag_level != M_HEAP_TAGGING_LEVEL_ASYNC) { + error_log("SetHeapTaggingLevel: blocked downgrade of tag level from %i to %i", heap_tagging_level, tag_level); + return false; + } + } + switch (tag_level) { case M_HEAP_TAGGING_LEVEL_NONE: __libc_globals.mutate([](libc_globals* globals) { @@ -134,6 +162,9 @@ bool SetHeapTaggingLevel(HeapTaggingLevel tag_level) { } #if defined(USE_SCUDO) && !__has_feature(hwaddress_sanitizer) scudo_malloc_disable_memory_tagging(); +#endif +#if defined(USE_H_MALLOC) + h_malloc_disable_memory_tagging(); #endif break; case M_HEAP_TAGGING_LEVEL_TBI: diff --git a/libc/bionic/heap_tagging.h b/libc/bionic/heap_tagging.h index 5bc1da0f463..94fd6b72bc0 100644 --- a/libc/bionic/heap_tagging.h +++ b/libc/bionic/heap_tagging.h @@ -40,6 +40,8 @@ void SetDefaultHeapTaggingLevel(); // useful for RAII on this lock. extern pthread_mutex_t g_heap_tagging_lock; +bool BlockHeapTaggingLevelDowngrade(); + // This function can be called in a multithreaded context, and thus should // only be called when holding the `g_heap_tagging_lock`. bool SetHeapTaggingLevel(HeapTaggingLevel level); diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp index 81de336a717..95cee39ce35 100644 --- a/libc/bionic/libc_init_common.cpp +++ b/libc/bionic/libc_init_common.cpp @@ -46,6 +46,7 @@ #include "heap_tagging.h" #include "private/ScopedPthreadMutexLocker.h" #include "private/WriteProtected.h" +#include "private/bionic_arc4random.h" #include "private/bionic_defs.h" #include "private/bionic_globals.h" #include "private/bionic_tls.h" @@ -89,6 +90,7 @@ void __libc_init_globals() { __libc_globals.mutate([](libc_globals* globals) { __libc_init_vdso(globals); __libc_init_setjmp_cookie(globals); + arc4random_buf(&globals->dtor_cookie, sizeof(globals->dtor_cookie)); }); } diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp index 9f593706d4f..949ffaa3ad7 100644 --- a/libc/bionic/libc_init_dynamic.cpp +++ b/libc/bionic/libc_init_dynamic.cpp @@ -46,12 +46,26 @@ #include "bionic/pthread_internal.h" #include "libc_init_common.h" +#include "async_safe/log.h" #include "private/bionic_elf_tls.h" #include "private/bionic_globals.h" #include "platform/bionic/macros.h" #include "private/bionic_ssp.h" #include "private/bionic_tls.h" #include "private/KernelArgumentBlock.h" +#include "sys/system_properties.h" +#include "sysprop_helpers.h" + +static bool starts_with(const char* s, const char* prefix) { + return strncmp(s, prefix, strlen(prefix)) == 0; +} + +static bool is_debuggable_build() { + char pv[8]; + return get_property_value("ro.debuggable", pv, sizeof(pv)) && strcmp(pv, "1") == 0; +} + +extern "C" const char* __gnu_basename(const char* path); extern "C" { extern void netdClientInit(void); @@ -77,6 +91,74 @@ extern "C" __attribute__((weak)) void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr)* phdr, ElfW(Half) phnum); +static void init_prog_id(libc_globals* globals) { + char exe_path[500]; + ssize_t readlink_res = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1 /* space for NUL terminator */); + if (readlink_res <= 0) { + return; + } + exe_path[readlink_res] = '\0'; + + int prog_id = 0; + int flags = 0; + +#define IS(prog) (!strcmp(exe_path, prog)) + + if (IS("/apex/com.google.pixel.camera.hal/bin/hw/android.hardware.camera.provider@2.7-service-google")) { + prog_id = PROG_PIXEL_CAMERA_PROVIDER_SERVICE; + } + else if (IS("/system/bin/surfaceflinger")) { + prog_id = PROG_SURFACEFLINGER; + } + else if (IS("/vendor/bin/hw/android.hardware.audio.service")) { + // needed for Pixel Tablet as of Android 15, see https://github.com/GrapheneOS/os-issue-tracker/issues/4306 + flags = GLOBAL_FLAG_DISABLE_HARDENED_MALLOC; + } else if (IS("/vendor/bin/shared_modem_platform")) { + char device_name[PROP_VALUE_MAX]; + get_property_value("ro.product.name", device_name, sizeof(device_name)); + if (strcmp(device_name, "tokay") == 0 + || strcmp(device_name, "caiman") == 0 + || strcmp(device_name, "komodo") == 0 + || strcmp(device_name, "comet") == 0 + || strcmp(device_name, "stallion") == 0 + || strcmp(device_name, "frankel") == 0 + || strcmp(device_name, "blazer") == 0 + || strcmp(device_name, "mustang") == 0 + || strcmp(device_name, "rango") == 0 + ) { + flags = GLOBAL_FLAG_DISABLE_HARDENED_MALLOC; + } + } + +#undef IS + + bool is_debuggable = is_debuggable_build(); + const bool is_vendor_prog = starts_with(exe_path, "/vendor/") || starts_with(exe_path, "/apex/com.google."); + if (is_debuggable && is_vendor_prog) { + const char* basename = __gnu_basename(exe_path); + static const char propName[] = "persist.device_config.memory_safety_native.hardened_malloc.mode_override.process."; + char sysprop_name[512]; + char sysprop_value[PROP_VALUE_MAX] = {}; + async_safe_format_buffer(sysprop_name, sizeof(sysprop_name), "%s%s", propName, + basename); + get_property_value(sysprop_name, sysprop_value, sizeof(sysprop_value)); + if (strcmp("disabled", sysprop_value) == 0) { + flags = GLOBAL_FLAG_DISABLE_HARDENED_MALLOC; + } else if (strcmp("enabled", sysprop_value) == 0) { + prog_id = 0; + flags = 0; + } + } + + // libc_globals struct is write-protected + globals->flags = flags; + globals->prog_id = prog_id; +} + +int get_prog_id() { + return __libc_globals->prog_id; +} + // We need a helper function for __libc_preinit because compiling with LTO may // inline functions requiring a stack protector check, but __stack_chk_guard is // not initialized at the start of __libc_preinit. __libc_preinit_impl will run @@ -108,7 +190,13 @@ static void __libc_preinit_impl() { #endif // Hooks for various libraries to let them know that we're starting up. - __libc_globals.mutate(__libc_init_malloc); + __libc_globals.mutate([](libc_globals* globals) { + init_prog_id(globals); + __libc_init_malloc(globals); + + // save the default SIGABRT handler to support restoring it with mallopt(M_BIONIC_RESTORE_DEFAULT_SIGABRT_HANDLER) + sigaction(SIGABRT, nullptr, &globals->saved_sigabrt_handler); + }); // Install reserved signal handlers for assisting the platform's profilers. __libc_init_profiling_handlers(); diff --git a/libc/bionic/libc_init_mte.cpp b/libc/bionic/libc_init_mte.cpp index d23b0563d0e..a95891b5d32 100644 --- a/libc/bionic/libc_init_mte.cpp +++ b/libc/bionic/libc_init_mte.cpp @@ -108,6 +108,15 @@ static HeapTaggingLevel __get_memtag_level_from_note(const ElfW(Phdr) * phdr_sta } } +static bool starts_with(const char* s, const char* prefix) { + return strncmp(s, prefix, strlen(prefix)) == 0; +} + +static bool is_debuggable_build() { + char pv[8]; + return get_property_value("ro.debuggable", pv, sizeof(pv)) && strcmp(pv, "1") == 0; +} + // Returns true if there's an environment setting (either sysprop or env var) // that should overwrite the ELF note, and places the equivalent heap tagging // level into *level. @@ -120,6 +129,40 @@ static bool get_environment_memtag_setting(HeapTaggingLevel* level) { const char* progname = __libc_shared_globals()->init_progname; if (progname == nullptr) return false; + const bool is_vendor_prog = starts_with(progname, "/vendor/") || starts_with(progname, "/apex/com.google."); + const bool is_debug_build = is_debuggable_build(); + if (is_vendor_prog) { + char device_name[PROP_VALUE_MAX]; + get_property_value("ro.product.name", device_name, sizeof(device_name)); + bool apply_override = + strcmp(progname, "/apex/com.google.pixel.camera.hal/bin/hw/android.hardware.camera.provider@2.7-service-google") != 0 + && strcmp(progname, "/apex/com.google.android.widevine/bin/hw/android.hardware.drm-service.widevine-rikers") != 0 + && ( + ( + strcmp(device_name, "tokay") != 0 + && strcmp(device_name, "caiman") != 0 + && strcmp(device_name, "komodo") != 0 + && strcmp(device_name, "comet") != 0 + && strcmp(device_name, "stallion") != 0 + && strcmp(device_name, "frankel") != 0 + && strcmp(device_name, "blazer") != 0 + && strcmp(device_name, "mustang") != 0 + && strcmp(device_name, "rango") != 0 + ) || strcmp(progname, "/vendor/bin/shared_modem_platform") != 0 + ) + ; + if (apply_override) { + *level = M_HEAP_TAGGING_LEVEL_ASYNC; + } else if (!is_debug_build) { + return true; + } + } + + if (!is_debug_build) { + // ignore heap memory tagging setting overrides on user builds + return false; + } + const char* basename = __gnu_basename(progname); char options_str[PROP_VALUE_MAX]; @@ -133,7 +176,7 @@ static bool get_environment_memtag_setting(HeapTaggingLevel* level) { if (!get_config_from_env_or_sysprops("MEMTAG_OPTIONS", sys_prop_names, arraysize(sys_prop_names), options_str, sizeof(options_str))) { - return false; + return is_vendor_prog; } if (strcmp("sync", options_str) == 0) { diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp index 2ae9068a398..5ecabd69109 100644 --- a/libc/bionic/malloc_common.cpp +++ b/libc/bionic/malloc_common.cpp @@ -116,6 +116,36 @@ extern "C" int mallopt(int param, int value) { ScopedPthreadMutexLocker locker(&g_heap_tagging_lock); return SetHeapTaggingLevel(static_cast(value)); } + + if (param == M_BIONIC_BLOCK_HEAP_TAGGING_LEVEL_DOWNGRADE) { + ScopedPthreadMutexLocker locker(&g_heap_tagging_lock); + return BlockHeapTaggingLevelDowngrade(); + } + + if (param == M_BIONIC_ENABLE_SIGCHAINLIB_MTE_SIGSEGV_INTERCEPTION) { + if (__libc_globals->is_sigchainlib_mte_sigsegv_interception_enabled) { + return 0; + } + + __libc_globals.mutate([](libc_globals* globals) { + globals->is_sigchainlib_mte_sigsegv_interception_enabled = true; + }); + + return 1; + } + + if (param == M_BIONIC_SIGCHAINLIB_SHOULD_INTERCEPT_MTE_SIGSEGV) { + return __libc_globals->is_sigchainlib_mte_sigsegv_interception_enabled; + } + + if (param == M_BIONIC_RESTORE_DEFAULT_SIGABRT_HANDLER) { + if (__libc_globals->saved_sigabrt_handler.sa_sigaction != nullptr) { + sigaction(SIGABRT, &__libc_globals->saved_sigabrt_handler, nullptr); + return 1; + } + return 0; + } + if (param == M_BIONIC_ZERO_INIT) { return SetHeapZeroInitialize(value); } @@ -363,9 +393,72 @@ static constexpr MallocDispatch __libc_malloc_default_dispatch __attribute__((un Malloc(malloc_info), }; +#if defined(BOTH_H_MALLOC_AND_SCUDO) + +#define ScudoMalloc(function) scudo_ ## function + +static constexpr MallocDispatch __scudo_malloc_dispatch __attribute__((unused)) = { + ScudoMalloc(calloc), + ScudoMalloc(free), + ScudoMalloc(mallinfo), + ScudoMalloc(malloc), + ScudoMalloc(malloc_usable_size), + ScudoMalloc(memalign), + ScudoMalloc(posix_memalign), + #if defined(HAVE_DEPRECATED_MALLOC_FUNCS) + ScudoMalloc(pvalloc), + #endif + ScudoMalloc(realloc), + ScudoMalloc(reallocarray), + #if defined(HAVE_DEPRECATED_MALLOC_FUNCS) + ScudoMalloc(valloc), + #endif + ScudoMalloc(malloc_iterate), + ScudoMalloc(malloc_disable), + ScudoMalloc(malloc_enable), + ScudoMalloc(mallopt), + ScudoMalloc(aligned_alloc), + ScudoMalloc(malloc_info), +}; + +static const MallocDispatch* native_allocator_dispatch; + +void InitNativeAllocatorDispatch(libc_globals* globals) { + bool hardened_impl = true; + switch (get_prog_id()) { + case PROG_PIXEL_CAMERA_PROVIDER_SERVICE: + case PROG_SURFACEFLINGER: + hardened_impl = false; + break; + default: + if (globals->flags & GLOBAL_FLAG_DISABLE_HARDENED_MALLOC) { + hardened_impl = false; + } else { + hardened_impl = getenv("DISABLE_HARDENED_MALLOC") == nullptr; + } + } + + const MallocDispatch* table = hardened_impl ? + &__libc_malloc_default_dispatch : + &__scudo_malloc_dispatch; + + if (!hardened_impl) { + globals->malloc_dispatch_table = __scudo_malloc_dispatch; + globals->current_dispatch_table = &globals->malloc_dispatch_table; + globals->default_dispatch_table = &globals->malloc_dispatch_table; + } + + native_allocator_dispatch = table; +} + +const MallocDispatch* NativeAllocatorDispatch() { + return native_allocator_dispatch; +} +#else const MallocDispatch* NativeAllocatorDispatch() { return &__libc_malloc_default_dispatch; } +#endif #if !defined(LIBC_STATIC) void MallocInitImpl(libc_globals* globals); diff --git a/libc/bionic/malloc_common.h b/libc/bionic/malloc_common.h index 4afcc4a8d5f..ef4b1a4be8f 100644 --- a/libc/bionic/malloc_common.h +++ b/libc/bionic/malloc_common.h @@ -55,21 +55,27 @@ __END_DECLS #else // __has_feature(hwaddress_sanitizer) -#if defined(USE_SCUDO) - -#include "scudo.h" -#define Malloc(function) scudo_ ## function +#ifdef __LP64__ +#ifndef USE_H_MALLOC +#error missing USE_H_MALLOC +#endif -#elif defined(USE_SCUDO_SVELTE) +#include "h_malloc.h" +#define Malloc(function) h_ ## function +__BEGIN_DECLS +int h_malloc_info(int options, FILE* fp); +__END_DECLS +#if defined(USE_SCUDO) #include "scudo.h" -#define Malloc(function) scudo_svelte_ ## function - -#else +void InitNativeAllocatorDispatch(libc_globals* globals); +#endif -#include "jemalloc.h" -#define Malloc(function) je_ ## function +#define BOTH_H_MALLOC_AND_SCUDO +#else // 32-bit +#include "scudo.h" +#define Malloc(function) scudo_ ## function #endif #endif diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp index dec8f9f00bd..c7d4e474af6 100644 --- a/libc/bionic/malloc_common_dynamic.cpp +++ b/libc/bionic/malloc_common_dynamic.cpp @@ -381,11 +381,13 @@ extern "C" size_t __scudo_get_stack_depot_size(); // Initializes memory allocation framework once per process. void MallocInitImpl(libc_globals* globals) { +#if defined(BOTH_H_MALLOC_AND_SCUDO) + InitNativeAllocatorDispatch(globals); +#endif + char prop[PROP_VALUE_MAX]; char* options = prop; - MaybeInitGwpAsanFromLibc(globals); - #if defined(USE_SCUDO) && !__has_feature(hwaddress_sanitizer) __libc_shared_globals()->scudo_stack_depot = __scudo_get_stack_depot_addr(); __libc_shared_globals()->scudo_region_info = __scudo_get_region_info_addr(); diff --git a/libc/bionic/pthread_atfork.cpp b/libc/bionic/pthread_atfork.cpp index 0dcabdfb2de..d59f3ae54bc 100644 --- a/libc/bionic/pthread_atfork.cpp +++ b/libc/bionic/pthread_atfork.cpp @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include "platform/bionic/macros.h" @@ -43,6 +46,9 @@ struct atfork_t { void* dso_handle; }; +static atfork_t* pool; +static atfork_t* page_list; + class atfork_list_t { public: constexpr atfork_list_t() : first_(nullptr), last_(nullptr) {} @@ -101,7 +107,8 @@ class atfork_list_t { last_ = entry->prev; } - free(entry); + entry->next = pool; + pool = entry; } atfork_t* first_; @@ -154,20 +161,54 @@ void __bionic_atfork_run_parent() { // __register_atfork is the name used by glibc extern "C" int __register_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void), void* dso) { - atfork_t* entry = reinterpret_cast(malloc(sizeof(atfork_t))); - if (entry == nullptr) { - return ENOMEM; + size_t page_size = getpagesize(); + + pthread_mutex_lock(&g_atfork_list_mutex); + + for (atfork_t* page_it = page_list; page_it; page_it = page_it->next) { + mprotect(page_it, page_size, PROT_READ|PROT_WRITE); + } + + if (!pool) { + char* page = static_cast(mmap(NULL, page_size, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)); + if (page == MAP_FAILED) { + for (atfork_t* page_it = page_list; page_it; page_it = page_it->next) { + mprotect(page_it, page_size, PROT_READ); + } + + pthread_mutex_unlock(&g_atfork_list_mutex); + return ENOMEM; + } + + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, page, page_size, + "atfork handlers"); + + for (char* it = page + sizeof(atfork_t); it < page + page_size - sizeof(atfork_t); it += sizeof(atfork_t)) { + atfork_t* node = reinterpret_cast(it); + node->next = pool; + pool = node; + } + + atfork_t* page_node = reinterpret_cast(page); + page_node->next = page_list; + page_list = page_node; } + atfork_t* entry = pool; + pool = entry->next; + entry->prepare = prepare; entry->parent = parent; entry->child = child; entry->dso_handle = dso; - pthread_mutex_lock(&g_atfork_list_mutex); - g_atfork_list.push_back(entry); + for (atfork_t* page_it = page_list; page_it; page_it = page_it->next) { + mprotect(page_it, page_size, PROT_READ); + } + pthread_mutex_unlock(&g_atfork_list_mutex); return 0; @@ -175,8 +216,20 @@ extern "C" int __register_atfork(void (*prepare)(void), void (*parent)(void), extern "C" __LIBC_HIDDEN__ void __unregister_atfork(void* dso) { pthread_mutex_lock(&g_atfork_list_mutex); + + size_t page_size = getpagesize(); + + for (atfork_t* page_it = page_list; page_it; page_it = page_it->next) { + mprotect(page_it, page_size, PROT_READ|PROT_WRITE); + } + g_atfork_list.remove_if([&](const atfork_t* entry) { return entry->dso_handle == dso; }); + + for (atfork_t* page_it = page_list; page_it; page_it = page_it->next) { + mprotect(page_it, page_size, PROT_READ); + } + pthread_mutex_unlock(&g_atfork_list_mutex); } diff --git a/libc/bionic/pthread_attr.cpp b/libc/bionic/pthread_attr.cpp index f27f0eac15b..dcd3acf92d2 100644 --- a/libc/bionic/pthread_attr.cpp +++ b/libc/bionic/pthread_attr.cpp @@ -54,7 +54,7 @@ int pthread_attr_init(pthread_attr_t* attr) { __BIONIC_WEAK_FOR_NATIVE_BRIDGE int pthread_attr_destroy(pthread_attr_t* attr) { - memset(attr, 0x42, sizeof(pthread_attr_t)); + memset(attr, 0xdf, sizeof(pthread_attr_t)); return 0; } diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index 60b5b077234..a55c6cebba9 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -226,10 +227,36 @@ int __init_thread(pthread_internal_t* thread) { ThreadMapping __allocate_thread_mapping(size_t stack_size, size_t stack_guard_size) { const StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout; - // Allocate in order: stack guard, stack, static TLS, libgen buffers, guard page. + // Address calculated using stack_size is passed to mprotect later, so make it page-aligned. + stack_size = __builtin_align_up(stack_size, page_size()); + // Round up static TLS layout size to be multiple of page size as well. + size_t static_tls_layout_size = __builtin_align_up(layout.size(), page_size()); + + // Place a randomly sized gap above the stack, up to 10% as large as the stack + // on 32-bit and 50% on 64-bit where virtual memory is plentiful. +#if __LP64__ + size_t max_gap_size = stack_size / 2; +#else + size_t max_gap_size = stack_size / 10; +#endif + // Make sure the random stack top guard size is a multiple of the page size, + // and always reserve at least one guard page between the stack and the + // pthread_internal_t / static TLS region. arc4random_uniform() can return 0, + // and align_up(0, page_size) is 0, which would leave no guard page at all + // and let a stack overflow silently corrupt pthread_internal_t. + // + // For the main thread, the stack isn't in this mapping and we don't need to + // add a random gap. + size_t gap_size = __builtin_align_up(arc4random_uniform(max_gap_size), page_size()); + if (stack_size > 0 && gap_size == 0) gap_size = page_size(); + + // Allocate in order: stack guard, stack, (random) guard page(s), pthread_internal_t, static TLS, libgen buffers, guard page. size_t mmap_size; if (__builtin_add_overflow(stack_size, stack_guard_size, &mmap_size)) return {}; - if (__builtin_add_overflow(mmap_size, layout.size(), &mmap_size)) return {}; + if (__builtin_add_overflow(mmap_size, gap_size, &mmap_size)) return {}; + size_t thread_page_size = __builtin_align_up(sizeof(pthread_internal_t), page_size()); + if (__builtin_add_overflow(mmap_size, thread_page_size, &mmap_size)) return {}; + if (__builtin_add_overflow(mmap_size, static_tls_layout_size, &mmap_size)) return {}; if (__builtin_add_overflow(mmap_size, PTHREAD_GUARD_SIZE, &mmap_size)) return {}; // Add space for the dedicated libgen buffers page(s). size_t libgen_buffers_padded_size = __builtin_align_up(sizeof(libgen_buffers), page_size()); @@ -240,8 +267,8 @@ ThreadMapping __allocate_thread_mapping(size_t stack_size, size_t stack_guard_si mmap_size = __builtin_align_up(mmap_size, page_size()); if (mmap_size < unaligned_size) return {}; - // Create a new private anonymous map. Make the entire mapping PROT_NONE, then carve out a - // read+write area in the middle. + // Create a new private anonymous map. Make the entire mapping PROT_NONE, then carve out + // read+write areas in the middle. const int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; char* const space = static_cast(mmap(nullptr, mmap_size, PROT_NONE, flags, -1, 0)); if (space == MAP_FAILED) { @@ -250,7 +277,6 @@ ThreadMapping __allocate_thread_mapping(size_t stack_size, size_t stack_guard_si mmap_size); return {}; } - const size_t writable_size = mmap_size - stack_guard_size - PTHREAD_GUARD_SIZE; int prot = PROT_READ | PROT_WRITE; const char* prot_str = "R+W"; #ifdef __aarch64__ @@ -259,11 +285,26 @@ ThreadMapping __allocate_thread_mapping(size_t stack_size, size_t stack_guard_si prot_str = "R+W+MTE"; } #endif - if (mprotect(space + stack_guard_size, writable_size, prot) != 0) { + // Stack is at the lower end of mapped space, stack guard region is at the lower end of stack. + // Make the usable portion of the stack between the guard region and random gap readable and + // writable. + if (mprotect(space + stack_guard_size, stack_size, prot) != 0) { + async_safe_format_log( + ANDROID_LOG_WARN, "libc", + "pthread_create failed: couldn't mprotect %s %zu-byte stack mapping region: %m", prot_str, + stack_size); + munmap(space, mmap_size); + return {}; + } + + const size_t non_stack_writeable_offset = stack_guard_size + stack_size + gap_size; + const size_t non_stack_writeable_size = mmap_size - non_stack_writeable_offset - PTHREAD_GUARD_SIZE; + + if (mprotect(space + non_stack_writeable_offset, non_stack_writeable_size, PROT_READ | PROT_WRITE) != 0) { async_safe_format_log( ANDROID_LOG_WARN, "libc", - "pthread_create failed: couldn't mprotect %s %zu-byte thread mapping region: %m", prot_str, - writable_size); + "pthread_create failed: couldn't mprotect R+W %zu-byte non-stack mapping region: %m", + non_stack_writeable_size); munmap(space, mmap_size); return {}; } @@ -272,7 +313,9 @@ ThreadMapping __allocate_thread_mapping(size_t stack_size, size_t stack_guard_si // // [ PTHREAD_GUARD_SIZE ] // [ libgen_buffers_padded_size (for dedicated page(s) for libgen buffers) ] - // [ layout.size() (for static TLS) ] + // [ static_tls_layout_size ] + // [ thread_page_size (for pthread_internal_t) ] + // [ gap_size (for (random) guard page(s)) ] // [ stack_size ] // [ stack_guard_size ] @@ -282,9 +325,12 @@ ThreadMapping __allocate_thread_mapping(size_t stack_size, size_t stack_guard_si result.mmap_base_unguarded = space + stack_guard_size; result.mmap_size_unguarded = mmap_size - stack_guard_size - PTHREAD_GUARD_SIZE; result.libgen_buffers = space + mmap_size - PTHREAD_GUARD_SIZE - libgen_buffers_padded_size; - result.static_tls = result.libgen_buffers - layout.size(); + result.static_tls = result.libgen_buffers - static_tls_layout_size; result.stack_base = space; - result.stack_top = result.static_tls; + // Choose a random base within the first page of the stack. Waste no more + // than the space originally wasted by pthread_internal_t for compatibility. + result.stack_top = space + stack_guard_size + stack_size - arc4random_uniform(sizeof(pthread_internal_t)); + result.stack_top = __builtin_align_down(result.stack_top, 16); return result; } @@ -314,13 +360,8 @@ static int __allocate_thread(pthread_attr_t* attr, bionic_tcb** tcbp, void** chi stack_top = static_cast(attr->stack_base) + attr->stack_size; } - // Carve out space from the stack for the thread's pthread_internal_t. This - // memory isn't counted in pthread_attr_getstacksize. - - // To safely access the pthread_internal_t and thread stack, we need to find a 16-byte aligned boundary. - stack_top = __builtin_align_down(stack_top - sizeof(pthread_internal_t), 16); - - pthread_internal_t* thread = reinterpret_cast(stack_top); + pthread_internal_t* thread = reinterpret_cast( + mapping.static_tls - __builtin_align_up(sizeof(pthread_internal_t), page_size())); if (!stack_clean) { // If thread was not allocated by mmap(), it may not have been cleared to zero. // So assume the worst and zero it. diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index b3133a92dfa..c10e25fb926 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -72,6 +72,7 @@ class pthread_internal_t { pid_t tid; private: + // accessed from vfork asm via offset of field, so don't put fields above this uint32_t cached_pid_ : 31; uint32_t vforked_ : 1; @@ -268,7 +269,11 @@ __LIBC_HIDDEN__ void pthread_key_clean_all(void); // stack overflows, we subtracted the same amount we were using there // from the default thread stack size. This should keep memory usage // roughly constant. +#ifdef __LP64__ +#define PTHREAD_STACK_SIZE_DEFAULT ((8 * 1024 * 1024) - SIGNAL_STACK_SIZE_WITHOUT_GUARD) +#else #define PTHREAD_STACK_SIZE_DEFAULT ((1 * 1024 * 1024) - SIGNAL_STACK_SIZE_WITHOUT_GUARD) +#endif // Leave room for a guard page in the internally created signal stacks. #define SIGNAL_STACK_SIZE (SIGNAL_STACK_SIZE_WITHOUT_GUARD + PTHREAD_GUARD_SIZE) diff --git a/libc/bionic/sysprop_helpers.cpp b/libc/bionic/sysprop_helpers.cpp index e025edde0d8..d4e276ae6a1 100644 --- a/libc/bionic/sysprop_helpers.cpp +++ b/libc/bionic/sysprop_helpers.cpp @@ -36,7 +36,7 @@ #include -static bool get_property_value(const char* property_name, char* dest, size_t dest_size) { +bool get_property_value(const char* property_name, char* dest, size_t dest_size) { CHECK(property_name); CHECK(dest); CHECK(dest_size != 0); diff --git a/libc/bionic/sysprop_helpers.h b/libc/bionic/sysprop_helpers.h index a02c2dc415b..9ff539f4fdf 100644 --- a/libc/bionic/sysprop_helpers.h +++ b/libc/bionic/sysprop_helpers.h @@ -43,3 +43,5 @@ __LIBC_HIDDEN__ bool get_config_from_env_or_sysprops(const char* env_var_name, const char* const* sys_prop_names, size_t sys_prop_names_size, char* options, size_t options_size); + +__LIBC_HIDDEN__ bool get_property_value(const char* property_name, char* dest, size_t dest_size); diff --git a/libc/bionic/system_property_api.cpp b/libc/bionic/system_property_api.cpp index 34438e7afd1..580deeee31b 100644 --- a/libc/bionic/system_property_api.cpp +++ b/libc/bionic/system_property_api.cpp @@ -135,3 +135,8 @@ int __system_properties_zygote_reload(void) { CHECK(getpid() == gettid()); return system_properties.EnableOverrides() ? 0 : -1; } + +__BIONIC_WEAK_FOR_NATIVE_BRIDGE +int __system_properties_enable_extended_override(void) { + return system_properties.EnableExtendedOverrides() ? 0 : -1; +} diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c index cc771f06e0a..4e8516e3329 100644 --- a/libc/dns/net/getaddrinfo.c +++ b/libc/dns/net/getaddrinfo.c @@ -385,7 +385,7 @@ static int _have_ipv4(unsigned mark, uid_t uid) { static const struct sockaddr_in sin_test = { .sin_family = AF_INET, - .sin_addr.s_addr = __constant_htonl(0x08080808L) // 8.8.8.8 + .sin_addr.s_addr = __constant_htonl(0x08080808L) // 8.8.8.8 (routable public IP, not used to make a connection or send any data) }; sockaddr_union addr = { .in = sin_test }; return _find_src_addr(&addr.generic, NULL, mark, uid) == 1; diff --git a/libc/include/malloc.h b/libc/include/malloc.h index f60292eb523..edbc3f1d0ed 100644 --- a/libc/include/malloc.h +++ b/libc/include/malloc.h @@ -342,6 +342,11 @@ int malloc_info(int __must_be_zero, FILE* _Nonnull __fp) __INTRODUCED_IN(23); */ #define M_BIONIC_SET_HEAP_TAGGING_LEVEL (-204) +#define M_BIONIC_BLOCK_HEAP_TAGGING_LEVEL_DOWNGRADE (-1000) +#define M_BIONIC_ENABLE_SIGCHAINLIB_MTE_SIGSEGV_INTERCEPTION (-1001) +#define M_BIONIC_SIGCHAINLIB_SHOULD_INTERCEPT_MTE_SIGSEGV (-1002) +#define M_BIONIC_RESTORE_DEFAULT_SIGABRT_HANDLER (-1003) + /** * Constants for use with the M_BIONIC_SET_HEAP_TAGGING_LEVEL mallopt() option. */ diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h index cb1ac1fff1d..90b499abc60 100644 --- a/libc/include/stdlib.h +++ b/libc/include/stdlib.h @@ -527,6 +527,12 @@ long double strtold(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ /** Equivalent to strtold() on Android. */ long double strtold_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) __RENAME(strtold); +int get_prog_id(); +#define is_prog(id) (get_prog_id() == id) + +#define PROG_PIXEL_CAMERA_PROVIDER_SERVICE 1 +#define PROG_SURFACEFLINGER 2 + __END_DECLS #endif /* _STDLIB_H */ diff --git a/libc/include/string.h b/libc/include/string.h index 06793c34e8b..602fef664a9 100644 --- a/libc/include/string.h +++ b/libc/include/string.h @@ -66,6 +66,8 @@ void* _Nonnull memmove(void* _Nonnull __dst, const void* _Nonnull __src, size_t */ void* _Nonnull memset(void* _Nonnull __dst, int __ch, size_t __n); +void explicit_bzero(void* _Nonnull s, size_t n); + #if __ANDROID_API__ >= 34 /** * [memset_explicit(3)](https://man7.org/linux/man-pages/man3/memset_explicit.3.html) diff --git a/libc/include/sys/system_properties.h b/libc/include/sys/system_properties.h index 1e72bf47723..c07e3c2b59f 100644 --- a/libc/include/sys/system_properties.h +++ b/libc/include/sys/system_properties.h @@ -240,6 +240,8 @@ int __system_property_update(prop_info* _Nonnull __pi, const char* _Nonnull __va * Available since API level 35. */ int __system_properties_zygote_reload(void) __INTRODUCED_IN(35); + +int __system_properties_enable_extended_override(void); #endif /** diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 260019c3a68..b892684ad53 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -159,6 +159,7 @@ int execv(const char* _Nonnull __path, char* _Nullable const* _Nullable __argv); int execvp(const char* _Nonnull __file, char* _Nullable const* _Nullable __argv); int execvpe(const char* _Nonnull __file, char* _Nullable const* _Nullable __argv, char* _Nullable const* _Nullable __envp); int execve(const char* _Nonnull __file, char* _Nullable const* _Nullable __argv, char* _Nullable const* _Nullable __envp); +int execveat(int dirfd, const char* _Nonnull __file, char* _Nullable const* _Nullable __argv, char* _Nullable const* _Nullable __envp, int flags); int execl(const char* _Nonnull __path, const char* _Nullable __arg0, ...) __attribute__((__sentinel__)); int execlp(const char* _Nonnull __file, const char* _Nullable __arg0, ...) __attribute__((__sentinel__)); int execle(const char* _Nonnull __path, const char* _Nullable __arg0, ... /*, char* const* __envp */) diff --git a/libc/libc.map.txt b/libc/libc.map.txt index b0633eb32fe..ef349bb76eb 100644 --- a/libc/libc.map.txt +++ b/libc/libc.map.txt @@ -330,9 +330,11 @@ LIBC { execlp; execv; execve; + execveat; execvp; execvpe; exit; + explicit_bzero; # introduced=33 faccessat; fallocate; fallocate64; @@ -423,6 +425,7 @@ LIBC { get_nprocs; # introduced=23 get_nprocs_conf; # introduced=23 get_phys_pages; # introduced=23 + get_prog_id; getaddrinfo; getauxval; getc; @@ -1609,6 +1612,7 @@ LIBC_V { # introduced=35 tzfree; wcsrtombs_l; __system_properties_zygote_reload; # apex + __system_properties_enable_extended_override; # apex } LIBC_U; LIBC_36 { # introduced=36 diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h index a7a4afe1d68..713ad4223ec 100644 --- a/libc/private/bionic_globals.h +++ b/libc/private/bionic_globals.h @@ -43,12 +43,15 @@ #include "private/bionic_malloc_dispatch.h" #include "private/bionic_vdso.h" +#define GLOBAL_FLAG_DISABLE_HARDENED_MALLOC 1 + struct libc_globals { vdso_entry vdso[VDSO_END]; long setjmp_cookie; uintptr_t heap_pointer_tag; _Atomic(bool) decay_time_enabled; _Atomic(bool) memtag; + long dtor_cookie; // In order to allow a complete switch between dispatch tables without // the need for copying each function by function in the structure, @@ -64,6 +67,10 @@ struct libc_globals { // limit is enabled and some other hook is enabled at the same time. _Atomic(const MallocDispatch*) default_dispatch_table; MallocDispatch malloc_dispatch_table; + int prog_id; + bool is_sigchainlib_mte_sigsegv_interception_enabled; + struct sigaction saved_sigabrt_handler; + int flags; }; struct memtag_dynamic_entries_t { diff --git a/libc/system_properties/include/system_properties/system_properties.h b/libc/system_properties/include/system_properties/system_properties.h index 6c3b1cd78d9..98c4a8df921 100644 --- a/libc/system_properties/include/system_properties/system_properties.h +++ b/libc/system_properties/include/system_properties/system_properties.h @@ -53,6 +53,7 @@ class SystemProperties { bool Init(const char* filename); bool EnableOverrides(); + bool EnableExtendedOverrides(); bool AreaInit(const char* filename, bool* fsetxattr_failed); bool AreaInit(const char* filename, bool* fsetxattr_failed, bool load_default_path); uint32_t AreaSerial(); @@ -91,6 +92,7 @@ class SystemProperties { bool initialized_; bool use_appcompat_override_; + bool use_extended_override_; PropertiesFilename properties_filename_; PropertiesFilename appcompat_filename_; }; diff --git a/libc/system_properties/system_properties.cpp b/libc/system_properties/system_properties.cpp index ca286e8ee78..42483e1b761 100644 --- a/libc/system_properties/system_properties.cpp +++ b/libc/system_properties/system_properties.cpp @@ -53,6 +53,7 @@ #define SERIAL_VALUE_LEN(serial) ((serial) >> 24) #define APPCOMPAT_PREFIX "ro.appcompat_override." #define APPCOMPAT_OVERRIDE_ENV_VAR "BIONIC_APPCOMPAT_OVERRIDE" +#define EXTENDED_OVERRIDE_ENV_VAR "GOS_HIDE_CARRIER_INFO_PROP_OVERRIDE" static bool is_dir(const char* pathname) { struct stat info; @@ -81,6 +82,11 @@ bool SystemProperties::Init(const char* filename) { use_appcompat_override_ = true; } + if (getenv(EXTENDED_OVERRIDE_ENV_VAR) != nullptr) { + use_appcompat_override_ = true; + use_extended_override_ = true; + } + initialized_ = true; return true; } @@ -151,6 +157,33 @@ bool SystemProperties::EnableOverrides() { return true; } +bool SystemProperties::EnableExtendedOverrides() { + CHECK(initialized_); + use_appcompat_override_ = true; + use_extended_override_ = true; + putenv(const_cast(APPCOMPAT_OVERRIDE_ENV_VAR "=1")); + putenv(const_cast(EXTENDED_OVERRIDE_ENV_VAR "=1")); + return true; +} + +static const char* const kHideCarrierInfoDeniedProps[] = { + "gsm.sim.operator.alpha", + "gsm.sim.operator.numeric", + "gsm.sim.operator.iso-country", + "gsm.operator.alpha", + "gsm.operator.numeric", + "gsm.operator.iso-country", + "gsm.operator.isroaming", + "gsm.sim.state", +}; + +static bool is_hide_carrier_info_denied(const char* name) { + for (const char* denied : kHideCarrierInfoDeniedProps) { + if (strcmp(name, denied) == 0) return true; + } + return false; +} + uint32_t SystemProperties::AreaSerial() { if (!initialized_) { return -1; @@ -170,6 +203,10 @@ const prop_info* SystemProperties::Find(const char* name) { return nullptr; } + if (use_extended_override_ && is_hide_carrier_info_denied(name)) { + return nullptr; + } + // if appcompat override is enabled, we first try finding APPCOMPAT_PREFIXed system // property if (use_appcompat_override_) { @@ -428,6 +465,11 @@ int SystemProperties::Add(const char* name, unsigned int namelen, const char* va CHECK(getpid() == 1 || getuid() == 0); atomic_thread_fence(memory_order_release); memcpy(other_pi->value, value, valuelen + 1); + // the high byte of serial encodes value length, we need to update it so readers + // (that use SERIAL_VALUE_LEN) return the full overridden string. + uint32_t old_serial = atomic_load_explicit(&other_pi->serial, memory_order_relaxed); + uint32_t new_serial = (valuelen << 24) | (old_serial & 0x00ffffff); + atomic_store_explicit(&other_pi->serial, new_serial, memory_order_release); } } diff --git a/libc/upstream-openbsd/android/include/openbsd-compat.h b/libc/upstream-openbsd/android/include/openbsd-compat.h index b89e09ad8e4..86a67361e5f 100644 --- a/libc/upstream-openbsd/android/include/openbsd-compat.h +++ b/libc/upstream-openbsd/android/include/openbsd-compat.h @@ -41,10 +41,6 @@ extern const char* __progname; #define PROTO_NORMAL(x) #define WRAP(x) x -#if !defined(ANDROID_HOST_MUSL) -#define explicit_bzero(p, s) memset_explicit(p, 0, s) -#endif - #if defined(ANDROID_HOST_MUSL) #define __LIBC_HIDDEN__ __attribute__((visibility("hidden"))) #endif diff --git a/linker/linker.cpp b/linker/linker.cpp index 05f1a469c33..6fb5627096b 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -890,14 +890,14 @@ class ZipArchiveCache { ZipArchiveCache() {} ~ZipArchiveCache(); - bool get_or_open(const char* zip_path, ZipArchiveHandle* handle); + bool get_or_open(const char* zip_path, int zip_fd, ZipArchiveHandle* handle); private: DISALLOW_COPY_AND_ASSIGN(ZipArchiveCache); std::unordered_map cache_; }; -bool ZipArchiveCache::get_or_open(const char* zip_path, ZipArchiveHandle* handle) { +bool ZipArchiveCache::get_or_open(const char* zip_path, int zip_fd, ZipArchiveHandle* handle) { std::string key(zip_path); auto it = cache_.find(key); @@ -906,7 +906,7 @@ bool ZipArchiveCache::get_or_open(const char* zip_path, ZipArchiveHandle* handle return true; } - int fd = TEMP_FAILURE_RETRY(open(zip_path, O_RDONLY | O_CLOEXEC)); + int fd = zip_fd != -1 ? dup(zip_fd) : TEMP_FAILURE_RETRY(open(zip_path, O_RDONLY | O_CLOEXEC)); if (fd == -1) { return false; } @@ -957,13 +957,19 @@ static int open_library_in_zipfile(ZipArchiveCache* zip_archive_cache, const char* zip_path = buf; const char* file_path = &buf[separator - path + 2]; - int fd = TEMP_FAILURE_RETRY(open(zip_path, O_RDONLY | O_CLOEXEC)); + int fd; + if (!strncmp("/gmscompat_fd_", zip_path, strlen("/gmscompat_fd_")) && + sscanf(zip_path, "/gmscompat_fd_%d", &fd) == 1) { + fd = dup(fd); + } else { + fd = TEMP_FAILURE_RETRY(open(zip_path, O_RDONLY | O_CLOEXEC)); + } if (fd == -1) { return -1; } ZipArchiveHandle handle; - if (!zip_archive_cache->get_or_open(zip_path, &handle)) { + if (!zip_archive_cache->get_or_open(zip_path, fd, &handle)) { // invalid zip-file (?) close(fd); return -1; diff --git a/tests/Android.bp b/tests/Android.bp index c5687625a8a..b806cee95d7 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -1322,6 +1322,54 @@ cc_test { ], } +// minial static binary for just the system properties tests +cc_test { + name: "bionic-sysprop-tests", + gtest: false, + defaults: [ + "bionic_tests_defaults", + "large_system_property_node_defaults", + ], + host_supported: false, + test_suites: ["general-tests"], + + srcs: [ + "gtest_globals.cpp", + "gtest_main.cpp", + "system_properties_test.cpp", + "utils.cpp", + ], + + include_dirs: [ + "bionic/libc", + ], + + target: { + bionic: { + whole_static_libs: [ + "libasync_safe", + "libprocinfo", + "libsystemproperties", + ], + }, + }, + + static_libs: [ + "libm", + "libc", + "libdl", + "liblog", + "libbase", + "libgtest_isolated", + ], + + static_executable: true, + stl: "libc++_static", + lto: { + never: true, + }, +} + // ----------------------------------------------------------------------------- // Tests to run on the host and linked against glibc. Run with: // cd bionic/tests; mm bionic-unit-tests-glibc-run diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp index 46f234205af..4a6925659f1 100644 --- a/tests/malloc_test.cpp +++ b/tests/malloc_test.cpp @@ -940,29 +940,6 @@ TEST(android_mallopt, set_allocation_limit_multiple_threads) { #endif } -#if defined(__BIONIC__) -using Mode = android_mallopt_gwp_asan_options_t::Mode; -TEST(android_mallopt, DISABLED_multiple_enable_gwp_asan) { - android_mallopt_gwp_asan_options_t options; - options.program_name = ""; // Don't infer GWP-ASan options from sysprops. - options.mode = Mode::APP_MANIFEST_NEVER; - // GWP-ASan should already be enabled. Trying to enable or disable it should - // always pass. - ASSERT_TRUE(android_mallopt(M_INITIALIZE_GWP_ASAN, &options, sizeof(options))); - options.mode = Mode::APP_MANIFEST_DEFAULT; - ASSERT_TRUE(android_mallopt(M_INITIALIZE_GWP_ASAN, &options, sizeof(options))); -} -#endif // defined(__BIONIC__) - -TEST(android_mallopt, multiple_enable_gwp_asan) { -#if defined(__BIONIC__) - // Always enable GWP-Asan, with default options. - RunGwpAsanTest("*.DISABLED_multiple_enable_gwp_asan"); -#else - GTEST_SKIP() << "bionic extension"; -#endif -} - TEST(android_mallopt, memtag_stack_is_on) { #if defined(__BIONIC__) bool memtag_stack; diff --git a/tests/system_properties_test.cpp b/tests/system_properties_test.cpp index b30c8fcfb77..cf9818922b6 100644 --- a/tests/system_properties_test.cpp +++ b/tests/system_properties_test.cpp @@ -227,6 +227,71 @@ TEST(properties, __system_property_getprop_appcompat) { #endif // __BIONIC__ } +TEST(properties, __system_property_add_extended_override) { +#if defined(__BIONIC__) + SystemPropertiesTest system_properties; + ASSERT_TRUE(system_properties.valid()); + + ASSERT_EQ(0, system_properties.Add("gsm.sim.operator.numeric", 24, "123456", 6)); + ASSERT_EQ(0, system_properties.Add("ro.other.prop", 13, "value", 5)); + + char propvalue[PROP_VALUE_MAX]; + + // before enabling: denied prop is readable + ASSERT_EQ(6, system_properties.Get("gsm.sim.operator.numeric", propvalue)); + ASSERT_STREQ(propvalue, "123456"); + ASSERT_NE(nullptr, system_properties.Find("gsm.sim.operator.numeric")); + + system_properties.EnableExtendedOverrides(); + + // after enabling: denied prop hidden from Find and Get + ASSERT_EQ(nullptr, system_properties.Find("gsm.sim.operator.numeric")); + ASSERT_EQ(0, system_properties.Get("gsm.sim.operator.numeric", propvalue)); + ASSERT_STREQ(propvalue, ""); + + // non-denied prop unaffected + ASSERT_EQ(5, system_properties.Get("ro.other.prop", propvalue)); + ASSERT_STREQ(propvalue, "value"); +#else // __BIONIC__ + GTEST_SKIP() << "bionic-only test"; +#endif // __BIONIC__ +} + +TEST(properties, __system_property_update_extended_override_denylist) { +#if defined(__BIONIC__) + SystemPropertiesTest system_properties; + ASSERT_TRUE(system_properties.valid()); + + ASSERT_EQ(0, system_properties.Add("gsm.sim.operator.numeric", 24, "123456", 6)); + ASSERT_EQ(0, system_properties.Add("gsm.version.baseband", 20, "v1", 2)); + + // capture prop_info* before enabling override (Find returns nullptr afterward for denied props) + const prop_info* denied_pi = system_properties.Find("gsm.sim.operator.numeric"); + ASSERT_NE(nullptr, denied_pi); + const prop_info* allowed_pi = system_properties.Find("gsm.version.baseband"); + ASSERT_NE(nullptr, allowed_pi); + + system_properties.EnableExtendedOverrides(); + + // update underlying values (as init would) + system_properties.Update(const_cast(denied_pi), "654321", 6); + system_properties.Update(const_cast(allowed_pi), "v2", 2); + + char propvalue[PROP_VALUE_MAX]; + + // denied prop still hidden after update + ASSERT_EQ(nullptr, system_properties.Find("gsm.sim.operator.numeric")); + ASSERT_EQ(0, system_properties.Get("gsm.sim.operator.numeric", propvalue)); + ASSERT_STREQ(propvalue, ""); + + // allowed prop updated normally + ASSERT_EQ(2, system_properties.Get("gsm.version.baseband", propvalue)); + ASSERT_STREQ(propvalue, "v2"); +#else // __BIONIC__ + GTEST_SKIP() << "bionic-only test"; +#endif // __BIONIC__ +} + TEST(properties, __system_property_update) { #if defined(__BIONIC__) SystemPropertiesTest system_properties;