Merge "Reassign TLS slot 2 to TLS_SLOT_APP."
diff --git a/libc/Android.bp b/libc/Android.bp
index 36a0507..6f2e347 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -2212,6 +2212,91 @@
},
}
+python_binary_host {
+ name: "genfunctosyscallnrs",
+ main: "tools/genfunctosyscallnrs.py",
+
+ srcs: [
+ "tools/genseccomp.py",
+ "tools/genfunctosyscallnrs.py",
+ "tools/gensyscalls.py",
+ ],
+
+ data: [
+ "kernel/uapi/**/*.h",
+ ],
+
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
+
+cc_genrule {
+ name: "func_to_syscall_nrs",
+ recovery_available: true,
+ cmd: "$(location genfunctosyscallnrs) --out-dir=$(genDir) $(in)",
+
+ tools: [ "genfunctosyscallnrs" ],
+
+ srcs: [
+ "SYSCALLS.TXT",
+ ":libseccomp_gen_syscall_nrs_arm",
+ ":libseccomp_gen_syscall_nrs_arm64",
+ ":libseccomp_gen_syscall_nrs_mips",
+ ":libseccomp_gen_syscall_nrs_mips64",
+ ":libseccomp_gen_syscall_nrs_x86",
+ ":libseccomp_gen_syscall_nrs_x86_64",
+ ],
+
+ out: [
+ "func_to_syscall_nrs.h",
+ ],
+}
+
+// SECCOMP_BLACKLIST_APP_ZYGOTE.TXT = SECCOMP_BLACKLIST_APP.txt - setresgid*
+genrule {
+ name: "generate_app_zygote_blacklist",
+ out: ["SECCOMP_BLACKLIST_APP_ZYGOTE.TXT"],
+ srcs: ["SECCOMP_BLACKLIST_APP.TXT"],
+ cmd: "grep -v '^int[ \t]*setresgid' $(in) > $(out)",
+}
+
+cc_genrule {
+ name: "libseccomp_policy_app_zygote_sources",
+ recovery_available: true,
+ cmd: "$(location genseccomp) --out-dir=$(genDir) --name-modifier=app_zygote $(in)",
+
+ tools: [ "genseccomp" ],
+
+ srcs: [
+ "SYSCALLS.TXT",
+ "SECCOMP_WHITELIST_COMMON.TXT",
+ "SECCOMP_WHITELIST_APP.TXT",
+ "SECCOMP_BLACKLIST_COMMON.TXT",
+ ":generate_app_zygote_blacklist",
+ ":libseccomp_gen_syscall_nrs_arm",
+ ":libseccomp_gen_syscall_nrs_arm64",
+ ":libseccomp_gen_syscall_nrs_mips",
+ ":libseccomp_gen_syscall_nrs_mips64",
+ ":libseccomp_gen_syscall_nrs_x86",
+ ":libseccomp_gen_syscall_nrs_x86_64",
+ ],
+
+ out: [
+ "arm64_app_zygote_policy.cpp",
+ "arm_app_zygote_policy.cpp",
+ "mips64_app_zygote_policy.cpp",
+ "mips_app_zygote_policy.cpp",
+ "x86_64_app_zygote_policy.cpp",
+ "x86_app_zygote_policy.cpp",
+ ],
+}
+
cc_genrule {
name: "libseccomp_policy_app_sources",
recovery_available: true,
@@ -2308,8 +2393,10 @@
cc_library {
name: "libseccomp_policy",
recovery_available: true,
+ generated_headers: ["func_to_syscall_nrs"],
generated_sources: [
"libseccomp_policy_app_sources",
+ "libseccomp_policy_app_zygote_sources",
"libseccomp_policy_global_sources",
"libseccomp_policy_system_sources",
],
diff --git a/libc/SECCOMP_BLACKLIST_APP.TXT b/libc/SECCOMP_BLACKLIST_APP.TXT
index 66e24cb..b7a05c4 100644
--- a/libc/SECCOMP_BLACKLIST_APP.TXT
+++ b/libc/SECCOMP_BLACKLIST_APP.TXT
@@ -29,17 +29,21 @@
# This file is processed by a python script named genseccomp.py.
# Note: Some privileged syscalls are still needed in app process after fork before uid change,
-# including capset and setresuid.
+# including capset and setresuid. This is because the seccomp filter must be installed while
+# the process still has CAP_SYS_ADMIN; changing the uid would remove that capability.
# syscalls to modify IDs
int setgid:setgid32(gid_t) arm,x86
int setgid:setgid(gid_t) arm64,mips,mips64,x86_64
int setuid:setuid32(uid_t) arm,x86
int setuid:setuid(uid_t) arm64,mips,mips64,x86_64
+int setregid:setregid32(gid_t, gid_t) arm,x86
+int setregid:setregid(gid_t, gid_t) arm64,mips,mips64,x86_64
int setreuid:setreuid32(uid_t, uid_t) arm,x86
int setreuid:setreuid(uid_t, uid_t) arm64,mips,mips64,x86_64
int setresgid:setresgid32(gid_t, gid_t, gid_t) arm,x86
int setresgid:setresgid(gid_t, gid_t, gid_t) arm64,mips,mips64,x86_64
+# setresuid is explicitly allowed, see above.
int setfsgid(gid_t) all
int setfsuid(uid_t) all
int setgroups:setgroups32(int, const gid_t*) arm,x86
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 4cc5df9..d1aa1ea 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -47,6 +47,7 @@
#include <private/bionic_defs.h>
#include <private/bionic_config.h>
#include <private/bionic_globals.h>
+#include <private/bionic_malloc.h>
#include <private/bionic_malloc_dispatch.h>
#if __has_feature(hwaddress_sanitizer)
@@ -304,6 +305,13 @@
async_safe_format_log(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
// =============================================================================
+// In a Zygote child process, this is set to true if profiling of this process
+// is allowed. Note that this set at a later time than the above
+// gMallocLeakZygoteChild. The latter is set during the fork (while still in
+// zygote's SELinux domain). While this bit is set after the child is
+// specialized (and has transferred SELinux domains if applicable).
+static _Atomic bool gMallocZygoteChildProfileable = false;
+
// =============================================================================
// Exported for use by ddms.
// =============================================================================
@@ -641,39 +649,36 @@
}
}
-// The logic for triggering heapprofd below is as following.
-// 1. HEAPPROFD_SIGNAL is received by the process.
-// 2. If neither InitHeapprofd nor InitHeapprofdHook are currently installed
-// (g_heapprofd_init_hook_installed is false), InitHeapprofdHook is
-// installed and g_heapprofd_init_in_progress is set to true.
-//
-// On the next subsequent malloc, InitHeapprofdHook is called and
-// 3a. If the signal is currently being handled (g_heapprofd_init_in_progress
-// is true), no action is taken.
-// 3b. Otherwise, The signal handler (InstallInitHeapprofdHook) installs a
-// temporary malloc hook (InitHeapprofdHook).
-// 4. When this hook gets run the first time, it uninstalls itself and spawns
-// a thread running InitHeapprofd that loads heapprofd.so and installs the
-// hooks within.
+// The logic for triggering heapprofd (at runtime) is as follows:
+// 1. HEAPPROFD_SIGNAL is received by the process, entering the
+// MaybeInstallInitHeapprofdHook signal handler.
+// 2. If the initialization is not already in flight
+// (g_heapprofd_init_in_progress is false), the malloc hook is set to
+// point at InitHeapprofdHook, and g_heapprofd_init_in_progress is set to
+// true.
+// 3. The next malloc call enters InitHeapprofdHook, which removes the malloc
+// hook, and spawns a detached pthread to run the InitHeapprofd task.
+// (g_heapprofd_init_hook_installed atomic is used to perform this once.)
+// 4. InitHeapprofd, on a dedicated pthread, loads the heapprofd client library,
+// installs the full set of heapprofd hooks, and invokes the client's
+// initializer. The dedicated pthread then terminates.
// 5. g_heapprofd_init_in_progress and g_heapprofd_init_hook_installed are
-// reset to false so heapprofd can be reinitialized. Reinitialization
-// means that a new profiling session is started and any still active is
+// reset to false such that heapprofd can be reinitialized. Reinitialization
+// means that a new profiling session is started, and any still active is
// torn down.
//
-// This roundabout way is needed because we are running non AS-safe code, so
-// we cannot run it directly in the signal handler. The other approach of
-// running a standby thread and signalling through write(2) and read(2) would
-// significantly increase the number of active threads in the system.
+// The incremental hooking and a dedicated task thread are used since we cannot
+// do heavy work within a signal handler, or when blocking a malloc invocation.
static _Atomic bool g_heapprofd_init_in_progress = false;
static _Atomic bool g_heapprofd_init_hook_installed = false;
-extern "C" void InstallInitHeapprofdHook(int);
+extern "C" void MaybeInstallInitHeapprofdHook(int);
// Initializes memory allocation framework once per process.
static void malloc_init_impl(libc_globals* globals) {
struct sigaction action = {};
- action.sa_handler = InstallInitHeapprofdHook;
+ action.sa_handler = MaybeInstallInitHeapprofdHook;
sigaction(HEAPPROFD_SIGNAL, &action, nullptr);
const char* prefix;
@@ -735,7 +740,13 @@
return Malloc(malloc)(bytes);
}
-extern "C" void InstallInitHeapprofdHook(int) {
+extern "C" void MaybeInstallInitHeapprofdHook(int) {
+ // Zygote child processes must be marked profileable.
+ if (gMallocLeakZygoteChild &&
+ !atomic_load_explicit_const(&gMallocZygoteChildProfileable, memory_order_acquire)) {
+ return;
+ }
+
if (!atomic_exchange(&g_heapprofd_init_in_progress, true)) {
__libc_globals.mutate([](libc_globals* globals) {
atomic_store(&globals->malloc_dispatch.malloc, InitHeapprofdHook);
@@ -746,6 +757,46 @@
#endif // !LIBC_STATIC
// =============================================================================
+// Platform-internal mallopt variant.
+// =============================================================================
+
+#if !defined(LIBC_STATIC)
+// Marks this process as a profileable zygote child.
+bool HandleInitZygoteChildProfiling() {
+ atomic_store_explicit(&gMallocZygoteChildProfileable, true,
+ memory_order_release);
+
+ // Conditionally start "from startup" profiling.
+ if (CheckLoadHeapprofd()) {
+ // Directly call the signal handler (will correctly guard against
+ // concurrent signal delivery).
+ MaybeInstallInitHeapprofdHook(HEAPPROFD_SIGNAL);
+ }
+ return true;
+}
+
+#else
+
+bool HandleInitZygoteChildProfiling() {
+ return true;
+}
+
+#endif // !defined(LIBC_STATIC)
+
+bool android_mallopt(int opcode, void* arg, size_t arg_size) {
+ if (opcode == M_INIT_ZYGOTE_CHILD_PROFILING) {
+ if (arg != nullptr || arg_size != 0) {
+ errno = EINVAL;
+ return false;
+ }
+ return HandleInitZygoteChildProfiling();
+ }
+
+ errno = ENOTSUP;
+ return false;
+}
+
+// =============================================================================
// Exported for use by libmemunreachable.
// =============================================================================
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index c27f884..8d67b9e 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1480,6 +1480,7 @@
android_getaddrinfofornet; # apex
# Used by libandroid_runtime
+ android_mallopt; # apex
gMallocLeakZygoteChild; # apex
} LIBC_P;
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index 926b265..dd20b5c 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -132,6 +132,9 @@
{
"verify_pointers", {TRACK_ALLOCS, &Config::VerifyValueEmpty},
},
+ {
+ "abort_on_error", {ABORT_ON_ERROR, &Config::VerifyValueEmpty},
+ },
};
bool Config::ParseValue(const std::string& option, const std::string& value, size_t min_value,
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index 86d1ee4..011dc77 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -44,6 +44,7 @@
constexpr uint64_t LEAK_TRACK = 0x100;
constexpr uint64_t RECORD_ALLOCS = 0x200;
constexpr uint64_t BACKTRACE_FULL = 0x400;
+constexpr uint64_t ABORT_ON_ERROR = 0x800;
// In order to guarantee posix compliance, set the minimum alignment
// to 8 bytes for 32 bit systems and 16 bytes for 64 bit systems.
diff --git a/libc/malloc_debug/GuardData.cpp b/libc/malloc_debug/GuardData.cpp
index debc14e..c307dc9 100644
--- a/libc/malloc_debug/GuardData.cpp
+++ b/libc/malloc_debug/GuardData.cpp
@@ -64,6 +64,9 @@
error_log("Backtrace at time of failure:");
BacktraceAndLog();
error_log(LOG_DIVIDER);
+ if (g_debug->config().options() & ABORT_ON_ERROR) {
+ abort();
+ }
}
FrontGuardData::FrontGuardData(DebugData* debug_data, const Config& config, size_t* offset)
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index b0e2fc8..638061b 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -206,7 +206,7 @@
std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
auto entry = pointers_.find(pointer);
if (entry == pointers_.end()) {
- // Error.
+ // Attempt to remove unknown pointer.
error_log("No tracked pointer found for 0x%" PRIxPTR, pointer);
return;
}
@@ -283,6 +283,9 @@
}
error_log(LOG_DIVIDER);
+ if (g_debug->config().options() & ABORT_ON_ERROR) {
+ abort();
+ }
}
void PointerData::VerifyFreedPointer(const FreePointerInfoType& info) {
@@ -295,6 +298,9 @@
error_log("+++ ALLOCATION 0x%" PRIxPTR " HAS CORRUPTED HEADER TAG 0x%x AFTER FREE",
info.pointer, header->tag);
error_log(LOG_DIVIDER);
+ if (g_debug->config().options() & ABORT_ON_ERROR) {
+ abort();
+ }
// Stop processing here, it is impossible to tell how the header
// may have been damaged.
diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md
index a8289b3..93b9b1e 100644
--- a/libc/malloc_debug/README.md
+++ b/libc/malloc_debug/README.md
@@ -394,6 +394,13 @@
**NOTE**: This option is not available until the P release of Android.
+### abort\_on\_error
+When malloc debug detects an error, abort after sending the error
+log message.
+
+**NOTE**: If leak\_track is enabled, no abort occurs if leaks have been
+detected when the process is exiting.
+
Additional Errors
-----------------
There are a few other error messages that might appear in the log.
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 9075a9c..2e6afff 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -154,6 +154,9 @@
error_log("Backtrace at time of failure:");
BacktraceAndLog();
error_log(LOG_DIVIDER);
+ if (g_debug->config().options() & ABORT_ON_ERROR) {
+ abort();
+ }
}
static bool VerifyPointer(const void* pointer, const char* function_name) {
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index a083b4f..fb54ee5 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -725,3 +725,21 @@
"value must be <= 50000000: 100000000\n");
ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
}
+
+TEST_F(MallocDebugConfigTest, abort_on_error) {
+ ASSERT_TRUE(InitConfig("abort_on_error")) << getFakeLogPrint();
+ ASSERT_EQ(ABORT_ON_ERROR, config->options());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, trigger_abort_fail) {
+ ASSERT_FALSE(InitConfig("abort_on_error=200")) << getFakeLogPrint();
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg(
+ "6 malloc_debug malloc_testing: value set for option 'abort_on_error' "
+ "which does not take a value\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 2d6346f..44f9795 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -2380,3 +2380,59 @@
expected_log += DIVIDER;
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
}
+
+TEST_F(MallocDebugTest, abort_on_error_log_error) {
+ Init("abort_on_error verify_pointers");
+
+ void* pointer = debug_malloc(10);
+ memset(pointer, 0, 10);
+ debug_free(pointer);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+
+ EXPECT_DEATH(debug_free(pointer), "");
+}
+
+TEST_F(MallocDebugTest, abort_on_error_guard_corrupted) {
+ Init("abort_on_error front_guard=32");
+
+ uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+ ASSERT_TRUE(pointer != nullptr);
+ pointer[-16] = 0x00;
+ EXPECT_DEATH(debug_free(pointer), "");
+ pointer[-16] = 0xaa;
+ debug_free(pointer);
+}
+
+TEST_F(MallocDebugTest, abort_on_error_use_after_free) {
+ Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0");
+
+ uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+ ASSERT_TRUE(pointer != nullptr);
+ memset(pointer, 0, 100);
+ debug_free(pointer);
+
+ pointer[56] = 0x91;
+
+ EXPECT_DEATH(debug_finalize(), "");
+
+ pointer[56] = 0xef;
+}
+
+TEST_F(MallocDebugTest, abort_on_error_header_tag_corrupted) {
+ Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0 rear_guard");
+
+ uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+ ASSERT_TRUE(pointer != nullptr);
+ memset(pointer, 0, 100);
+ debug_free(pointer);
+
+ uint8_t tag_value = pointer[-get_tag_offset()];
+ pointer[-get_tag_offset()] = 0x00;
+
+ EXPECT_DEATH(debug_finalize(), "");
+
+ pointer[-get_tag_offset()] = tag_value;
+}
+
diff --git a/libc/private/bionic_malloc.h b/libc/private/bionic_malloc.h
new file mode 100644
index 0000000..a9fa22d
--- /dev/null
+++ b/libc/private/bionic_malloc.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+// Opcodes for android_mallopt.
+
+// Marks the calling process as a profileable zygote child, possibly
+// initializing profiling infrastructure.
+enum {
+ M_INIT_ZYGOTE_CHILD_PROFILING = 1,
+#define M_INIT_ZYGOTE_CHILD_PROFILING M_INIT_ZYGOTE_CHILD_PROFILING
+};
+
+// Manipulates bionic-specific handling of memory allocation APIs such as
+// malloc. Only for use by the Android platform itself.
+//
+// On success, returns true. On failure, returns false and sets errno.
+extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size);
diff --git a/libc/seccomp/include/seccomp_policy.h b/libc/seccomp/include/seccomp_policy.h
index 49280f4..fd0fb60 100644
--- a/libc/seccomp/include/seccomp_policy.h
+++ b/libc/seccomp/include/seccomp_policy.h
@@ -17,8 +17,14 @@
#pragma once
#include <stddef.h>
+#include <stdint.h>
#include <linux/filter.h>
bool set_app_seccomp_filter();
+bool set_app_zygote_seccomp_filter();
bool set_system_seccomp_filter();
bool set_global_seccomp_filter();
+
+// Installs a filter that limits setresuid/setresgid to a range of
+// [uid_gid_min..uid_gid_max] (for the real-, effective- and super-ids).
+bool install_setuidgid_seccomp_filter(uint32_t uid_gid_min, uint32_t uid_gid_max);
diff --git a/libc/seccomp/seccomp_bpfs.h b/libc/seccomp/seccomp_bpfs.h
index 797dfc5..d9e8047 100644
--- a/libc/seccomp/seccomp_bpfs.h
+++ b/libc/seccomp/seccomp_bpfs.h
@@ -21,6 +21,8 @@
extern const struct sock_filter arm_app_filter[];
extern const size_t arm_app_filter_size;
+extern const struct sock_filter arm_app_zygote_filter[];
+extern const size_t arm_app_zygote_filter_size;
extern const struct sock_filter arm_system_filter[];
extern const size_t arm_system_filter_size;
extern const struct sock_filter arm_global_filter[];
@@ -28,6 +30,8 @@
extern const struct sock_filter arm64_app_filter[];
extern const size_t arm64_app_filter_size;
+extern const struct sock_filter arm64_app_zygote_filter[];
+extern const size_t arm64_app_zygote_filter_size;
extern const struct sock_filter arm64_system_filter[];
extern const size_t arm64_system_filter_size;
extern const struct sock_filter arm64_global_filter[];
@@ -35,6 +39,8 @@
extern const struct sock_filter x86_app_filter[];
extern const size_t x86_app_filter_size;
+extern const struct sock_filter x86_app_zygote_filter[];
+extern const size_t x86_app_zygote_filter_size;
extern const struct sock_filter x86_system_filter[];
extern const size_t x86_system_filter_size;
extern const struct sock_filter x86_global_filter[];
@@ -42,6 +48,8 @@
extern const struct sock_filter x86_64_app_filter[];
extern const size_t x86_64_app_filter_size;
+extern const struct sock_filter x86_64_app_zygote_filter[];
+extern const size_t x86_64_app_zygote_filter_size;
extern const struct sock_filter x86_64_system_filter[];
extern const size_t x86_64_system_filter_size;
extern const struct sock_filter x86_64_global_filter[];
@@ -49,6 +57,8 @@
extern const struct sock_filter mips_app_filter[];
extern const size_t mips_app_filter_size;
+extern const struct sock_filter mips_app_zygote_filter[];
+extern const size_t mips_app_zygote_filter_size;
extern const struct sock_filter mips_system_filter[];
extern const size_t mips_system_filter_size;
extern const struct sock_filter mips_global_filter[];
@@ -56,6 +66,8 @@
extern const struct sock_filter mips64_app_filter[];
extern const size_t mips64_app_filter_size;
+extern const struct sock_filter mips64_app_zygote_filter[];
+extern const size_t mips64_app_zygote_filter_size;
extern const struct sock_filter mips64_system_filter[];
extern const size_t mips64_system_filter_size;
extern const struct sock_filter mips64_global_filter[];
diff --git a/libc/seccomp/seccomp_policy.cpp b/libc/seccomp/seccomp_policy.cpp
index 3d617be..222a2c8 100644
--- a/libc/seccomp/seccomp_policy.cpp
+++ b/libc/seccomp/seccomp_policy.cpp
@@ -20,78 +20,111 @@
#include <linux/audit.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
+#include <sys/syscall.h>
#include <vector>
#include <android-base/logging.h>
+#include "func_to_syscall_nrs.h"
#include "seccomp_bpfs.h"
-
#if defined __arm__ || defined __aarch64__
#define DUAL_ARCH
#define PRIMARY_ARCH AUDIT_ARCH_AARCH64
static const struct sock_filter* primary_app_filter = arm64_app_filter;
static const size_t primary_app_filter_size = arm64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = arm64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = arm64_app_zygote_filter_size;
static const struct sock_filter* primary_system_filter = arm64_system_filter;
static const size_t primary_system_filter_size = arm64_system_filter_size;
static const struct sock_filter* primary_global_filter = arm64_global_filter;
static const size_t primary_global_filter_size = arm64_global_filter_size;
+
+static const long primary_setresgid = __arm64_setresgid;
+static const long primary_setresuid = __arm64_setresuid;
#define SECONDARY_ARCH AUDIT_ARCH_ARM
static const struct sock_filter* secondary_app_filter = arm_app_filter;
static const size_t secondary_app_filter_size = arm_app_filter_size;
+static const struct sock_filter* secondary_app_zygote_filter = arm_app_zygote_filter;
+static const size_t secondary_app_zygote_filter_size = arm_app_zygote_filter_size;
static const struct sock_filter* secondary_system_filter = arm_system_filter;
static const size_t secondary_system_filter_size = arm_system_filter_size;
static const struct sock_filter* secondary_global_filter = arm_global_filter;
static const size_t secondary_global_filter_size = arm_global_filter_size;
+static const long secondary_setresgid = __arm_setresgid;
+static const long secondary_setresuid = __arm_setresuid;
#elif defined __i386__ || defined __x86_64__
#define DUAL_ARCH
#define PRIMARY_ARCH AUDIT_ARCH_X86_64
static const struct sock_filter* primary_app_filter = x86_64_app_filter;
static const size_t primary_app_filter_size = x86_64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = x86_64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = x86_64_app_zygote_filter_size;
static const struct sock_filter* primary_system_filter = x86_64_system_filter;
static const size_t primary_system_filter_size = x86_64_system_filter_size;
static const struct sock_filter* primary_global_filter = x86_64_global_filter;
static const size_t primary_global_filter_size = x86_64_global_filter_size;
+
+static const long primary_setresgid = __x86_64_setresgid;
+static const long primary_setresuid = __x86_64_setresuid;
#define SECONDARY_ARCH AUDIT_ARCH_I386
static const struct sock_filter* secondary_app_filter = x86_app_filter;
static const size_t secondary_app_filter_size = x86_app_filter_size;
+static const struct sock_filter* secondary_app_zygote_filter = x86_app_zygote_filter;
+static const size_t secondary_app_zygote_filter_size = x86_app_zygote_filter_size;
static const struct sock_filter* secondary_system_filter = x86_system_filter;
static const size_t secondary_system_filter_size = x86_system_filter_size;
static const struct sock_filter* secondary_global_filter = x86_global_filter;
static const size_t secondary_global_filter_size = x86_global_filter_size;
+static const long secondary_setresgid = __x86_setresgid;
+static const long secondary_setresuid = __x86_setresuid;
#elif defined __mips__ || defined __mips64__
#define DUAL_ARCH
#define PRIMARY_ARCH AUDIT_ARCH_MIPSEL64
static const struct sock_filter* primary_app_filter = mips64_app_filter;
static const size_t primary_app_filter_size = mips64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = mips64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = mips64_app_zygote_filter_size;
static const struct sock_filter* primary_system_filter = mips64_system_filter;
static const size_t primary_system_filter_size = mips64_system_filter_size;
static const struct sock_filter* primary_global_filter = mips64_global_filter;
static const size_t primary_global_filter_size = mips64_global_filter_size;
+
+static const long primary_setresgid = __mips64_setresgid;
+static const long primary_setresuid = __mips64_setresuid;
#define SECONDARY_ARCH AUDIT_ARCH_MIPSEL
static const struct sock_filter* secondary_app_filter = mips_app_filter;
static const size_t secondary_app_filter_size = mips_app_filter_size;
+static const struct sock_filter* secondary_app_zygote_filter = mips_app_zygote_filter;
+static const size_t secondary_app_zygote_filter_size = mips_app_zygote_filter_size;
static const struct sock_filter* secondary_system_filter = mips_system_filter;
static const size_t secondary_system_filter_size = mips_system_filter_size;
static const struct sock_filter* secondary_global_filter = mips_global_filter;
static const size_t secondary_global_filter_size = mips_global_filter_size;
+static const long secondary_setresgid = __mips_setresgid;
+static const long secondary_setresuid = __mips_setresuid;
#else
#error No architecture was defined!
#endif
#define syscall_nr (offsetof(struct seccomp_data, nr))
+#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))
#define arch_nr (offsetof(struct seccomp_data, arch))
typedef std::vector<sock_filter> filter;
+inline void Allow(filter& f) {
+ f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
+}
+
inline void Disallow(filter& f) {
f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP));
}
@@ -128,6 +161,49 @@
}
#endif
+static void ValidateSyscallArgInRange(filter& f, __u32 arg_num, __u32 range_min, __u32 range_max) {
+ const __u32 syscall_arg = syscall_arg(arg_num);
+
+ if (range_max == UINT32_MAX) {
+ LOG(FATAL) << "range_max exceeds maximum argument range.";
+ return;
+ }
+
+ f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg));
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, range_min, 0, 1));
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, range_max + 1, 0, 1));
+ Disallow(f);
+}
+
+// This filter is meant to be installed in addition to a regular whitelist filter.
+// Therefore, it's default action has to be Allow, except when the evaluated
+// system call matches setresuid/setresgid and the arguments don't fall within the
+// passed in range.
+//
+// The regular whitelist only allows setresuid/setresgid for UID/GID changes, so
+// that's the only system call we need to check here. A CTS test ensures the other
+// calls will remain blocked.
+static void ValidateSetUidGid(filter& f, uint32_t uid_gid_min, uint32_t uid_gid_max, bool primary) {
+ // Check setresuid(ruid, euid, sguid) fall within range
+ ExamineSyscall(f);
+ __u32 setresuid_nr = primary ? primary_setresuid : secondary_setresuid;
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresuid_nr, 0, 12));
+ for (int arg = 0; arg < 3; arg++) {
+ ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max);
+ }
+
+ // Check setresgid(rgid, egid, sgid) fall within range
+ ExamineSyscall(f);
+ __u32 setresgid_nr = primary ? primary_setresgid : secondary_setresgid;
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresgid_nr, 0, 12));
+ for (int arg = 0; arg < 3; arg++) {
+ ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max);
+ }
+
+ // Default is to allow; other filters may still reject this call.
+ Allow(f);
+}
+
static bool install_filter(filter const& f) {
struct sock_fprog prog = {
static_cast<unsigned short>(f.size()),
@@ -141,8 +217,33 @@
return true;
}
+bool _install_setuidgid_filter(uint32_t uid_gid_min, uint32_t uid_gid_max) {
+ filter f;
+#ifdef DUAL_ARCH
+ // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
+ // jump that must be changed to point to the start of the 32-bit policy
+ // 32 bit syscalls will not hit the policy between here and the call to SetJump
+ auto offset_to_secondary_filter = ValidateArchitectureAndJumpIfNeeded(f);
+#else
+ ValidateArchitecture(f);
+#endif
+
+ ValidateSetUidGid(f, uid_gid_min, uid_gid_max, true /* primary */);
+
+#ifdef DUAL_ARCH
+ if (!SetValidateArchitectureJumpTarget(offset_to_secondary_filter, f)) {
+ return false;
+ }
+
+ ValidateSetUidGid(f, uid_gid_min, uid_gid_max, false /* primary */);
+#endif
+
+ return install_filter(f);
+}
+
enum FilterType {
APP,
+ APP_ZYGOTE,
SYSTEM,
GLOBAL
};
@@ -159,6 +260,12 @@
s = secondary_app_filter;
s_size = secondary_app_filter_size;
break;
+ case APP_ZYGOTE:
+ p = primary_app_zygote_filter;
+ p_size = primary_app_zygote_filter_size;
+ s = secondary_app_zygote_filter;
+ s_size = secondary_app_zygote_filter_size;
+ break;
case SYSTEM:
p = primary_system_filter;
p_size = primary_system_filter_size;
@@ -210,6 +317,10 @@
return _set_seccomp_filter(FilterType::APP);
}
+bool set_app_zygote_seccomp_filter() {
+ return _set_seccomp_filter(FilterType::APP_ZYGOTE);
+}
+
bool set_system_seccomp_filter() {
return _set_seccomp_filter(FilterType::SYSTEM);
}
@@ -217,3 +328,7 @@
bool set_global_seccomp_filter() {
return _set_seccomp_filter(FilterType::GLOBAL);
}
+
+bool install_setuidgid_seccomp_filter(uint32_t uid_gid_min, uint32_t uid_gid_max) {
+ return _install_setuidgid_filter(uid_gid_min, uid_gid_max);
+}
diff --git a/libc/tools/genfunctosyscallnrs.py b/libc/tools/genfunctosyscallnrs.py
new file mode 100755
index 0000000..6a456f2
--- /dev/null
+++ b/libc/tools/genfunctosyscallnrs.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+import argparse
+import collections
+import logging
+import os
+import re
+import subprocess
+import textwrap
+
+from gensyscalls import SysCallsTxtParser
+from genseccomp import parse_syscall_NRs
+
+def load_syscall_names_from_file(file_path, architecture):
+ parser = SysCallsTxtParser()
+ parser.parse_open_file(open(file_path))
+ arch_map = {}
+ for syscall in parser.syscalls:
+ if syscall.get(architecture):
+ arch_map[syscall["func"]] = syscall["name"];
+
+ return arch_map
+
+def gen_syscall_nrs(out_file, base_syscall_file, syscall_NRs):
+ for arch in ('arm', 'arm64', 'mips', 'mips64', 'x86', 'x86_64'):
+ base_names = load_syscall_names_from_file(base_syscall_file, arch)
+
+ for func,syscall in base_names.iteritems():
+ out_file.write("#define __" + arch + "_" + func + " " + str(syscall_NRs[arch][syscall]) + ";\n")
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Generates a mapping of bionic functions to system call numbers per architecture.")
+ parser.add_argument("--verbose", "-v", help="Enables verbose logging.")
+ parser.add_argument("--out-dir",
+ help="The output directory for the output files")
+ parser.add_argument("base_file", metavar="base-file", type=str,
+ help="The path of the base syscall list (SYSCALLS.TXT).")
+ parser.add_argument("files", metavar="FILE", type=str, nargs="+",
+ help=("A syscall name-number mapping file for an architecture.\n"))
+ args = parser.parse_args()
+
+ if args.verbose:
+ logging.basicConfig(level=logging.DEBUG)
+ else:
+ logging.basicConfig(level=logging.INFO)
+
+ syscall_files = []
+ syscall_NRs = {}
+ for filename in args.files:
+ m = re.search(r"libseccomp_gen_syscall_nrs_([^/]+)", filename)
+ syscall_NRs[m.group(1)] = parse_syscall_NRs(filename)
+
+ output_path = os.path.join(args.out_dir, "func_to_syscall_nrs.h")
+ with open(output_path, "w") as output_file:
+ gen_syscall_nrs(out_file=output_file,
+ syscall_NRs=syscall_NRs, base_syscall_file=args.base_file)
+
+if __name__ == "__main__":
+ main()
diff --git a/linker/Android.bp b/linker/Android.bp
index e103ade..4991935 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -285,7 +285,11 @@
symlinks: ["linker_asan"],
recovery_available: true,
multilib: {
+ lib32: {
+ cflags: ["-DLIB_PATH=\"lib\""],
+ },
lib64: {
+ cflags: ["-DLIB_PATH=\"lib64\""],
suffix: "64",
},
},
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index dcc6bf3..89119aa 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -82,8 +82,15 @@
split_path(path, ":", &runpaths);
std::string origin = dirname(get_realpath());
- // FIXME: add $LIB and $PLATFORM.
- std::vector<std::pair<std::string, std::string>> params = {{"ORIGIN", origin}};
+ // FIXME: add $PLATFORM.
+ std::vector<std::pair<std::string, std::string>> params = {
+ {"ORIGIN", origin},
+#if defined(LIB_PATH)
+ {"LIB", LIB_PATH},
+#else
+#error "LIB_PATH not defined"
+#endif
+ };
for (auto&& s : runpaths) {
format_string(&s, params);
}
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 4a01278..2506691 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -25,6 +25,7 @@
#include <tinyxml2.h>
#include "private/bionic_config.h"
+#include "private/bionic_malloc.h"
#include "utils.h"
#if defined(__BIONIC__)
@@ -601,3 +602,32 @@
GTEST_LOG_(INFO) << "Host glibc does not pass this test, skipping.\n";
#endif
}
+
+TEST(android_mallopt, error_on_unexpected_option) {
+#if defined(__BIONIC__)
+ const int unrecognized_option = -1;
+ errno = 0;
+ EXPECT_EQ(false, android_mallopt(unrecognized_option, nullptr, 0));
+ EXPECT_EQ(ENOTSUP, errno);
+#else
+ GTEST_LOG_(INFO) << "This tests a bionic implementation detail.\n";
+#endif
+}
+
+TEST(android_mallopt, init_zygote_child_profiling) {
+#if defined(__BIONIC__)
+ // Successful call.
+ errno = 0;
+ EXPECT_EQ(true, android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0));
+ EXPECT_EQ(0, errno);
+
+ // Unexpected arguments rejected.
+ errno = 0;
+ char unexpected = 0;
+ EXPECT_EQ(false, android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, &unexpected, 1));
+ EXPECT_EQ(EINVAL, errno);
+#else
+ GTEST_LOG_(INFO) << "This tests a bionic implementation detail.\n";
+#endif
+}
+