diff --git a/base/file.cpp b/base/file.cpp index 32c243957..81b04d74e 100644 --- a/base/file.cpp +++ b/base/file.cpp @@ -238,8 +238,11 @@ std::string GetExecutablePath() { #endif } -std::string Basename(const std::string& path) { +std::string GetExecutableDirectory() { + return Dirname(GetExecutablePath()); +} +std::string Basename(const std::string& path) { // Copy path because basename may modify the string passed in. std::string result(path); diff --git a/base/file_test.cpp b/base/file_test.cpp index bbd503783..102132626 100644 --- a/base/file_test.cpp +++ b/base/file_test.cpp @@ -159,6 +159,14 @@ TEST(file, Readlink) { #endif } +TEST(file, GetExecutableDirectory) { + std::string path = android::base::GetExecutableDirectory(); + ASSERT_NE("", path); + ASSERT_NE(android::base::GetExecutablePath(), path); + ASSERT_EQ('/', path[0]); + ASSERT_NE('/', path[path.size() - 1]); +} + TEST(file, GetExecutablePath) { ASSERT_NE("", android::base::GetExecutablePath()); } diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h index c7e094e13..33d1ab320 100644 --- a/base/include/android-base/file.h +++ b/base/include/android-base/file.h @@ -51,6 +51,7 @@ bool Readlink(const std::string& path, std::string* result); #endif std::string GetExecutablePath(); +std::string GetExecutableDirectory(); // Like the regular basename and dirname, but thread-safe on all // platforms and capable of correctly handling exotic Windows paths. diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp index 70ada729e..ea606a194 100644 --- a/libutils/tests/Android.bp +++ b/libutils/tests/Android.bp @@ -23,6 +23,7 @@ cc_test { srcs: [ "BitSet_test.cpp", "LruCache_test.cpp", + "Singleton_test.cpp", "String8_test.cpp", "StrongPointer_test.cpp", "Unicode_test.cpp", @@ -42,6 +43,8 @@ cc_test { "liblog", "libcutils", "libutils", + "libbase", + "libdl", ], }, linux: { @@ -54,13 +57,35 @@ cc_test { static_libs: [ "libutils", "liblog", + "libbase", ], + host_ldlibs: ["-ldl"], }, }, + required: [ + "libutils_tests_singleton1", + "libutils_tests_singleton2", + ], + cflags: [ "-Wall", "-Wextra", "-Werror", ], } + +cc_test_library { + name: "libutils_tests_singleton1", + host_supported: true, + relative_install_path: "libutils_tests", + srcs: ["Singleton_test1.cpp"], +} + +cc_test_library { + name: "libutils_tests_singleton2", + host_supported: true, + relative_install_path: "libutils_tests", + srcs: ["Singleton_test2.cpp"], + shared_libs: ["libutils_tests_singleton1"], +} diff --git a/libutils/tests/Singleton_test.cpp b/libutils/tests/Singleton_test.cpp new file mode 100644 index 000000000..9acd3c3c2 --- /dev/null +++ b/libutils/tests/Singleton_test.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 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. + */ + +#define LOG_TAG "Singleton_test" + +#include + +#include +#include +#include + +#include + +#include "Singleton_test.h" + +namespace android { + +TEST(SingletonTest, bug35674422) { + std::string path = android::base::GetExecutableDirectory(); + // libutils_tests_singleton1.so contains the ANDROID_SINGLETON_STATIC_INSTANCE + // definition of SingletonTestData, load it first. + std::string lib = android::base::StringPrintf("%s/libutils_tests_singleton1.so", path.c_str()); + void* handle1 = dlopen(lib.c_str(), RTLD_NOW); + ASSERT_TRUE(handle1 != nullptr) << dlerror(); + + // libutils_tests_singleton2.so references SingletonTestData but should not + // have a definition + lib = android::base::StringPrintf("%s/libutils_tests_singleton2.so", path.c_str()); + void* handle2 = dlopen(lib.c_str(), RTLD_NOW); + ASSERT_TRUE(handle2 != nullptr) << dlerror(); + + using has_fn_t = decltype(&singletonHasInstance); + using get_fn_t = decltype(&singletonGetInstanceContents); + using set_fn_t = decltype(&singletonSetInstanceContents); + + has_fn_t has1 = reinterpret_cast(dlsym(handle1, "singletonHasInstance")); + ASSERT_TRUE(has1 != nullptr) << dlerror(); + has_fn_t has2 = reinterpret_cast(dlsym(handle2, "singletonHasInstance")); + ASSERT_TRUE(has2 != nullptr) << dlerror(); + get_fn_t get1 = reinterpret_cast(dlsym(handle1, "singletonGetInstanceContents")); + ASSERT_TRUE(get1 != nullptr) << dlerror(); + get_fn_t get2 = reinterpret_cast(dlsym(handle2, "singletonGetInstanceContents")); + ASSERT_TRUE(get2 != nullptr) << dlerror(); + set_fn_t set1 = reinterpret_cast(dlsym(handle2, "singletonSetInstanceContents")); + ASSERT_TRUE(set1 != nullptr) << dlerror(); + + EXPECT_FALSE(has1()); + EXPECT_FALSE(has2()); + set1(12345678U); + EXPECT_TRUE(has1()); + EXPECT_TRUE(has2()); + EXPECT_EQ(12345678U, get1()); + EXPECT_EQ(12345678U, get2()); +} + +} diff --git a/libutils/tests/Singleton_test.h b/libutils/tests/Singleton_test.h new file mode 100644 index 000000000..c77d9ffe6 --- /dev/null +++ b/libutils/tests/Singleton_test.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef ANDROID_UTILS_SINGLETON_TEST_H +#define ANDROID_UTILS_SINGLETON_TEST_H + +#include + +#include "Singleton_test.h" + +namespace android { + +struct SingletonTestData : Singleton { + unsigned int contents; +}; + +__BEGIN_DECLS + +unsigned int singletonGetInstanceContents(); +void singletonSetInstanceContents(unsigned int); +bool singletonHasInstance(); + +__END_DECLS + +} + +#endif // ANDROID_UTILS_SINGLETON_TEST_H + diff --git a/libutils/tests/Singleton_test1.cpp b/libutils/tests/Singleton_test1.cpp new file mode 100644 index 000000000..4a91ec08a --- /dev/null +++ b/libutils/tests/Singleton_test1.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 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 + +#include "Singleton_test.h" + +namespace android { + +// Singleton is referenced in Singleton_test1.cpp and +// Singleton_test2.cpp, but only defined in Singleton_test1.cpp. +ANDROID_SINGLETON_STATIC_INSTANCE(SingletonTestData); + +void singletonSetInstanceContents(unsigned int contents) { + SingletonTestData::getInstance().contents = contents; +} + +unsigned int singletonGetInstanceContents() { + return SingletonTestData::getInstance().contents; +} + +bool singletonHasInstance() { + return SingletonTestData::hasInstance(); +} + +} diff --git a/libutils/tests/Singleton_test2.cpp b/libutils/tests/Singleton_test2.cpp new file mode 100644 index 000000000..eb2a9dfc0 --- /dev/null +++ b/libutils/tests/Singleton_test2.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 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 + +#include "Singleton_test.h" + +namespace android { + +// Singleton is referenced in Singleton_test1.cpp and +// Singleton_test2.cpp, but only defined in Singleton_test1.cpp. + +void singletonSetInstanceContents(unsigned int contents) { + SingletonTestData::getInstance().contents = contents; +} + +unsigned int singletonGetInstanceContents() { + return SingletonTestData::getInstance().contents; +} + +bool singletonHasInstance() { + return SingletonTestData::hasInstance(); +} + +}