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:
parent
30bc91a083
commit
aca299ac47
|
@ -110,6 +110,7 @@ struct android_namespace_t {
|
||||||
void add_soinfos(const soinfo::soinfo_list_t& soinfos) {
|
void add_soinfos(const soinfo::soinfo_list_t& soinfos) {
|
||||||
for (auto si : soinfos) {
|
for (auto si : soinfos) {
|
||||||
add_soinfo(si);
|
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<LinkedListEntry<soinfo>> g_soinfo_links_allocator;
|
||||||
|
|
||||||
static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator;
|
static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator;
|
||||||
|
static LinkerTypeAllocator<LinkedListEntry<android_namespace_t>> g_namespace_list_allocator;
|
||||||
|
|
||||||
static soinfo* solist;
|
static soinfo* solist;
|
||||||
static soinfo* sonext;
|
static soinfo* sonext;
|
||||||
|
@ -286,6 +288,14 @@ void SoinfoListAllocator::free(LinkedListEntry<soinfo>* entry) {
|
||||||
g_soinfo_links_allocator.free(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,
|
static soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
|
||||||
struct stat* file_stat, off64_t file_offset,
|
struct stat* file_stat, off64_t file_offset,
|
||||||
uint32_t rtld_flags) {
|
uint32_t rtld_flags) {
|
||||||
|
@ -349,9 +359,6 @@ static void soinfo_free(soinfo* si) {
|
||||||
sonext = prev;
|
sonext = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove from the namespace
|
|
||||||
si->get_namespace()->remove_soinfo(si);
|
|
||||||
|
|
||||||
si->~soinfo();
|
si->~soinfo();
|
||||||
g_soinfo_allocator.free(si);
|
g_soinfo_allocator.free(si);
|
||||||
}
|
}
|
||||||
|
@ -843,7 +850,7 @@ soinfo::soinfo(android_namespace_t* ns, const char* realpath,
|
||||||
}
|
}
|
||||||
|
|
||||||
this->rtld_flags_ = rtld_flags;
|
this->rtld_flags_ = rtld_flags;
|
||||||
this->namespace_ = ns;
|
this->primary_namespace_ = ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
soinfo::~soinfo() {
|
soinfo::~soinfo() {
|
||||||
|
@ -1003,6 +1010,7 @@ class ProtectedDataGuard {
|
||||||
g_soinfo_allocator.protect_all(protection);
|
g_soinfo_allocator.protect_all(protection);
|
||||||
g_soinfo_links_allocator.protect_all(protection);
|
g_soinfo_links_allocator.protect_all(protection);
|
||||||
g_namespace_allocator.protect_all(protection);
|
g_namespace_allocator.protect_all(protection);
|
||||||
|
g_namespace_list_allocator.protect_all(protection);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t ref_count_;
|
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",
|
TRACE("deprecated (old format of soinfo): %s needs to unload %s",
|
||||||
si->get_realpath(), library_name);
|
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);
|
library_name, RTLD_NOLOAD, nullptr, nullptr);
|
||||||
|
|
||||||
if (needed != 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;
|
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) {
|
void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
|
||||||
// Use basic string manipulation calls to avoid snprintf.
|
// Use basic string manipulation calls to avoid snprintf.
|
||||||
// snprintf indirectly calls pthread_getspecific to get the size of a buffer.
|
// 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;
|
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 != nullptr) {
|
||||||
if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) {
|
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;
|
soinfo* found = nullptr;
|
||||||
const ElfW(Sym)* sym = nullptr;
|
const ElfW(Sym)* sym = nullptr;
|
||||||
soinfo* caller = find_containing_library(caller_addr);
|
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_instance;
|
||||||
version_info* vi = nullptr;
|
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);
|
soinfo* caller_soinfo = find_containing_library(caller_addr);
|
||||||
|
|
||||||
android_namespace_t* caller_ns = caller_soinfo != nullptr ?
|
android_namespace_t* caller_ns = caller_soinfo != nullptr ?
|
||||||
caller_soinfo->get_namespace() :
|
caller_soinfo->get_primary_namespace() :
|
||||||
g_anonymous_namespace;
|
g_anonymous_namespace;
|
||||||
|
|
||||||
ProtectedDataGuard guard;
|
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();
|
parents_.clear();
|
||||||
children_.clear();
|
children_.clear();
|
||||||
|
secondary_namespaces_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_t soinfo::get_st_dev() const {
|
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;
|
return g_empty_runpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
android_namespace_t* soinfo::get_namespace() {
|
android_namespace_t* soinfo::get_primary_namespace() {
|
||||||
if (has_min_version(3)) {
|
if (has_min_version(3)) {
|
||||||
return namespace_;
|
return primary_namespace_;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &g_default_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 {
|
ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const {
|
||||||
if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
|
if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
|
||||||
return call_ifunc_resolver(s->st_value + load_bias);
|
return call_ifunc_resolver(s->st_value + load_bias);
|
||||||
|
|
|
@ -114,6 +114,16 @@ class SoinfoListAllocator {
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(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 {
|
class SymbolName {
|
||||||
public:
|
public:
|
||||||
explicit SymbolName(const char* name)
|
explicit SymbolName(const char* name)
|
||||||
|
@ -166,6 +176,7 @@ class VersionTracker {
|
||||||
struct soinfo {
|
struct soinfo {
|
||||||
public:
|
public:
|
||||||
typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
|
typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
|
||||||
|
typedef LinkedList<android_namespace_t, NamespaceListAllocator> android_namespace_list_t;
|
||||||
#if defined(__work_around_b_24465209__)
|
#if defined(__work_around_b_24465209__)
|
||||||
private:
|
private:
|
||||||
char old_name_[SOINFO_NAME_LEN];
|
char old_name_[SOINFO_NAME_LEN];
|
||||||
|
@ -342,7 +353,8 @@ struct soinfo {
|
||||||
|
|
||||||
void set_dt_runpath(const char *);
|
void set_dt_runpath(const char *);
|
||||||
const std::vector<std::string>& get_dt_runpath() const;
|
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);
|
void set_mapped_by_caller(bool reserved_map);
|
||||||
bool is_mapped_by_caller() const;
|
bool is_mapped_by_caller() const;
|
||||||
|
@ -414,7 +426,8 @@ struct soinfo {
|
||||||
|
|
||||||
// version >= 3
|
// version >= 3
|
||||||
std::vector<std::string> dt_runpath_;
|
std::vector<std::string> dt_runpath_;
|
||||||
android_namespace_t* namespace_;
|
android_namespace_t* primary_namespace_;
|
||||||
|
android_namespace_list_t secondary_namespaces_;
|
||||||
uintptr_t handle_;
|
uintptr_t handle_;
|
||||||
|
|
||||||
friend soinfo* get_libdl_info();
|
friend soinfo* get_libdl_info();
|
||||||
|
|
|
@ -959,6 +959,70 @@ TEST(dlext, ns_shared) {
|
||||||
dlclose(handle2);
|
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) {
|
TEST(dlext, ns_anonymous) {
|
||||||
static const char* root_lib = "libnstest_root.so";
|
static const char* root_lib = "libnstest_root.so";
|
||||||
std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
|
std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
|
||||||
|
|
Loading…
Reference in New Issue