Merge "Fix google-explicit-constructor warning."
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 0fdf90d..c193861 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2365,12 +2365,16 @@
}
bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path) {
- CHECK(public_ns_sonames != nullptr);
if (g_public_namespace_initialized) {
DL_ERR("public namespace has already been initialized.");
return false;
}
+ if (public_ns_sonames == nullptr || public_ns_sonames[0] == '\0') {
+ DL_ERR("error initializing public namespace: the list of public libraries is empty.");
+ return false;
+ }
+
std::vector<std::string> sonames = android::base::Split(public_ns_sonames, ":");
ProtectedDataGuard guard;
diff --git a/tests/Android.mk b/tests/Android.mk
index 7292d9d..8e190ea 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -104,6 +104,7 @@
sys_personality_test.cpp \
sys_prctl_test.cpp \
sys_procfs_test.cpp \
+ sys_ptrace_test.cpp \
sys_quota_test.cpp \
sys_resource_test.cpp \
sys_select_test.cpp \
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 87e5dbc..420c934 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -629,6 +629,10 @@
ASSERT_STREQ("android_init_namespaces failed: error initializing public namespace: "
"\"libnstest_public.so\" was not found in the default namespace", dlerror());
+ ASSERT_FALSE(android_init_namespaces("", nullptr));
+ ASSERT_STREQ("android_init_namespaces failed: error initializing public namespace: "
+ "the list of public libraries is empty.", dlerror());
+
const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
const std::string lib_public_path = lib_path + "/public_namespace_libs/" + g_public_lib;
diff --git a/tests/sys_ptrace_test.cpp b/tests/sys_ptrace_test.cpp
new file mode 100644
index 0000000..bd84d30
--- /dev/null
+++ b/tests/sys_ptrace_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/ptrace.h>
+
+#include <elf.h>
+#include <sched.h>
+#include <sys/prctl.h>
+#include <sys/uio.h>
+#include <sys/user.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+// Host libc does not define this.
+#ifndef TRAP_HWBKPT
+#define TRAP_HWBKPT 4
+#endif
+
+template<typename T>
+static void __attribute__((noreturn)) fork_child(unsigned cpu, T &data) {
+ // Extra precaution: make sure we go away if anything happens to our parent.
+ if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
+ perror("prctl(PR_SET_PDEATHSIG)");
+ _exit(1);
+ }
+
+ cpu_set_t cpus;
+ CPU_ZERO(&cpus);
+ CPU_SET(cpu, &cpus);
+ if (sched_setaffinity(0, sizeof cpus, &cpus) == -1) {
+ perror("sched_setaffinity");
+ _exit(2);
+ }
+ if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
+ perror("ptrace(PTRACE_TRACEME)");
+ _exit(3);
+ }
+
+ raise(SIGSTOP); // Synchronize with the tracer, let it set the watchpoint.
+
+ data = 1; // Now trigger the watchpoint.
+
+ _exit(0);
+}
+
+class ChildGuard {
+ public:
+ ChildGuard(pid_t pid) : pid(pid) {}
+
+ ~ChildGuard() {
+ kill(pid, SIGKILL);
+ int status;
+ waitpid(pid, &status, 0);
+ }
+
+ private:
+ pid_t pid;
+};
+
+static bool are_watchpoints_supported(pid_t child) {
+#if defined(__arm__)
+ long capabilities;
+ long result = ptrace(PTRACE_GETHBPREGS, child, 0, &capabilities);
+ if (result == -1) {
+ EXPECT_EQ(EIO, errno);
+ return false;
+ }
+ return ((capabilities >> 8) & 0xff) > 0;
+#elif defined(__aarch64__)
+ user_hwdebug_state dreg_state;
+ iovec iov;
+ iov.iov_base = &dreg_state;
+ iov.iov_len = sizeof(dreg_state);
+
+ long result = ptrace(PTRACE_GETREGSET, child, NT_ARM_HW_WATCH, &iov);
+ if (result == -1) {
+ EXPECT_EQ(EINVAL, errno);
+ return false;
+ }
+ return (dreg_state.dbg_info & 0xff) > 0;
+#elif defined(__i386__) || defined(__x86_64__)
+ // We assume watchpoints are always supported on x86.
+ (void) child;
+ return true;
+#else
+ // TODO: mips support.
+ (void) child;
+ return false;
+#endif
+}
+
+static void set_watchpoint(pid_t child, const void *address, size_t size) {
+#if defined(__arm__) || defined(__aarch64__)
+ const unsigned byte_mask = (1 << size) - 1;
+ const unsigned type = 2; // Write.
+ const unsigned enable = 1;
+ const unsigned control = byte_mask << 5 | type << 3 | enable;
+
+#ifdef __arm__
+ ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, -1, &address)) << strerror(errno);
+ ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, -2, &control)) << strerror(errno);
+#else // aarch64
+ user_hwdebug_state dreg_state;
+ memset(&dreg_state, 0, sizeof dreg_state);
+ dreg_state.dbg_regs[0].addr = reinterpret_cast<uintptr_t>(address);
+ dreg_state.dbg_regs[0].ctrl = control;
+
+ iovec iov;
+ iov.iov_base = &dreg_state;
+ iov.iov_len = offsetof(user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]);
+
+ ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, child, NT_ARM_HW_WATCH, &iov)) << strerror(errno);
+#endif
+#elif defined(__i386__) || defined(__x86_64__)
+ ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[0]), address)) << strerror(errno);
+ errno = 0;
+ unsigned data = ptrace(PTRACE_PEEKUSER, child, offsetof(user, u_debugreg[7]), nullptr);
+ ASSERT_EQ(0, errno);
+
+ const unsigned size_flag = (size == 8) ? 2 : size - 1;
+ const unsigned enable = 1;
+ const unsigned type = 1; // Write.
+
+ const unsigned mask = 3 << 18 | 3 << 16 | 1;
+ const unsigned value = size_flag << 18 | type << 16 | enable;
+ data &= mask;
+ data |= value;
+ ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[7]), data)) << strerror(errno);
+#else
+ (void) child;
+ (void) address;
+ (void) size;
+#endif
+}
+
+template<typename T>
+static void run_watchpoint_test_impl(unsigned cpu) {
+ alignas(8) T data = 0;
+
+ pid_t child = fork();
+ ASSERT_NE(-1, child) << strerror(errno);
+ if (child == 0) fork_child(cpu, data);
+
+ ChildGuard guard(child);
+
+ int status;
+ ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
+ ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
+ ASSERT_EQ(SIGSTOP, WSTOPSIG(status)) << "Status was: " << status;
+
+ if (!are_watchpoints_supported(child)) return;
+
+ set_watchpoint(child, &data, sizeof data);
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, child, nullptr, nullptr)) << strerror(errno);
+ ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
+ ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
+ ASSERT_EQ(SIGTRAP, WSTOPSIG(status)) << "Status was: " << status;
+
+ siginfo_t siginfo;
+ ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child, nullptr, &siginfo)) << strerror(errno);
+ ASSERT_EQ(TRAP_HWBKPT, siginfo.si_code);
+#if defined(__arm__) || defined(__aarch64__)
+ ASSERT_EQ(&data, siginfo.si_addr);
+#endif
+}
+
+static void run_watchpoint_test(unsigned cpu) {
+ run_watchpoint_test_impl<uint8_t>(cpu);
+ run_watchpoint_test_impl<uint16_t>(cpu);
+ run_watchpoint_test_impl<uint32_t>(cpu);
+#if __LP64__
+ run_watchpoint_test_impl<uint64_t>(cpu);
+#endif
+}
+
+// Test watchpoint API. The test is considered successful if our watchpoints get hit OR the
+// system reports that watchpoint support is not present. We run the test for different
+// watchpoint sizes, while pinning the process to each cpu in turn, for better coverage.
+TEST(ptrace, watchpoint_stress) {
+ cpu_set_t available_cpus;
+ ASSERT_EQ(0, sched_getaffinity(0, sizeof available_cpus, &available_cpus));
+
+ for (size_t cpu = 0; cpu < CPU_SETSIZE; ++cpu) {
+ if (!CPU_ISSET(cpu, &available_cpus)) continue;
+ run_watchpoint_test(cpu);
+ }
+}