Merge "Add a checksum to jmp_buf on mips and mips64."
diff --git a/libc/Android.bp b/libc/Android.bp
index ca8a4d5..8ec2b92 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1279,6 +1279,7 @@
"bionic/ftruncate.cpp",
"bionic/futimens.cpp",
"bionic/getcwd.cpp",
+ "bionic/getdomainname.cpp",
"bionic/gethostname.cpp",
"bionic/getpgrp.cpp",
"bionic/getpid.cpp",
diff --git a/libc/arch-x86/bionic/setjmp.S b/libc/arch-x86/bionic/setjmp.S
index 86e6e3c..efb6459 100644
--- a/libc/arch-x86/bionic/setjmp.S
+++ b/libc/arch-x86/bionic/setjmp.S
@@ -32,6 +32,21 @@
#include <private/bionic_asm.h>
+// The internal structure of a jmp_buf is totally private.
+// Current layout (changes from release to release):
+//
+// word name description
+// 0 edx registers
+// 1 ebx
+// 2 esp
+// 3 ebp
+// 4 esi
+// 5 edi
+// 6 sigmask signal mask (not used with _setjmp / _longjmp)
+// 7 sigflag/cookie setjmp cookie in top 31 bits, signal mask flag in low bit
+// 8 checksum checksum of the core registers, to give better error messages.
+// 9 reserved
+
#define _JB_EDX 0
#define _JB_EBX 1
#define _JB_ESP 2
@@ -40,6 +55,7 @@
#define _JB_EDI 5
#define _JB_SIGMASK 6
#define _JB_SIGFLAG 7
+#define _JB_CHECKSUM 8
.macro m_mangle_registers reg
xorl \reg,%edx
@@ -54,6 +70,13 @@
m_mangle_registers \reg
.endm
+.macro m_calculate_checksum dst, src
+ movl $0, \dst
+ .irp i,0,1,2,3,4,5
+ xorl (\i*4)(\src), \dst
+ .endr
+.endm
+
ENTRY(setjmp)
movl 4(%esp),%ecx
mov $1,%eax
@@ -111,13 +134,22 @@
movl %edi,(_JB_EDI * 4)(%ecx)
m_unmangle_registers %eax
+ m_calculate_checksum %eax, %ecx
+ movl %eax, (_JB_CHECKSUM * 4)(%ecx)
+
xorl %eax,%eax
ret
END(sigsetjmp)
ENTRY(siglongjmp)
- // Do we have a signal mask to restore?
movl 4(%esp),%edx
+
+ // Check the checksum before doing anything.
+ m_calculate_checksum %eax, %edx
+ xorl (_JB_CHECKSUM * 4)(%edx), %eax
+ jnz 3f
+
+ // Do we have a signal mask to restore?
movl (_JB_SIGFLAG * 4)(%edx), %eax
testl $1,%eax
jz 1f
@@ -165,6 +197,11 @@
2:
movl %ecx,0(%esp)
ret
+
+3:
+ PIC_PROLOGUE
+ pushl (_JB_SIGMASK * 4)(%edx)
+ call PIC_PLT(__bionic_setjmp_checksum_mismatch)
END(siglongjmp)
ALIAS_SYMBOL(longjmp, siglongjmp)
diff --git a/libc/arch-x86_64/bionic/setjmp.S b/libc/arch-x86_64/bionic/setjmp.S
index 56ebb07..34b4365 100644
--- a/libc/arch-x86_64/bionic/setjmp.S
+++ b/libc/arch-x86_64/bionic/setjmp.S
@@ -35,8 +35,22 @@
#include <private/bionic_asm.h>
-// These are only the callee-saved registers. Code calling setjmp
-// will expect the rest to be clobbered anyway.
+
+// The internal structure of a jmp_buf is totally private.
+// Current layout (changes from release to release):
+//
+// word name description
+// 0 rbx registers
+// 1 rbp
+// 2 r12
+// 3 r13
+// 4 r14
+// 5 r15
+// 6 rsp
+// 7 pc
+// 8 sigflag/cookie setjmp cookie in top 31 bits, signal mask flag in low bit
+// 9 sigmask signal mask (includes rt signals as well)
+// 10 checksum checksum of the core registers, to give better error messages.
#define _JB_RBX 0
#define _JB_RBP 1
@@ -48,9 +62,10 @@
#define _JB_PC 7
#define _JB_SIGFLAG 8
#define _JB_SIGMASK 9
-#define _JB_SIGMASK_RT 10 // sigprocmask will write here too.
+#define _JB_CHECKSUM 10
#define MANGLE_REGISTERS 1
+
.macro m_mangle_registers reg
#if MANGLE_REGISTERS
xorq \reg,%rbx
@@ -68,6 +83,12 @@
m_mangle_registers \reg
.endm
+.macro m_calculate_checksum dst, src
+ movq $0, \dst
+ .irp i,0,1,2,3,4,5,6,7
+ xorq (\i*8)(\src), \dst
+ .endr
+.endm
ENTRY(setjmp)
movl $1,%esi
@@ -118,6 +139,9 @@
movq %r11,(_JB_PC * 8)(%rdi)
m_unmangle_registers %rax
+ m_calculate_checksum %rax, %rdi
+ movq %rax, (_JB_CHECKSUM * 8)(%rdi)
+
xorl %eax,%eax
ret
END(sigsetjmp)
@@ -127,6 +151,10 @@
movq %rdi,%r12
pushq %rsi // Push 'value'.
+ m_calculate_checksum %rax, %rdi
+ xorq (_JB_CHECKSUM * 8)(%rdi), %rax
+ jnz 3f
+
// Do we need to restore the signal mask?
movq (_JB_SIGFLAG * 8)(%rdi), %rdi
pushq %rdi // Push cookie
@@ -172,6 +200,9 @@
1:
movq %r11,0(%rsp)
ret
+
+3:
+ call PIC_PLT(__bionic_setjmp_checksum_mismatch)
END(siglongjmp)
ALIAS_SYMBOL(longjmp, siglongjmp)
diff --git a/libc/bionic/clock_nanosleep.cpp b/libc/bionic/clock_nanosleep.cpp
index 8e2146f..eade850 100644
--- a/libc/bionic/clock_nanosleep.cpp
+++ b/libc/bionic/clock_nanosleep.cpp
@@ -33,6 +33,8 @@
extern "C" int ___clock_nanosleep(clockid_t, int, const timespec*, timespec*);
int clock_nanosleep(clockid_t clock_id, int flags, const timespec* in, timespec* out) {
+ if (clock_id == CLOCK_THREAD_CPUTIME_ID) return EINVAL;
+
ErrnoRestorer errno_restorer;
return (___clock_nanosleep(clock_id, flags, in, out) == 0) ? 0 : errno;
}
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index a7c3fb0..2fc8af0 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -68,7 +68,7 @@
static void* dlopen_ext(const char* filename, int flags,
const android_dlextinfo* extinfo, void* caller_addr) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
- soinfo* result = do_dlopen(filename, flags, extinfo, caller_addr);
+ void* result = do_dlopen(filename, flags, extinfo, caller_addr);
if (result == nullptr) {
__bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
return nullptr;
@@ -86,8 +86,6 @@
return dlopen_ext(filename, flags, nullptr, caller_addr);
}
-extern android_namespace_t* g_anonymous_namespace;
-
void* dlsym_impl(void* handle, const char* symbol, const char* version, void* caller_addr) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
void* result;
@@ -116,9 +114,11 @@
int dlclose(void* handle) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
- do_dlclose(reinterpret_cast<soinfo*>(handle));
- // dlclose has no defined errors.
- return 0;
+ int result = do_dlclose(handle);
+ if (result != 0) {
+ __bionic_format_dlerror("dlclose failed", linker_get_error_buffer());
+ }
+ return result;
}
int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data) {
@@ -263,6 +263,7 @@
__libdl_info->local_group_root_ = __libdl_info;
__libdl_info->soname_ = "libdl.so";
__libdl_info->target_sdk_version_ = __ANDROID_API__;
+ __libdl_info->generate_handle();
#if defined(__work_around_b_24465209__)
strlcpy(__libdl_info->old_name_, __libdl_info->soname_, sizeof(__libdl_info->old_name_));
#endif
diff --git a/linker/linked_list.h b/linker/linked_list.h
index 88386b0..092e831 100644
--- a/linker/linked_list.h
+++ b/linker/linked_list.h
@@ -43,7 +43,7 @@
return *this;
}
- T* operator*() {
+ T* const operator*() {
return entry_->element;
}
@@ -190,15 +190,15 @@
return nullptr;
}
- iterator begin() {
+ iterator begin() const {
return iterator(head_);
}
- iterator end() {
+ iterator end() const {
return iterator(nullptr);
}
- iterator find(T* value) {
+ iterator find(T* value) const {
for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) {
if (e->element == value) {
return iterator(e);
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 2cdfd2c..855b32b 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -103,7 +103,23 @@
permitted_paths_ = permitted_paths;
}
- soinfo::soinfo_list_t& soinfo_list() { return soinfo_list_; }
+ void add_soinfo(soinfo* si) {
+ soinfo_list_.push_back(si);
+ }
+
+ void add_soinfos(const soinfo::soinfo_list_t& soinfos) {
+ for (auto si : soinfos) {
+ add_soinfo(si);
+ }
+ }
+
+ void remove_soinfo(soinfo* si) {
+ soinfo_list_.remove_if([&](soinfo* candidate) {
+ return si == candidate;
+ });
+ }
+
+ const soinfo::soinfo_list_t& soinfo_list() const { return soinfo_list_; }
// For isolated namespaces - checks if the file is on the search path;
// always returns true for not isolated namespace.
@@ -121,7 +137,8 @@
};
android_namespace_t g_default_namespace;
-android_namespace_t* g_anonymous_namespace = &g_default_namespace;
+static std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
+static android_namespace_t* g_anonymous_namespace = &g_default_namespace;
static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf);
@@ -283,7 +300,8 @@
sonext->next = si;
sonext = si;
- ns->soinfo_list().push_back(si);
+ si->generate_handle();
+ ns->add_soinfo(si);
TRACE("name %s: allocated soinfo @ %p", name, si);
return si;
@@ -332,9 +350,7 @@
}
// remove from the namespace
- si->get_namespace()->soinfo_list().remove_if([&](soinfo* candidate) {
- return si == candidate;
- });
+ si->get_namespace()->remove_soinfo(si);
si->~soinfo();
g_soinfo_allocator.free(si);
@@ -830,6 +846,10 @@
this->namespace_ = ns;
}
+soinfo::~soinfo() {
+ g_soinfo_handles_map.erase(handle_);
+}
+
static uint32_t calculate_elf_hash(const char* name) {
const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name);
uint32_t h = 0, g;
@@ -1252,21 +1272,21 @@
void* handle) {
SymbolName symbol_name(name);
- soinfo::soinfo_list_t& soinfo_list = ns->soinfo_list();
- soinfo::soinfo_list_t::iterator start = soinfo_list.begin();
+ auto& soinfo_list = ns->soinfo_list();
+ auto start = soinfo_list.begin();
if (handle == RTLD_NEXT) {
if (caller == nullptr) {
return nullptr;
} else {
- soinfo::soinfo_list_t::iterator it = soinfo_list.find(caller);
+ auto it = soinfo_list.find(caller);
CHECK (it != soinfo_list.end());
start = ++it;
}
}
const ElfW(Sym)* s = nullptr;
- for (soinfo::soinfo_list_t::iterator it = start, end = soinfo_list.end(); it != end; ++it) {
+ for (auto it = start, end = soinfo_list.end(); it != end; ++it) {
soinfo* si = *it;
// Do not skip RTLD_LOCAL libraries in dlsym(RTLD_DEFAULT, ...)
// if the library is opened by application with target api level <= 22
@@ -1638,7 +1658,7 @@
if (si == nullptr) {
si = g_public_namespace.find_if(predicate);
if (si != nullptr) {
- ns->soinfo_list().push_back(si);
+ ns->add_soinfo(si);
}
}
@@ -1812,7 +1832,7 @@
});
if (candidate != nullptr) {
- ns->soinfo_list().push_back(candidate);
+ ns->add_soinfo(candidate);
task->set_soinfo(candidate);
return true;
}
@@ -2179,7 +2199,7 @@
parse_LD_LIBRARY_PATH(ld_library_path);
}
-soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
+void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
void* caller_addr) {
soinfo* const caller = find_containing_library(caller_addr);
@@ -2223,9 +2243,10 @@
soinfo* si = find_library(ns, name, flags, extinfo, caller);
if (si != nullptr) {
si->call_constructors();
+ return si->to_handle();
}
- return si;
+ return nullptr;
}
int do_dladdr(const void* addr, Dl_info* info) {
@@ -2251,6 +2272,19 @@
return 1;
}
+static soinfo* soinfo_from_handle(void* handle) {
+ if ((reinterpret_cast<uintptr_t>(handle) & 1) != 0) {
+ auto it = g_soinfo_handles_map.find(reinterpret_cast<uintptr_t>(handle));
+ if (it == g_soinfo_handles_map.end()) {
+ return nullptr;
+ } else {
+ return it->second;
+ }
+ }
+
+ return static_cast<soinfo*>(handle);
+}
+
bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver,
void* caller_addr, void** symbol) {
#if !defined(__LP64__)
@@ -2282,7 +2316,12 @@
if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) {
sym = dlsym_linear_lookup(ns, sym_name, vi, &found, caller, handle);
} else {
- sym = dlsym_handle_lookup(reinterpret_cast<soinfo*>(handle), &found, sym_name, vi);
+ soinfo* si = soinfo_from_handle(handle);
+ if (si == nullptr) {
+ DL_ERR("dlsym failed: invalid handle: %p", handle);
+ return false;
+ }
+ sym = dlsym_handle_lookup(si, &found, sym_name, vi);
}
if (sym != nullptr) {
@@ -2301,9 +2340,16 @@
return false;
}
-void do_dlclose(soinfo* si) {
+int do_dlclose(void* handle) {
ProtectedDataGuard guard;
+ soinfo* si = soinfo_from_handle(handle);
+ if (si == nullptr) {
+ DL_ERR("invalid handle: %p", handle);
+ return -1;
+ }
+
soinfo_unload(si);
+ return 0;
}
bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path) {
@@ -2390,12 +2436,10 @@
if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) {
// If shared - clone the caller namespace
- auto& soinfo_list = caller_ns->soinfo_list();
- std::copy(soinfo_list.begin(), soinfo_list.end(), std::back_inserter(ns->soinfo_list()));
+ ns->add_soinfos(caller_ns->soinfo_list());
} else {
// If not shared - copy only the global group
- auto global_group = make_global_group(caller_ns);
- std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list()));
+ ns->add_soinfos(make_global_group(caller_ns));
}
return ns;
@@ -3239,6 +3283,39 @@
return local_group_root_->target_sdk_version_;
}
+uintptr_t soinfo::get_handle() const {
+ CHECK(has_min_version(3));
+ CHECK(handle_ != 0);
+ return handle_;
+}
+
+void* soinfo::to_handle() {
+ if (get_application_target_sdk_version() <= 23 || !has_min_version(3)) {
+ return this;
+ }
+
+ return reinterpret_cast<void*>(get_handle());
+}
+
+void soinfo::generate_handle() {
+ CHECK(has_min_version(3));
+ CHECK(handle_ == 0); // Make sure this is the first call
+
+ // Make sure the handle is unique and does not collide
+ // with special values which are RTLD_DEFAULT and RTLD_NEXT.
+ do {
+ arc4random_buf(&handle_, sizeof(handle_));
+ // the least significant bit for the handle is always 1
+ // making it easy to test the type of handle passed to
+ // dl* functions.
+ handle_ = handle_ | 1;
+ } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) ||
+ handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) ||
+ g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end());
+
+ g_soinfo_handles_map[handle_] = this;
+}
+
bool soinfo::prelink_image() {
/* Extract dynamic section */
ElfW(Word) dynamic_flags = 0;
@@ -4212,7 +4289,7 @@
// before get_libdl_info().
solist = get_libdl_info();
sonext = get_libdl_info();
- g_default_namespace.soinfo_list().push_back(get_libdl_info());
+ g_default_namespace.add_soinfo(get_libdl_info());
// We have successfully fixed our own relocations. It's safe to run
// the main part of the linker now.
diff --git a/linker/linker.h b/linker/linker.h
index 9145454..81f93ac 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -268,6 +268,7 @@
public:
soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat,
off64_t file_offset, int rtld_flags);
+ ~soinfo();
void call_constructors();
void call_destructors();
@@ -346,6 +347,10 @@
void set_mapped_by_caller(bool reserved_map);
bool is_mapped_by_caller() const;
+ uintptr_t get_handle() const;
+ void generate_handle();
+ void* to_handle();
+
private:
bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
ElfW(Sym)* elf_addr_lookup(const void* addr);
@@ -410,6 +415,7 @@
// version >= 3
std::vector<std::string> dt_runpath_;
android_namespace_t* namespace_;
+ uintptr_t handle_;
friend soinfo* get_libdl_info();
};
@@ -432,8 +438,8 @@
void do_android_get_LD_LIBRARY_PATH(char*, size_t);
void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path);
-soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, void* caller_addr);
-void do_dlclose(soinfo* si);
+void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, void* caller_addr);
+int do_dlclose(void* handle);
int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data);
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 66d8859..bbdc024 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -1050,3 +1050,19 @@
ASSERT_TRUE(ns_get_dlopened_string_anon() != ns_get_dlopened_string_private());
}
+
+TEST(dlext, dlopen_handle_value_platform) {
+ void* handle = dlopen("libtest_dlsym_from_this.so", RTLD_NOW | RTLD_LOCAL);
+ ASSERT_TRUE((reinterpret_cast<uintptr_t>(handle) & 1) != 0)
+ << "dlopen should return odd value for the handle";
+ dlclose(handle);
+}
+
+TEST(dlext, dlopen_handle_value_app_compat) {
+ android_set_application_target_sdk_version(23);
+ void* handle = dlopen("libtest_dlsym_from_this.so", RTLD_NOW | RTLD_LOCAL);
+ ASSERT_TRUE(reinterpret_cast<uintptr_t>(handle) % sizeof(uintptr_t) == 0)
+ << "dlopen should return valid pointer";
+ dlclose(handle);
+}
+
diff --git a/tests/getauxval_test.cpp b/tests/getauxval_test.cpp
index 54458c4..63bc963 100644
--- a/tests/getauxval_test.cpp
+++ b/tests/getauxval_test.cpp
@@ -16,6 +16,7 @@
#include <errno.h>
#include <sys/cdefs.h>
+#include <sys/utsname.h>
#include <gtest/gtest.h>
// getauxval() was only added as of glibc version 2.16.
@@ -64,15 +65,23 @@
TEST(getauxval, arm_has_AT_HWCAP2) {
#if defined(__arm__)
- // If this test fails, apps that use getauxval to decide at runtime whether crypto hardware is
- // available will incorrectly assume that it isn't, and will have really bad performance.
- // If this test fails, ensure that you've enabled COMPAT_BINFMT_ELF in your kernel configuration.
- // Note that 0 ("I don't support any of these things") is a legitimate response --- we need
- // to check errno to see whether we got a "true" 0 or a "not found" 0.
- errno = 0;
- getauxval(AT_HWCAP2);
- ASSERT_EQ(0, errno) << "kernel not reporting AT_HWCAP2 to 32-bit ARM process";
-#else
- GTEST_LOG_(INFO) << "This test is only meaningful for 32-bit ARM code.\n";
+ // There are no known 32-bit processors that implement any of these instructions, so rather
+ // than require that OEMs backport kernel patches, let's just ignore old hardware. Strictly
+ // speaking this would be fooled by someone choosing to ship a 32-bit kernel on 64-bit hardware,
+ // but that doesn't seem very likely in 2016.
+ utsname u;
+ ASSERT_EQ(0, uname(&u));
+ if (strcmp(u.machine, "aarch64") == 0) {
+ // If this test fails, apps that use getauxval to decide at runtime whether crypto hardware is
+ // available will incorrectly assume that it isn't, and will have really bad performance.
+ // If this test fails, ensure that you've enabled COMPAT_BINFMT_ELF in your kernel configuration.
+ // Note that 0 ("I don't support any of these things") is a legitimate response --- we need
+ // to check errno to see whether we got a "true" 0 or a "not found" 0.
+ errno = 0;
+ getauxval(AT_HWCAP2);
+ ASSERT_EQ(0, errno) << "64-bit kernel not reporting AT_HWCAP2 to 32-bit ARM process";
+ return;
+ }
#endif
+ GTEST_LOG_(INFO) << "This test is only meaningful for 32-bit ARM code on 64-bit devices.\n";
}
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
index 32308aa..c5128ea 100644
--- a/tests/signal_test.cpp
+++ b/tests/signal_test.cpp
@@ -411,4 +411,14 @@
<< sent.si_code << ", received " << received.si_code
<< error_msg;
}
+
+#if defined(__arm__) || defined(__aarch64__) || defined(__i386__) || defined(__x86_64__)
+TEST(signal, sigset_size) {
+ // The setjmp implementations for ARM, AArch64, x86, and x86_64 assume that sigset_t can fit in a
+ // long. This is true because ARM and x86 have broken rt signal support, and AArch64 and x86_64
+ // both have a SIGRTMAX defined as 64.
+ static_assert(sizeof(sigset_t) <= sizeof(long), "sigset_t doesn't fit in a long");
+}
+
+#endif
#endif
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 6cdabd2..b1f6364 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -573,3 +573,10 @@
timespec out;
ASSERT_EQ(EINVAL, clock_nanosleep(-1, 0, &in, &out));
}
+
+TEST(time, clock_nanosleep_thread_cputime_id) {
+ timespec in;
+ in.tv_sec = 1;
+ in.tv_nsec = 0;
+ ASSERT_EQ(EINVAL, clock_nanosleep(CLOCK_THREAD_CPUTIME_ID, 0, &in, nullptr));
+}