linker: remove link from external library on unload
am: b37d10cc80
Change-Id: I48901a8fbf4bc25fff1462e6086fc23b76ca2ba3
diff --git a/libc/bionic/pthread_cond.cpp b/libc/bionic/pthread_cond.cpp
index d36426c..c35d9f1 100644
--- a/libc/bionic/pthread_cond.cpp
+++ b/libc/bionic/pthread_cond.cpp
@@ -224,16 +224,18 @@
return pthread_cond_timedwait_monotonic(cond_interface, mutex, abs_timeout);
}
+// Force this function using CLOCK_MONOTONIC because it was always using
+// CLOCK_MONOTONIC in history.
extern "C" int pthread_cond_timedwait_relative_np(pthread_cond_t* cond_interface,
pthread_mutex_t* mutex,
const timespec* rel_timeout) {
timespec ts;
timespec* abs_timeout = nullptr;
if (rel_timeout != nullptr) {
- absolute_timespec_from_timespec(ts, *rel_timeout, CLOCK_REALTIME);
+ absolute_timespec_from_timespec(ts, *rel_timeout, CLOCK_MONOTONIC);
abs_timeout = &ts;
}
- return __pthread_cond_timedwait(__get_internal_cond(cond_interface), mutex, true, abs_timeout);
+ return __pthread_cond_timedwait(__get_internal_cond(cond_interface), mutex, false, abs_timeout);
}
extern "C" int pthread_cond_timeout_np(pthread_cond_t* cond_interface,
diff --git a/linker/linker.cpp b/linker/linker.cpp
index a287cf4..0ab8bd2 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -155,32 +155,35 @@
static soinfo* sonext;
static soinfo* somain; // main process, always the one after libdl_info
-static const char* const kDefaultLdPaths[] = {
#if defined(__LP64__)
- "/system/lib64",
- "/vendor/lib64",
+static const char* const kSystemLibDir = "/system/lib64";
+static const char* const kVendorLibDir = "/vendor/lib64";
+static const char* const kAsanSystemLibDir = "/data/lib64";
+static const char* const kAsanVendorLibDir = "/data/vendor/lib64";
#else
- "/system/lib",
- "/vendor/lib",
+static const char* const kSystemLibDir = "/system/lib";
+static const char* const kVendorLibDir = "/vendor/lib";
+static const char* const kAsanSystemLibDir = "/data/lib";
+static const char* const kAsanVendorLibDir = "/data/vendor/lib";
#endif
+
+static const char* const kDefaultLdPaths[] = {
+ kSystemLibDir,
+ kVendorLibDir,
nullptr
};
static const char* const kAsanDefaultLdPaths[] = {
-#if defined(__LP64__)
- "/data/lib64",
- "/system/lib64",
- "/data/vendor/lib64",
- "/vendor/lib64",
-#else
- "/data/lib",
- "/system/lib",
- "/data/vendor/lib",
- "/vendor/lib",
-#endif
+ kAsanSystemLibDir,
+ kSystemLibDir,
+ kAsanVendorLibDir,
+ kVendorLibDir,
nullptr
};
+// Is ASAN enabled?
+static bool g_is_asan = false;
+
static bool is_system_library(const std::string& realpath) {
for (const auto& dir : g_default_namespace.get_default_library_paths()) {
if (file_is_in_dir(realpath, dir)) {
@@ -190,12 +193,16 @@
return false;
}
-#if defined(__LP64__)
-static const char* const kSystemLibDir = "/system/lib64";
-#else
-static const char* const kSystemLibDir = "/system/lib";
-#endif
-
+// Checks if the file exists and not a directory.
+static bool file_exists(const char* path) {
+ int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ return false;
+ } else {
+ close(fd);
+ return true;
+ }
+}
static std::string dirname(const char *path);
// TODO(dimitry): The grey-list is a workaround for http://b/26394120 ---
@@ -2373,8 +2380,27 @@
}
}
+ std::string asan_name_holder;
+
+ const char* translated_name = name;
+ if (g_is_asan) {
+ if (file_is_in_dir(name, kSystemLibDir)) {
+ asan_name_holder = std::string(kAsanSystemLibDir) + "/" + basename(name);
+ if (file_exists(asan_name_holder.c_str())) {
+ translated_name = asan_name_holder.c_str();
+ PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
+ }
+ } else if (file_is_in_dir(name, kVendorLibDir)) {
+ asan_name_holder = std::string(kAsanVendorLibDir) + "/" + basename(name);
+ if (file_exists(asan_name_holder.c_str())) {
+ translated_name = asan_name_holder.c_str();
+ PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
+ }
+ }
+ }
+
ProtectedDataGuard guard;
- soinfo* si = find_library(ns, name, flags, extinfo, caller);
+ soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
if (si != nullptr) {
si->call_constructors();
return si->to_handle();
@@ -2507,8 +2533,8 @@
find_loaded_library_by_soname(&g_default_namespace, soname.c_str(), &candidate);
if (candidate == nullptr) {
- DL_ERR("error initializing public namespace: \"%s\" was not found"
- " in the default namespace", soname.c_str());
+ DL_ERR("error initializing public namespace: a library with soname \"%s\""
+ " was not found in the default namespace", soname.c_str());
return false;
}
@@ -4125,6 +4151,7 @@
const char* bname = basename(interp);
if (bname && (strcmp(bname, "linker_asan") == 0 || strcmp(bname, "linker_asan64") == 0)) {
g_default_ld_paths = kAsanDefaultLdPaths;
+ g_is_asan = true;
} else {
g_default_ld_paths = kDefaultLdPaths;
}
@@ -4199,12 +4226,15 @@
}
}
- const char* executable_path = get_executable_path();
struct stat file_stat;
- if (TEMP_FAILURE_RETRY(stat(executable_path, &file_stat)) != 0) {
- __libc_fatal("unable to stat file for the executable \"%s\": %s", executable_path, strerror(errno));
+ // Stat "/proc/self/exe" instead of executable_path because
+ // the executable could be unlinked by this point and it should
+ // not cause a crash (see http://b/31084669)
+ if (TEMP_FAILURE_RETRY(stat("/proc/self/exe", &file_stat)) != 0) {
+ __libc_fatal("unable to stat \"/proc/self/exe\": %s", strerror(errno));
}
+ const char* executable_path = get_executable_path();
soinfo* si = soinfo_alloc(&g_default_namespace, executable_path, &file_stat, 0, RTLD_GLOBAL);
if (si == nullptr) {
__libc_fatal("Couldn't allocate soinfo: out of memory?");
diff --git a/linker/linker.h b/linker/linker.h
index 3ea601f..49eee78 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -59,6 +59,12 @@
__libc_format_fd(2, "\n"); \
} while (false)
+#define DL_ERR_AND_LOG(fmt, x...) \
+ do { \
+ DL_ERR(fmt, x); \
+ PRINT(fmt, x); \
+ } while (false)
+
#if defined(__LP64__)
#define ELFW(what) ELF64_ ## what
#else
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 136e432..3b60460 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -248,14 +248,15 @@
return true;
}
-bool ElfReader::CheckFileRange(ElfW(Addr) offset, size_t size) {
+bool ElfReader::CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment) {
off64_t range_start;
off64_t range_end;
return safe_add(&range_start, file_offset_, offset) &&
safe_add(&range_end, range_start, size) &&
- range_start < file_size_ &&
- range_end <= file_size_;
+ (range_start < file_size_) &&
+ (range_end <= file_size_) &&
+ ((offset % alignment) == 0);
}
// Loads the program header table from an ELF file into a read-only private
@@ -272,8 +273,11 @@
// Boundary checks
size_t size = phdr_num_ * sizeof(ElfW(Phdr));
- if (!CheckFileRange(header_.e_phoff, size)) {
- DL_ERR("\"%s\" has invalid phdr offset/size", name_.c_str());
+ if (!CheckFileRange(header_.e_phoff, size, alignof(ElfW(Phdr)))) {
+ DL_ERR_AND_LOG("\"%s\" has invalid phdr offset/size: %zu/%zu",
+ name_.c_str(),
+ static_cast<size_t>(header_.e_phoff),
+ size);
return false;
}
@@ -290,13 +294,16 @@
shdr_num_ = header_.e_shnum;
if (shdr_num_ == 0) {
- DL_ERR("\"%s\" has no section headers", name_.c_str());
+ DL_ERR_AND_LOG("\"%s\" has no section headers", name_.c_str());
return false;
}
size_t size = shdr_num_ * sizeof(ElfW(Shdr));
- if (!CheckFileRange(header_.e_shoff, size)) {
- DL_ERR("\"%s\" has invalid shdr offset/size", name_.c_str());
+ if (!CheckFileRange(header_.e_shoff, size, alignof(const ElfW(Shdr)))) {
+ DL_ERR_AND_LOG("\"%s\" has invalid shdr offset/size: %zu/%zu",
+ name_.c_str(),
+ static_cast<size_t>(header_.e_shoff),
+ size);
return false;
}
@@ -320,26 +327,27 @@
}
if (dynamic_shdr == nullptr) {
- DL_ERR("\"%s\" .dynamic section header was not found", name_.c_str());
+ DL_ERR_AND_LOG("\"%s\" .dynamic section header was not found", name_.c_str());
return false;
}
if (dynamic_shdr->sh_link >= shdr_num_) {
- DL_ERR("\"%s\" .dynamic section has invalid sh_link: %d", name_.c_str(), dynamic_shdr->sh_link);
+ DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid sh_link: %d",
+ name_.c_str(),
+ dynamic_shdr->sh_link);
return false;
}
const ElfW(Shdr)* strtab_shdr = &shdr_table_[dynamic_shdr->sh_link];
if (strtab_shdr->sh_type != SHT_STRTAB) {
- DL_ERR("\"%s\" .dynamic section has invalid link(%d) sh_type: %d (expected SHT_STRTAB)",
- name_.c_str(), dynamic_shdr->sh_link, strtab_shdr->sh_type);
+ DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid link(%d) sh_type: %d (expected SHT_STRTAB)",
+ name_.c_str(), dynamic_shdr->sh_link, strtab_shdr->sh_type);
return false;
}
- if (!CheckFileRange(dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) {
- DL_ERR("\"%s\" has invalid offset/size of .dynamic section", name_.c_str());
- PRINT("\"%s\" has invalid offset/size of .dynamic section", name_.c_str());
+ if (!CheckFileRange(dynamic_shdr->sh_offset, dynamic_shdr->sh_size, alignof(const ElfW(Dyn)))) {
+ DL_ERR_AND_LOG("\"%s\" has invalid offset/size of .dynamic section", name_.c_str());
return false;
}
@@ -350,9 +358,9 @@
dynamic_ = static_cast<const ElfW(Dyn)*>(dynamic_fragment_.data());
- if (!CheckFileRange(strtab_shdr->sh_offset, strtab_shdr->sh_size)) {
- DL_ERR("\"%s\" has invalid offset/size of the .strtab section linked from .dynamic section",
- name_.c_str());
+ if (!CheckFileRange(strtab_shdr->sh_offset, strtab_shdr->sh_size, alignof(const char))) {
+ DL_ERR_AND_LOG("\"%s\" has invalid offset/size of the .strtab section linked from .dynamic section",
+ name_.c_str());
return false;
}
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 89ec094..d6276ed 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -65,7 +65,7 @@
bool LoadSegments();
bool FindPhdr();
bool CheckPhdr(ElfW(Addr));
- bool CheckFileRange(ElfW(Addr) offset, size_t size);
+ bool CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment);
bool did_read_;
bool did_load_;
diff --git a/tests/pty_test.cpp b/tests/pty_test.cpp
index 91d1f5e..a371434 100644
--- a/tests/pty_test.cpp
+++ b/tests/pty_test.cpp
@@ -14,11 +14,17 @@
* limitations under the License.
*/
+#include <pty.h>
+
#include <gtest/gtest.h>
-#include <pty.h>
+#include <pthread.h>
#include <sys/ioctl.h>
+#include <atomic>
+
+#include <android-base/file.h>
+
#include "utils.h"
TEST(pty, openpty) {
@@ -64,3 +70,82 @@
close(master);
}
+
+struct PtyReader_28979140_Arg {
+ int slave_fd;
+ uint32_t data_count;
+ bool finished;
+ std::atomic<bool> matched;
+};
+
+static void PtyReader_28979140(PtyReader_28979140_Arg* arg) {
+ arg->finished = false;
+ cpu_set_t cpus;
+ ASSERT_EQ(0, sched_getaffinity(0, sizeof(cpu_set_t), &cpus));
+ CPU_CLR(0, &cpus);
+ ASSERT_EQ(0, sched_setaffinity(0, sizeof(cpu_set_t), &cpus));
+
+ uint32_t counter = 0;
+ while (counter <= arg->data_count) {
+ char buf[4096]; // Use big buffer to read to hit the bug more easily.
+ size_t to_read = std::min(sizeof(buf), (arg->data_count + 1 - counter) * sizeof(uint32_t));
+ ASSERT_TRUE(android::base::ReadFully(arg->slave_fd, buf, to_read));
+ size_t num_of_value = to_read / sizeof(uint32_t);
+ uint32_t* p = reinterpret_cast<uint32_t*>(buf);
+ while (num_of_value-- > 0) {
+ if (*p++ != counter++) {
+ arg->matched = false;
+ }
+ }
+ }
+ close(arg->slave_fd);
+ arg->finished = true;
+}
+
+TEST(pty, bug_28979140) {
+ // This test is to test a kernel bug, which uses a lock free ring-buffer to
+ // pass data through a raw pty, but missing necessary memory barriers.
+ if (sysconf(_SC_NPROCESSORS_ONLN) == 1) {
+ GTEST_LOG_(INFO) << "This test tests bug happens only on multiprocessors.";
+ return;
+ }
+ constexpr uint32_t TEST_DATA_COUNT = 200000;
+
+ // 1. Open raw pty.
+ int master;
+ int slave;
+ ASSERT_EQ(0, openpty(&master, &slave, nullptr, nullptr, nullptr));
+ termios tattr;
+ ASSERT_EQ(0, tcgetattr(slave, &tattr));
+ cfmakeraw(&tattr);
+ ASSERT_EQ(0, tcsetattr(slave, TCSADRAIN, &tattr));
+
+ // 2. Create thread for slave reader.
+ pthread_t thread;
+ PtyReader_28979140_Arg arg;
+ arg.slave_fd = slave;
+ arg.data_count = TEST_DATA_COUNT;
+ arg.matched = true;
+ ASSERT_EQ(0, pthread_create(&thread, nullptr,
+ reinterpret_cast<void*(*)(void*)>(PtyReader_28979140),
+ &arg));
+
+ // 3. Make master thread and slave thread running on different cpus:
+ // master thread uses cpu 0, and slave thread uses other cpus.
+ cpu_set_t cpus;
+ CPU_ZERO(&cpus);
+ CPU_SET(0, &cpus);
+ ASSERT_EQ(0, sched_setaffinity(0, sizeof(cpu_set_t), &cpus));
+
+ // 4. Send data to slave.
+ uint32_t counter = 0;
+ while (counter <= TEST_DATA_COUNT) {
+ ASSERT_TRUE(android::base::WriteFully(master, &counter, sizeof(counter)));
+ ASSERT_TRUE(arg.matched) << "failed at count = " << counter;
+ counter++;
+ }
+ ASSERT_EQ(0, pthread_join(thread, nullptr));
+ ASSERT_TRUE(arg.finished);
+ ASSERT_TRUE(arg.matched);
+ close(master);
+}
diff --git a/tests/sys_mman_test.cpp b/tests/sys_mman_test.cpp
index ddb6c77..62401a6 100644
--- a/tests/sys_mman_test.cpp
+++ b/tests/sys_mman_test.cpp
@@ -221,14 +221,21 @@
ASSERT_EQ(MAP_FAILED, mremap(nullptr, 0, 0, 0));
}
-const size_t huge = size_t(PTRDIFF_MAX) + 1;
+constexpr size_t kHuge = size_t(PTRDIFF_MAX) + 1;
TEST(sys_mman, mmap_PTRDIFF_MAX) {
- ASSERT_EQ(MAP_FAILED, mmap(nullptr, huge, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+ ASSERT_EQ(MAP_FAILED, mmap(nullptr, kHuge, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
}
TEST(sys_mman, mremap_PTRDIFF_MAX) {
void* map = mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_NE(MAP_FAILED, map);
- ASSERT_EQ(MAP_FAILED, mremap(map, PAGE_SIZE, huge, MREMAP_MAYMOVE));
+ ASSERT_EQ(MAP_FAILED, mremap(map, PAGE_SIZE, kHuge, MREMAP_MAYMOVE));
+}
+
+TEST(sys_mman, mmap_bug_27265969) {
+ char* base = reinterpret_cast<char*>(mmap(nullptr, PAGE_SIZE * 2, PROT_EXEC | PROT_READ,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
+ // Some kernels had bugs that would cause segfaults here...
+ __builtin___clear_cache(base, base + (PAGE_SIZE * 2));
}