blob: 88964b7a85432371d0b2bf24d46e8a010b9a9354 [file] [log] [blame]
Orion Hodson9b16e342019-10-09 13:29:16 +01001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <dlfcn.h>
18#include <memory>
19#include <unordered_map>
20
21#include <android-base/strings.h>
22#include <gmock/gmock.h>
23#include <gtest/gtest.h>
24#include <jni.h>
25
26#include "native_loader_namespace.h"
Martin Stjernholm94fd9ea2019-10-24 16:57:34 +010027#include "nativehelper/scoped_utf_chars.h"
Orion Hodson9b16e342019-10-09 13:29:16 +010028#include "nativeloader/dlext_namespaces.h"
29#include "nativeloader/native_loader.h"
30#include "public_libraries.h"
31
Orion Hodson9b16e342019-10-09 13:29:16 +010032namespace android {
33namespace nativeloader {
34
Martin Stjernholm48297332019-11-12 21:21:32 +000035using ::testing::Eq;
36using ::testing::Return;
37using ::testing::StrEq;
38using ::testing::_;
39using internal::ConfigEntry;
40using internal::ParseConfig;
41
Martin Stjernholmbe08b202019-11-12 20:11:00 +000042#if defined(__LP64__)
43#define LIB_DIR "lib64"
44#else
45#define LIB_DIR "lib"
46#endif
47
Orion Hodson9b16e342019-10-09 13:29:16 +010048// gmock interface that represents interested platform APIs on libdl and libnativebridge
49class Platform {
50 public:
51 virtual ~Platform() {}
52
53 // libdl APIs
54 virtual void* dlopen(const char* filename, int flags) = 0;
55 virtual int dlclose(void* handle) = 0;
56 virtual char* dlerror(void) = 0;
57
58 // These mock_* are the APIs semantically the same across libdl and libnativebridge.
59 // Instead of having two set of mock APIs for the two, define only one set with an additional
60 // argument 'bool bridged' to identify the context (i.e., called for libdl or libnativebridge).
61 typedef char* mock_namespace_handle;
62 virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames,
63 const char* search_paths) = 0;
64 virtual mock_namespace_handle mock_create_namespace(
65 bool bridged, const char* name, const char* ld_library_path, const char* default_library_path,
66 uint64_t type, const char* permitted_when_isolated_path, mock_namespace_handle parent) = 0;
67 virtual bool mock_link_namespaces(bool bridged, mock_namespace_handle from,
68 mock_namespace_handle to, const char* sonames) = 0;
69 virtual mock_namespace_handle mock_get_exported_namespace(bool bridged, const char* name) = 0;
70 virtual void* mock_dlopen_ext(bool bridged, const char* filename, int flags,
71 mock_namespace_handle ns) = 0;
72
73 // libnativebridge APIs for which libdl has no corresponding APIs
74 virtual bool NativeBridgeInitialized() = 0;
75 virtual const char* NativeBridgeGetError() = 0;
76 virtual bool NativeBridgeIsPathSupported(const char*) = 0;
77 virtual bool NativeBridgeIsSupported(const char*) = 0;
78
79 // To mock "ClassLoader Object.getParent()"
80 virtual const char* JniObject_getParent(const char*) = 0;
81};
82
83// The mock does not actually create a namespace object. But simply casts the pointer to the
84// string for the namespace name as the handle to the namespace object.
85#define TO_ANDROID_NAMESPACE(str) \
86 reinterpret_cast<struct android_namespace_t*>(const_cast<char*>(str))
87
88#define TO_BRIDGED_NAMESPACE(str) \
89 reinterpret_cast<struct native_bridge_namespace_t*>(const_cast<char*>(str))
90
91#define TO_MOCK_NAMESPACE(ns) reinterpret_cast<Platform::mock_namespace_handle>(ns)
92
93// These represents built-in namespaces created by the linker according to ld.config.txt
94static std::unordered_map<std::string, Platform::mock_namespace_handle> namespaces = {
95 {"platform", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("platform"))},
96 {"default", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("default"))},
97 {"art", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("art"))},
98 {"sphal", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("sphal"))},
99 {"vndk", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk"))},
100 {"neuralnetworks", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("neuralnetworks"))},
Luke Huang5c017722019-12-17 10:54:26 +0800101 {"cronet", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("cronet"))},
Orion Hodson9b16e342019-10-09 13:29:16 +0100102};
103
104// The actual gmock object
105class MockPlatform : public Platform {
106 public:
107 explicit MockPlatform(bool is_bridged) : is_bridged_(is_bridged) {
108 ON_CALL(*this, NativeBridgeIsSupported(_)).WillByDefault(Return(is_bridged_));
109 ON_CALL(*this, NativeBridgeIsPathSupported(_)).WillByDefault(Return(is_bridged_));
110 ON_CALL(*this, mock_get_exported_namespace(_, _))
Martin Stjernholm48297332019-11-12 21:21:32 +0000111 .WillByDefault(testing::Invoke([](bool, const char* name) -> mock_namespace_handle {
Orion Hodson9b16e342019-10-09 13:29:16 +0100112 if (namespaces.find(name) != namespaces.end()) {
113 return namespaces[name];
114 }
115 return TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("(namespace not found"));
116 }));
117 }
118
119 // Mocking libdl APIs
120 MOCK_METHOD2(dlopen, void*(const char*, int));
121 MOCK_METHOD1(dlclose, int(void*));
122 MOCK_METHOD0(dlerror, char*());
123
124 // Mocking the common APIs
125 MOCK_METHOD3(mock_init_anonymous_namespace, bool(bool, const char*, const char*));
126 MOCK_METHOD7(mock_create_namespace,
127 mock_namespace_handle(bool, const char*, const char*, const char*, uint64_t,
128 const char*, mock_namespace_handle));
129 MOCK_METHOD4(mock_link_namespaces,
130 bool(bool, mock_namespace_handle, mock_namespace_handle, const char*));
131 MOCK_METHOD2(mock_get_exported_namespace, mock_namespace_handle(bool, const char*));
132 MOCK_METHOD4(mock_dlopen_ext, void*(bool, const char*, int, mock_namespace_handle));
133
134 // Mocking libnativebridge APIs
135 MOCK_METHOD0(NativeBridgeInitialized, bool());
136 MOCK_METHOD0(NativeBridgeGetError, const char*());
137 MOCK_METHOD1(NativeBridgeIsPathSupported, bool(const char*));
138 MOCK_METHOD1(NativeBridgeIsSupported, bool(const char*));
139
140 // Mocking "ClassLoader Object.getParent()"
141 MOCK_METHOD1(JniObject_getParent, const char*(const char*));
142
143 private:
144 bool is_bridged_;
145};
146
147static std::unique_ptr<MockPlatform> mock;
148
149// Provide C wrappers for the mock object.
150extern "C" {
151void* dlopen(const char* file, int flag) {
152 return mock->dlopen(file, flag);
153}
154
155int dlclose(void* handle) {
156 return mock->dlclose(handle);
157}
158
159char* dlerror(void) {
160 return mock->dlerror();
161}
162
163bool android_init_anonymous_namespace(const char* sonames, const char* search_path) {
164 return mock->mock_init_anonymous_namespace(false, sonames, search_path);
165}
166
167struct android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
168 const char* default_library_path,
169 uint64_t type,
170 const char* permitted_when_isolated_path,
171 struct android_namespace_t* parent) {
172 return TO_ANDROID_NAMESPACE(
173 mock->mock_create_namespace(false, name, ld_library_path, default_library_path, type,
174 permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
175}
176
177bool android_link_namespaces(struct android_namespace_t* from, struct android_namespace_t* to,
178 const char* sonames) {
179 return mock->mock_link_namespaces(false, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
180}
181
182struct android_namespace_t* android_get_exported_namespace(const char* name) {
183 return TO_ANDROID_NAMESPACE(mock->mock_get_exported_namespace(false, name));
184}
185
186void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* info) {
187 return mock->mock_dlopen_ext(false, filename, flags, TO_MOCK_NAMESPACE(info->library_namespace));
188}
189
190// libnativebridge APIs
191bool NativeBridgeIsSupported(const char* libpath) {
192 return mock->NativeBridgeIsSupported(libpath);
193}
194
195struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
196 return TO_BRIDGED_NAMESPACE(mock->mock_get_exported_namespace(true, name));
197}
198
199struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
200 const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
201 const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent) {
202 return TO_BRIDGED_NAMESPACE(
203 mock->mock_create_namespace(true, name, ld_library_path, default_library_path, type,
204 permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
205}
206
207bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
208 struct native_bridge_namespace_t* to, const char* sonames) {
209 return mock->mock_link_namespaces(true, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
210}
211
212void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
213 struct native_bridge_namespace_t* ns) {
214 return mock->mock_dlopen_ext(true, libpath, flag, TO_MOCK_NAMESPACE(ns));
215}
216
217bool NativeBridgeInitialized() {
218 return mock->NativeBridgeInitialized();
219}
220
221bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
222 const char* anon_ns_library_path) {
223 return mock->mock_init_anonymous_namespace(true, public_ns_sonames, anon_ns_library_path);
224}
225
226const char* NativeBridgeGetError() {
227 return mock->NativeBridgeGetError();
228}
229
230bool NativeBridgeIsPathSupported(const char* path) {
231 return mock->NativeBridgeIsPathSupported(path);
232}
233
234} // extern "C"
235
236// A very simple JNI mock.
237// jstring is a pointer to utf8 char array. We don't need utf16 char here.
238// jobject, jclass, and jmethodID are also a pointer to utf8 char array
239// Only a few JNI methods that are actually used in libnativeloader are mocked.
240JNINativeInterface* CreateJNINativeInterface() {
241 JNINativeInterface* inf = new JNINativeInterface();
242 memset(inf, 0, sizeof(JNINativeInterface));
243
244 inf->GetStringUTFChars = [](JNIEnv*, jstring s, jboolean*) -> const char* {
245 return reinterpret_cast<const char*>(s);
246 };
247
248 inf->ReleaseStringUTFChars = [](JNIEnv*, jstring, const char*) -> void { return; };
249
250 inf->NewStringUTF = [](JNIEnv*, const char* bytes) -> jstring {
251 return reinterpret_cast<jstring>(const_cast<char*>(bytes));
252 };
253
254 inf->FindClass = [](JNIEnv*, const char* name) -> jclass {
255 return reinterpret_cast<jclass>(const_cast<char*>(name));
256 };
257
258 inf->CallObjectMethodV = [](JNIEnv*, jobject obj, jmethodID mid, va_list) -> jobject {
259 if (strcmp("getParent", reinterpret_cast<const char*>(mid)) == 0) {
260 // JniObject_getParent can be a valid jobject or nullptr if there is
261 // no parent classloader.
262 const char* ret = mock->JniObject_getParent(reinterpret_cast<const char*>(obj));
263 return reinterpret_cast<jobject>(const_cast<char*>(ret));
264 }
265 return nullptr;
266 };
267
268 inf->GetMethodID = [](JNIEnv*, jclass, const char* name, const char*) -> jmethodID {
269 return reinterpret_cast<jmethodID>(const_cast<char*>(name));
270 };
271
272 inf->NewWeakGlobalRef = [](JNIEnv*, jobject obj) -> jobject { return obj; };
273
274 inf->IsSameObject = [](JNIEnv*, jobject a, jobject b) -> jboolean {
275 return strcmp(reinterpret_cast<const char*>(a), reinterpret_cast<const char*>(b)) == 0;
276 };
277
278 return inf;
279}
280
281static void* const any_nonnull = reinterpret_cast<void*>(0x12345678);
282
283// Custom matcher for comparing namespace handles
284MATCHER_P(NsEq, other, "") {
285 *result_listener << "comparing " << reinterpret_cast<const char*>(arg) << " and " << other;
286 return strcmp(reinterpret_cast<const char*>(arg), reinterpret_cast<const char*>(other)) == 0;
287}
288
289/////////////////////////////////////////////////////////////////
290
291// Test fixture
292class NativeLoaderTest : public ::testing::TestWithParam<bool> {
293 protected:
294 bool IsBridged() { return GetParam(); }
295
296 void SetUp() override {
Martin Stjernholm48297332019-11-12 21:21:32 +0000297 mock = std::make_unique<testing::NiceMock<MockPlatform>>(IsBridged());
Orion Hodson9b16e342019-10-09 13:29:16 +0100298
299 env = std::make_unique<JNIEnv>();
300 env->functions = CreateJNINativeInterface();
301 }
302
303 void SetExpectations() {
304 std::vector<std::string> default_public_libs =
305 android::base::Split(preloadable_public_libraries(), ":");
306 for (auto l : default_public_libs) {
307 EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
308 .WillOnce(Return(any_nonnull));
309 }
310 }
311
312 void RunTest() { InitializeNativeLoader(); }
313
314 void TearDown() override {
315 ResetNativeLoader();
316 delete env->functions;
317 mock.reset();
318 }
319
320 std::unique_ptr<JNIEnv> env;
321};
322
323/////////////////////////////////////////////////////////////////
324
325TEST_P(NativeLoaderTest, InitializeLoadsDefaultPublicLibraries) {
326 SetExpectations();
327 RunTest();
328}
329
330INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool());
331
332/////////////////////////////////////////////////////////////////
333
334class NativeLoaderTest_Create : public NativeLoaderTest {
335 protected:
336 // Test inputs (initialized to the default values). Overriding these
337 // must be done before calling SetExpectations() and RunTest().
338 uint32_t target_sdk_version = 29;
339 std::string class_loader = "my_classloader";
340 bool is_shared = false;
341 std::string dex_path = "/data/app/foo/classes.dex";
Martin Stjernholmbe08b202019-11-12 20:11:00 +0000342 std::string library_path = "/data/app/foo/" LIB_DIR "/arm";
343 std::string permitted_path = "/data/app/foo/" LIB_DIR;
Orion Hodson9b16e342019-10-09 13:29:16 +0100344
345 // expected output (.. for the default test inputs)
346 std::string expected_namespace_name = "classloader-namespace";
347 uint64_t expected_namespace_flags =
348 ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
349 std::string expected_library_path = library_path;
350 std::string expected_permitted_path = std::string("/data:/mnt/expand:") + permitted_path;
351 std::string expected_parent_namespace = "platform";
352 bool expected_link_with_platform_ns = true;
353 bool expected_link_with_art_ns = true;
354 bool expected_link_with_sphal_ns = !vendor_public_libraries().empty();
355 bool expected_link_with_vndk_ns = false;
356 bool expected_link_with_default_ns = false;
357 bool expected_link_with_neuralnetworks_ns = true;
Luke Huang5c017722019-12-17 10:54:26 +0800358 bool expected_link_with_cronet_ns = true;
Orion Hodson9b16e342019-10-09 13:29:16 +0100359 std::string expected_shared_libs_to_platform_ns = default_public_libraries();
360 std::string expected_shared_libs_to_art_ns = art_public_libraries();
361 std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries();
362 std::string expected_shared_libs_to_vndk_ns = vndksp_libraries();
363 std::string expected_shared_libs_to_default_ns = default_public_libraries();
364 std::string expected_shared_libs_to_neuralnetworks_ns = neuralnetworks_public_libraries();
Luke Huang5c017722019-12-17 10:54:26 +0800365 std::string expected_shared_libs_to_cronet_ns = cronet_public_libraries();
Orion Hodson9b16e342019-10-09 13:29:16 +0100366
367 void SetExpectations() {
368 NativeLoaderTest::SetExpectations();
369
370 ON_CALL(*mock, JniObject_getParent(StrEq(class_loader))).WillByDefault(Return(nullptr));
371
Martin Stjernholm48297332019-11-12 21:21:32 +0000372 EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(testing::AnyNumber());
373 EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(testing::AnyNumber());
Orion Hodson9b16e342019-10-09 13:29:16 +0100374
375 EXPECT_CALL(*mock, mock_create_namespace(
376 Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
377 StrEq(expected_library_path), expected_namespace_flags,
378 StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
379 .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
380 if (expected_link_with_platform_ns) {
381 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("platform"),
382 StrEq(expected_shared_libs_to_platform_ns)))
383 .WillOnce(Return(true));
384 }
385 if (expected_link_with_art_ns) {
386 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("art"),
387 StrEq(expected_shared_libs_to_art_ns)))
388 .WillOnce(Return(true));
389 }
390 if (expected_link_with_sphal_ns) {
391 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("sphal"),
392 StrEq(expected_shared_libs_to_sphal_ns)))
393 .WillOnce(Return(true));
394 }
395 if (expected_link_with_vndk_ns) {
396 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("vndk"),
397 StrEq(expected_shared_libs_to_vndk_ns)))
398 .WillOnce(Return(true));
399 }
400 if (expected_link_with_default_ns) {
401 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("default"),
402 StrEq(expected_shared_libs_to_default_ns)))
403 .WillOnce(Return(true));
404 }
405 if (expected_link_with_neuralnetworks_ns) {
406 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("neuralnetworks"),
407 StrEq(expected_shared_libs_to_neuralnetworks_ns)))
408 .WillOnce(Return(true));
409 }
Luke Huang5c017722019-12-17 10:54:26 +0800410 if (expected_link_with_cronet_ns) {
411 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("cronet"),
412 StrEq(expected_shared_libs_to_cronet_ns)))
413 .WillOnce(Return(true));
414 }
Orion Hodson9b16e342019-10-09 13:29:16 +0100415 }
416
417 void RunTest() {
418 NativeLoaderTest::RunTest();
419
420 jstring err = CreateClassLoaderNamespace(
421 env(), target_sdk_version, env()->NewStringUTF(class_loader.c_str()), is_shared,
422 env()->NewStringUTF(dex_path.c_str()), env()->NewStringUTF(library_path.c_str()),
423 env()->NewStringUTF(permitted_path.c_str()));
424
425 // no error
Martin Stjernholm94fd9ea2019-10-24 16:57:34 +0100426 EXPECT_EQ(err, nullptr) << "Error is: " << std::string(ScopedUtfChars(env(), err).c_str());
Orion Hodson9b16e342019-10-09 13:29:16 +0100427
428 if (!IsBridged()) {
429 struct android_namespace_t* ns =
430 FindNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
431
432 // The created namespace is for this apk
433 EXPECT_EQ(dex_path.c_str(), reinterpret_cast<const char*>(ns));
434 } else {
435 struct NativeLoaderNamespace* ns =
436 FindNativeLoaderNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
437
438 // The created namespace is for the this apk
439 EXPECT_STREQ(dex_path.c_str(),
440 reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
441 }
442 }
443
444 JNIEnv* env() { return NativeLoaderTest::env.get(); }
445};
446
447TEST_P(NativeLoaderTest_Create, DownloadedApp) {
448 SetExpectations();
449 RunTest();
450}
451
452TEST_P(NativeLoaderTest_Create, BundledSystemApp) {
453 dex_path = "/system/app/foo/foo.apk";
454 is_shared = true;
455
Martin Stjernholm94fd9ea2019-10-24 16:57:34 +0100456 expected_namespace_name = "classloader-namespace-shared";
Orion Hodson9b16e342019-10-09 13:29:16 +0100457 expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
458 SetExpectations();
459 RunTest();
460}
461
462TEST_P(NativeLoaderTest_Create, BundledVendorApp) {
463 dex_path = "/vendor/app/foo/foo.apk";
464 is_shared = true;
465
Martin Stjernholm94fd9ea2019-10-24 16:57:34 +0100466 expected_namespace_name = "classloader-namespace-shared";
Orion Hodson9b16e342019-10-09 13:29:16 +0100467 expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
468 SetExpectations();
469 RunTest();
470}
471
472TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) {
473 dex_path = "/vendor/app/foo/foo.apk";
474 is_shared = false;
475
476 expected_namespace_name = "vendor-classloader-namespace";
Martin Stjernholmbe08b202019-11-12 20:11:00 +0000477 expected_library_path = expected_library_path + ":/vendor/" LIB_DIR;
478 expected_permitted_path = expected_permitted_path + ":/vendor/" LIB_DIR;
Orion Hodson9b16e342019-10-09 13:29:16 +0100479 expected_shared_libs_to_platform_ns =
480 expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
481 expected_link_with_vndk_ns = true;
482 SetExpectations();
483 RunTest();
484}
485
Justin Yun3db26d52019-12-16 14:09:39 +0900486TEST_P(NativeLoaderTest_Create, BundledProductApp) {
Orion Hodson9b16e342019-10-09 13:29:16 +0100487 dex_path = "/product/app/foo/foo.apk";
488 is_shared = true;
489
Martin Stjernholm94fd9ea2019-10-24 16:57:34 +0100490 expected_namespace_name = "classloader-namespace-shared";
Orion Hodson9b16e342019-10-09 13:29:16 +0100491 expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
492 SetExpectations();
493 RunTest();
494}
495
Justin Yun3db26d52019-12-16 14:09:39 +0900496TEST_P(NativeLoaderTest_Create, UnbundledProductApp) {
Orion Hodson9b16e342019-10-09 13:29:16 +0100497 dex_path = "/product/app/foo/foo.apk";
498 is_shared = false;
Orion Hodson9b16e342019-10-09 13:29:16 +0100499
Justin Yun3db26d52019-12-16 14:09:39 +0900500 if (is_product_vndk_version_defined()) {
501 expected_namespace_name = "vendor-classloader-namespace";
502 expected_library_path = expected_library_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
503 expected_permitted_path =
504 expected_permitted_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
505 expected_shared_libs_to_platform_ns =
506 expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
507 expected_link_with_vndk_ns = true;
508 }
Orion Hodson9b16e342019-10-09 13:29:16 +0100509 SetExpectations();
510 RunTest();
511}
512
513TEST_P(NativeLoaderTest_Create, NamespaceForSharedLibIsNotUsedAsAnonymousNamespace) {
514 if (IsBridged()) {
515 // There is no shared lib in translated arch
516 // TODO(jiyong): revisit this
517 return;
518 }
519 // compared to apks, for java shared libs, library_path is empty; java shared
520 // libs don't have their own native libs. They use platform's.
521 library_path = "";
522 expected_library_path = library_path;
523 // no ALSO_USED_AS_ANONYMOUS
524 expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
525 SetExpectations();
526 RunTest();
527}
528
529TEST_P(NativeLoaderTest_Create, TwoApks) {
530 SetExpectations();
531 const uint32_t second_app_target_sdk_version = 29;
532 const std::string second_app_class_loader = "second_app_classloader";
533 const bool second_app_is_shared = false;
534 const std::string second_app_dex_path = "/data/app/bar/classes.dex";
Martin Stjernholmbe08b202019-11-12 20:11:00 +0000535 const std::string second_app_library_path = "/data/app/bar/" LIB_DIR "/arm";
536 const std::string second_app_permitted_path = "/data/app/bar/" LIB_DIR;
Orion Hodson9b16e342019-10-09 13:29:16 +0100537 const std::string expected_second_app_permitted_path =
538 std::string("/data:/mnt/expand:") + second_app_permitted_path;
539 const std::string expected_second_app_parent_namespace = "classloader-namespace";
540 // no ALSO_USED_AS_ANONYMOUS
541 const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
542
543 // The scenario is that second app is loaded by the first app.
544 // So the first app's classloader (`classloader`) is parent of the second
545 // app's classloader.
546 ON_CALL(*mock, JniObject_getParent(StrEq(second_app_class_loader)))
547 .WillByDefault(Return(class_loader.c_str()));
548
549 // namespace for the second app is created. Its parent is set to the namespace
550 // of the first app.
551 EXPECT_CALL(*mock, mock_create_namespace(
552 Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
553 StrEq(second_app_library_path), expected_second_namespace_flags,
554 StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str())))
555 .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
556 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), NsEq(second_app_dex_path.c_str()), _, _))
557 .WillRepeatedly(Return(true));
558
559 RunTest();
560 jstring err = CreateClassLoaderNamespace(
561 env(), second_app_target_sdk_version, env()->NewStringUTF(second_app_class_loader.c_str()),
562 second_app_is_shared, env()->NewStringUTF(second_app_dex_path.c_str()),
563 env()->NewStringUTF(second_app_library_path.c_str()),
564 env()->NewStringUTF(second_app_permitted_path.c_str()));
565
566 // success
Martin Stjernholm94fd9ea2019-10-24 16:57:34 +0100567 EXPECT_EQ(err, nullptr) << "Error is: " << std::string(ScopedUtfChars(env(), err).c_str());
Orion Hodson9b16e342019-10-09 13:29:16 +0100568
569 if (!IsBridged()) {
570 struct android_namespace_t* ns =
571 FindNamespaceByClassLoader(env(), env()->NewStringUTF(second_app_class_loader.c_str()));
572
573 // The created namespace is for the second apk
574 EXPECT_EQ(second_app_dex_path.c_str(), reinterpret_cast<const char*>(ns));
575 } else {
576 struct NativeLoaderNamespace* ns = FindNativeLoaderNamespaceByClassLoader(
577 env(), env()->NewStringUTF(second_app_class_loader.c_str()));
578
579 // The created namespace is for the second apk
580 EXPECT_STREQ(second_app_dex_path.c_str(),
581 reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
582 }
583}
584
585INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
586
587const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
588 [](const struct ConfigEntry&) -> Result<bool> { return true; };
589
590TEST(NativeLoaderConfigParser, NamesAndComments) {
591 const char file_content[] = R"(
592######
593
594libA.so
595#libB.so
596
597
598 libC.so
599libD.so
600 #### libE.so
601)";
602 const std::vector<std::string> expected_result = {"libA.so", "libC.so", "libD.so"};
603 Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
604 ASSERT_TRUE(result) << result.error().message();
605 ASSERT_EQ(expected_result, *result);
606}
607
608TEST(NativeLoaderConfigParser, WithBitness) {
609 const char file_content[] = R"(
610libA.so 32
611libB.so 64
612libC.so
613)";
614#if defined(__LP64__)
615 const std::vector<std::string> expected_result = {"libB.so", "libC.so"};
616#else
617 const std::vector<std::string> expected_result = {"libA.so", "libC.so"};
618#endif
619 Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
620 ASSERT_TRUE(result) << result.error().message();
621 ASSERT_EQ(expected_result, *result);
622}
623
624TEST(NativeLoaderConfigParser, WithNoPreload) {
625 const char file_content[] = R"(
626libA.so nopreload
627libB.so nopreload
628libC.so
629)";
630
631 const std::vector<std::string> expected_result = {"libC.so"};
632 Result<std::vector<std::string>> result =
633 ParseConfig(file_content,
634 [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
635 ASSERT_TRUE(result) << result.error().message();
636 ASSERT_EQ(expected_result, *result);
637}
638
639TEST(NativeLoaderConfigParser, WithNoPreloadAndBitness) {
640 const char file_content[] = R"(
641libA.so nopreload 32
642libB.so 64 nopreload
643libC.so 32
644libD.so 64
645libE.so nopreload
646)";
647
648#if defined(__LP64__)
649 const std::vector<std::string> expected_result = {"libD.so"};
650#else
651 const std::vector<std::string> expected_result = {"libC.so"};
652#endif
653 Result<std::vector<std::string>> result =
654 ParseConfig(file_content,
655 [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
656 ASSERT_TRUE(result) << result.error().message();
657 ASSERT_EQ(expected_result, *result);
658}
659
660TEST(NativeLoaderConfigParser, RejectMalformed) {
661 ASSERT_FALSE(ParseConfig("libA.so 32 64", always_true));
662 ASSERT_FALSE(ParseConfig("libA.so 32 32", always_true));
663 ASSERT_FALSE(ParseConfig("libA.so 32 nopreload 64", always_true));
664 ASSERT_FALSE(ParseConfig("32 libA.so nopreload", always_true));
665 ASSERT_FALSE(ParseConfig("nopreload libA.so 32", always_true));
666 ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true));
667}
668
669} // namespace nativeloader
670} // namespace android