Remove dangling links in secondary namespaces

linker didn't remove link to the soinfo from shared
namespaces on soinfo_unload, because it didn't keep
record of all namespaces the library is added to.

This change adds test for this and also fixes the
problem by introducing list of secondary namespaces
to soinfo, which is used to remove soinfo in
soinfo::remove_all_links().

Bug: http://b/28115950
Change-Id: Ifbf6e54f92fa6e88f86b6a8dd6dc22d4553afd22
This commit is contained in:
Dimitry Ivanov 2016-04-11 12:42:58 -07:00
parent 30bc91a083
commit aca299ac47
3 changed files with 118 additions and 13 deletions

View File

@ -110,6 +110,7 @@ struct android_namespace_t {
void add_soinfos(const soinfo::soinfo_list_t& soinfos) {
for (auto si : soinfos) {
add_soinfo(si);
si->add_secondary_namespace(this);
}
}
@ -146,6 +147,7 @@ static LinkerTypeAllocator<soinfo> g_soinfo_allocator;
static LinkerTypeAllocator<LinkedListEntry<soinfo>> g_soinfo_links_allocator;
static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator;
static LinkerTypeAllocator<LinkedListEntry<android_namespace_t>> g_namespace_list_allocator;
static soinfo* solist;
static soinfo* sonext;
@ -286,6 +288,14 @@ void SoinfoListAllocator::free(LinkedListEntry<soinfo>* entry) {
g_soinfo_links_allocator.free(entry);
}
LinkedListEntry<android_namespace_t>* NamespaceListAllocator::alloc() {
return g_namespace_list_allocator.alloc();
}
void NamespaceListAllocator::free(LinkedListEntry<android_namespace_t>* entry) {
g_namespace_list_allocator.free(entry);
}
static soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
struct stat* file_stat, off64_t file_offset,
uint32_t rtld_flags) {
@ -349,9 +359,6 @@ static void soinfo_free(soinfo* si) {
sonext = prev;
}
// remove from the namespace
si->get_namespace()->remove_soinfo(si);
si->~soinfo();
g_soinfo_allocator.free(si);
}
@ -843,7 +850,7 @@ soinfo::soinfo(android_namespace_t* ns, const char* realpath,
}
this->rtld_flags_ = rtld_flags;
this->namespace_ = ns;
this->primary_namespace_ = ns;
}
soinfo::~soinfo() {
@ -1003,6 +1010,7 @@ class ProtectedDataGuard {
g_soinfo_allocator.protect_all(protection);
g_soinfo_links_allocator.protect_all(protection);
g_namespace_allocator.protect_all(protection);
g_namespace_list_allocator.protect_all(protection);
}
static size_t ref_count_;
@ -2122,7 +2130,7 @@ static void soinfo_unload(soinfo* root) {
TRACE("deprecated (old format of soinfo): %s needs to unload %s",
si->get_realpath(), library_name);
soinfo* needed = find_library(si->get_namespace(),
soinfo* needed = find_library(si->get_primary_namespace(),
library_name, RTLD_NOLOAD, nullptr, nullptr);
if (needed != nullptr) {
@ -2172,6 +2180,10 @@ static std::string symbol_display_name(const char* sym_name, const char* sym_ver
return std::string(sym_name) + ", version " + sym_ver;
}
static android_namespace_t* get_caller_namespace(soinfo* caller) {
return caller != nullptr ? caller->get_primary_namespace() : g_anonymous_namespace;
}
void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
// Use basic string manipulation calls to avoid snprintf.
// snprintf indirectly calls pthread_getspecific to get the size of a buffer.
@ -2208,7 +2220,7 @@ void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
return nullptr;
}
android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_namespace;
android_namespace_t* ns = get_caller_namespace(caller);
if (extinfo != nullptr) {
if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) {
@ -2302,7 +2314,7 @@ bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver,
soinfo* found = nullptr;
const ElfW(Sym)* sym = nullptr;
soinfo* caller = find_containing_library(caller_addr);
android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_namespace;
android_namespace_t* ns = get_caller_namespace(caller);
version_info vi_instance;
version_info* vi = nullptr;
@ -2415,7 +2427,7 @@ android_namespace_t* create_namespace(const void* caller_addr,
soinfo* caller_soinfo = find_containing_library(caller_addr);
android_namespace_t* caller_ns = caller_soinfo != nullptr ?
caller_soinfo->get_namespace() :
caller_soinfo->get_primary_namespace() :
g_anonymous_namespace;
ProtectedDataGuard guard;
@ -3050,9 +3062,20 @@ void soinfo::remove_all_links() {
});
});
// 2. Once everything untied - clear local lists.
// 2. Remove from the primary namespace
primary_namespace_->remove_soinfo(this);
primary_namespace_ = nullptr;
// 3. Remove from secondary namespaces
secondary_namespaces_.for_each([&](android_namespace_t* ns) {
ns->remove_soinfo(this);
});
// 4. Once everything untied - clear local lists.
parents_.clear();
children_.clear();
secondary_namespaces_.clear();
}
dev_t soinfo::get_st_dev() const {
@ -3186,14 +3209,19 @@ const std::vector<std::string>& soinfo::get_dt_runpath() const {
return g_empty_runpath;
}
android_namespace_t* soinfo::get_namespace() {
android_namespace_t* soinfo::get_primary_namespace() {
if (has_min_version(3)) {
return namespace_;
return primary_namespace_;
}
return &g_default_namespace;
}
void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) {
CHECK(has_min_version(3));
secondary_namespaces_.push_back(secondary_ns);
}
ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const {
if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
return call_ifunc_resolver(s->st_value + load_bias);

View File

@ -114,6 +114,16 @@ class SoinfoListAllocator {
DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator);
};
class NamespaceListAllocator {
public:
static LinkedListEntry<android_namespace_t>* alloc();
static void free(LinkedListEntry<android_namespace_t>* entry);
private:
// unconstructable
DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceListAllocator);
};
class SymbolName {
public:
explicit SymbolName(const char* name)
@ -166,6 +176,7 @@ class VersionTracker {
struct soinfo {
public:
typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
typedef LinkedList<android_namespace_t, NamespaceListAllocator> android_namespace_list_t;
#if defined(__work_around_b_24465209__)
private:
char old_name_[SOINFO_NAME_LEN];
@ -342,7 +353,8 @@ struct soinfo {
void set_dt_runpath(const char *);
const std::vector<std::string>& get_dt_runpath() const;
android_namespace_t* get_namespace();
android_namespace_t* get_primary_namespace();
void add_secondary_namespace(android_namespace_t* secondary_ns);
void set_mapped_by_caller(bool reserved_map);
bool is_mapped_by_caller() const;
@ -414,7 +426,8 @@ struct soinfo {
// version >= 3
std::vector<std::string> dt_runpath_;
android_namespace_t* namespace_;
android_namespace_t* primary_namespace_;
android_namespace_list_t secondary_namespaces_;
uintptr_t handle_;
friend soinfo* get_libdl_info();

View File

@ -959,6 +959,70 @@ TEST(dlext, ns_shared) {
dlclose(handle2);
}
TEST(dlext, ns_shared_dlclose) {
std::string path = "libc.so:libc++.so:libdl.so:libm.so";
const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
android_set_application_target_sdk_version(42U); // something > 23
ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
// preload this library to the default namespace to check if it
// is shared later on.
void* handle_dlopened =
dlopen((lib_path + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW);
ASSERT_TRUE(handle_dlopened != nullptr) << dlerror();
android_namespace_t* ns_isolated_shared =
android_create_namespace("private_isolated_shared", nullptr,
(lib_path + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED,
nullptr);
ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror();
// Check if "libnstest_dlopened.so" is loaded (and the same)
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
extinfo.library_namespace = ns_isolated_shared;
void* handle = android_dlopen_ext("libnstest_dlopened.so", RTLD_NOW | RTLD_NOLOAD, &extinfo);
ASSERT_TRUE(handle != nullptr) << dlerror();
ASSERT_TRUE(handle == handle_dlopened);
dlclose(handle);
dlclose(handle_dlopened);
// And now check that the library cannot be found by soname (and is no longer loaded)
handle = android_dlopen_ext("libnstest_dlopened.so", RTLD_NOW | RTLD_NOLOAD, &extinfo);
ASSERT_TRUE(handle == nullptr)
<< "Error: libnstest_dlopened.so is still accessible in shared namespace";
handle = android_dlopen_ext((lib_path + "/private_namespace_libs/libnstest_dlopened.so").c_str(),
RTLD_NOW | RTLD_NOLOAD, &extinfo);
ASSERT_TRUE(handle == nullptr)
<< "Error: libnstest_dlopened.so is still accessible in shared namespace";
handle = dlopen("libnstest_dlopened.so", RTLD_NOW | RTLD_NOLOAD);
ASSERT_TRUE(handle == nullptr)
<< "Error: libnstest_dlopened.so is still accessible in default namespace";
handle = dlopen((lib_path + "/private_namespace_libs/libnstest_dlopened.so").c_str(),
RTLD_NOW | RTLD_NOLOAD);
ASSERT_TRUE(handle == nullptr)
<< "Error: libnstest_dlopened.so is still accessible in default namespace";
// Now lets see if the soinfo area gets reused in the wrong way:
// load a library to default namespace.
const std::string lib_public_path = lib_path + "/public_namespace_libs/" + g_public_lib;
void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
ASSERT_TRUE(handle_public != nullptr) << dlerror();
// try to find it in shared namespace
handle = android_dlopen_ext(g_public_lib, RTLD_NOW | RTLD_NOLOAD, &extinfo);
ASSERT_TRUE(handle == nullptr)
<< "Error: " << g_public_lib << " is accessible in shared namespace";
}
TEST(dlext, ns_anonymous) {
static const char* root_lib = "libnstest_root.so";
std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;