dlext_test.cpp revision 3c5c720b0b46ecd801329c09d23bb6e7098d76d3
1/* 2 * Copyright (C) 2014 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 <gtest/gtest.h> 18 19#include <dlfcn.h> 20#include <errno.h> 21#include <fcntl.h> 22#include <stdio.h> 23#include <string.h> 24#include <unistd.h> 25#include <android/dlext.h> 26#include <sys/mman.h> 27#include <sys/types.h> 28#include <sys/wait.h> 29 30#include <pagemap/pagemap.h> 31 32 33#define ASSERT_DL_NOTNULL(ptr) \ 34 ASSERT_TRUE(ptr != nullptr) << "dlerror: " << dlerror() 35 36#define ASSERT_DL_ZERO(i) \ 37 ASSERT_EQ(0, i) << "dlerror: " << dlerror() 38 39#define ASSERT_NOERROR(i) \ 40 ASSERT_NE(-1, i) << "errno: " << strerror(errno) 41 42 43typedef int (*fn)(void); 44#define LIBNAME "libdlext_test.so" 45#define LIBNAME_NORELRO "libdlext_test_norelro.so" 46#define LIBSIZE 1024*1024 // how much address space to reserve for it 47 48#if defined(__LP64__) 49#define LIBPATH_PREFIX "%s/nativetest64/libdlext_test_fd/" 50#else 51#define LIBPATH_PREFIX "%s/nativetest/libdlext_test_fd/" 52#endif 53 54#define LIBPATH LIBPATH_PREFIX "libdlext_test_fd.so" 55#define LIBZIPPATH LIBPATH_PREFIX "libdlext_test_fd_zipaligned.zip" 56 57#define LIBZIP_OFFSET 2*PAGE_SIZE 58 59class DlExtTest : public ::testing::Test { 60protected: 61 virtual void SetUp() { 62 handle_ = nullptr; 63 // verify that we don't have the library loaded already 64 void* h = dlopen(LIBNAME, RTLD_NOW | RTLD_NOLOAD); 65 ASSERT_TRUE(h == nullptr); 66 h = dlopen(LIBNAME_NORELRO, RTLD_NOW | RTLD_NOLOAD); 67 ASSERT_TRUE(h == nullptr); 68 // call dlerror() to swallow the error, and check it was the one we wanted 69 ASSERT_STREQ("dlopen failed: library \"" LIBNAME_NORELRO "\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); 70 } 71 72 virtual void TearDown() { 73 if (handle_ != nullptr) { 74 ASSERT_DL_ZERO(dlclose(handle_)); 75 } 76 } 77 78 void* handle_; 79}; 80 81TEST_F(DlExtTest, ExtInfoNull) { 82 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, nullptr); 83 ASSERT_DL_NOTNULL(handle_); 84 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 85 ASSERT_DL_NOTNULL(f); 86 EXPECT_EQ(4, f()); 87} 88 89TEST_F(DlExtTest, ExtInfoNoFlags) { 90 android_dlextinfo extinfo; 91 extinfo.flags = 0; 92 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); 93 ASSERT_DL_NOTNULL(handle_); 94 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 95 ASSERT_DL_NOTNULL(f); 96 EXPECT_EQ(4, f()); 97} 98 99TEST_F(DlExtTest, ExtInfoUseFd) { 100 const char* android_data = getenv("ANDROID_DATA"); 101 ASSERT_TRUE(android_data != nullptr); 102 char lib_path[PATH_MAX]; 103 snprintf(lib_path, sizeof(lib_path), LIBPATH, android_data); 104 105 android_dlextinfo extinfo; 106 extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD; 107 extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC)); 108 ASSERT_TRUE(extinfo.library_fd != -1); 109 handle_ = android_dlopen_ext(lib_path, RTLD_NOW, &extinfo); 110 ASSERT_DL_NOTNULL(handle_); 111 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 112 ASSERT_DL_NOTNULL(f); 113 EXPECT_EQ(4, f()); 114} 115 116TEST_F(DlExtTest, ExtInfoUseFdWithOffset) { 117 const char* android_data = getenv("ANDROID_DATA"); 118 ASSERT_TRUE(android_data != nullptr); 119 120 char lib_path[PATH_MAX]; 121 snprintf(lib_path, sizeof(lib_path), LIBZIPPATH, android_data); 122 123 android_dlextinfo extinfo; 124 extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_OFFSET; 125 extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC)); 126 extinfo.library_offset = LIBZIP_OFFSET; 127 128 handle_ = android_dlopen_ext(lib_path, RTLD_NOW, &extinfo); 129 ASSERT_DL_NOTNULL(handle_); 130 131 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 132 ASSERT_DL_NOTNULL(f); 133 EXPECT_EQ(4, f()); 134} 135 136TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) { 137 const char* android_data = getenv("ANDROID_DATA"); 138 ASSERT_TRUE(android_data != nullptr); 139 140 char lib_path[PATH_MAX]; 141 snprintf(lib_path, sizeof(lib_path), LIBZIPPATH, android_data); 142 143 android_dlextinfo extinfo; 144 extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_OFFSET; 145 extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC)); 146 extinfo.library_offset = 17; 147 148 handle_ = android_dlopen_ext("libname_placeholder", RTLD_NOW, &extinfo); 149 ASSERT_TRUE(handle_ == nullptr); 150 ASSERT_STREQ("dlopen failed: file offset for the library libname_placeholder is not page-aligned: 17", dlerror()); 151} 152 153TEST_F(DlExtTest, ExtInfoUseOffsetWihtoutFd) { 154 android_dlextinfo extinfo; 155 extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_OFFSET; 156 extinfo.library_offset = LIBZIP_OFFSET; 157 158 handle_ = android_dlopen_ext("/some/lib/that/does_not_exist", RTLD_NOW, &extinfo); 159 ASSERT_TRUE(handle_ == nullptr); 160 ASSERT_STREQ("dlopen failed: invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_OFFSET without ANDROID_DLEXT_USE_LIBRARY_FD): 0x20", dlerror()); 161} 162 163TEST_F(DlExtTest, Reserved) { 164 void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 165 -1, 0); 166 ASSERT_TRUE(start != MAP_FAILED); 167 android_dlextinfo extinfo; 168 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS; 169 extinfo.reserved_addr = start; 170 extinfo.reserved_size = LIBSIZE; 171 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); 172 ASSERT_DL_NOTNULL(handle_); 173 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 174 ASSERT_DL_NOTNULL(f); 175 EXPECT_GE(f, start); 176 EXPECT_LT(reinterpret_cast<void*>(f), 177 reinterpret_cast<char*>(start) + LIBSIZE); 178 EXPECT_EQ(4, f()); 179} 180 181TEST_F(DlExtTest, ReservedTooSmall) { 182 void* start = mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 183 -1, 0); 184 ASSERT_TRUE(start != MAP_FAILED); 185 android_dlextinfo extinfo; 186 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS; 187 extinfo.reserved_addr = start; 188 extinfo.reserved_size = PAGE_SIZE; 189 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); 190 EXPECT_EQ(nullptr, handle_); 191} 192 193TEST_F(DlExtTest, ReservedHint) { 194 void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 195 -1, 0); 196 ASSERT_TRUE(start != MAP_FAILED); 197 android_dlextinfo extinfo; 198 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT; 199 extinfo.reserved_addr = start; 200 extinfo.reserved_size = LIBSIZE; 201 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); 202 ASSERT_DL_NOTNULL(handle_); 203 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 204 ASSERT_DL_NOTNULL(f); 205 EXPECT_GE(f, start); 206 EXPECT_LT(reinterpret_cast<void*>(f), 207 reinterpret_cast<char*>(start) + LIBSIZE); 208 EXPECT_EQ(4, f()); 209} 210 211TEST_F(DlExtTest, ReservedHintTooSmall) { 212 void* start = mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 213 -1, 0); 214 ASSERT_TRUE(start != MAP_FAILED); 215 android_dlextinfo extinfo; 216 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT; 217 extinfo.reserved_addr = start; 218 extinfo.reserved_size = PAGE_SIZE; 219 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); 220 ASSERT_DL_NOTNULL(handle_); 221 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 222 ASSERT_DL_NOTNULL(f); 223 EXPECT_TRUE(f < start || (reinterpret_cast<void*>(f) >= 224 reinterpret_cast<char*>(start) + PAGE_SIZE)); 225 EXPECT_EQ(4, f()); 226} 227 228class DlExtRelroSharingTest : public DlExtTest { 229protected: 230 virtual void SetUp() { 231 DlExtTest::SetUp(); 232 void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 233 -1, 0); 234 ASSERT_TRUE(start != MAP_FAILED); 235 extinfo_.flags = ANDROID_DLEXT_RESERVED_ADDRESS; 236 extinfo_.reserved_addr = start; 237 extinfo_.reserved_size = LIBSIZE; 238 extinfo_.relro_fd = -1; 239 240 const char* android_data = getenv("ANDROID_DATA"); 241 ASSERT_TRUE(android_data != nullptr); 242 snprintf(relro_file_, sizeof(relro_file_), "%s/local/tmp/libdlext_test.relro", android_data); 243 } 244 245 virtual void TearDown() { 246 DlExtTest::TearDown(); 247 if (extinfo_.relro_fd != -1) { 248 ASSERT_NOERROR(close(extinfo_.relro_fd)); 249 } 250 } 251 252 void CreateRelroFile(const char* lib) { 253 int relro_fd = open(relro_file_, O_CREAT | O_RDWR | O_TRUNC, 0644); 254 ASSERT_NOERROR(relro_fd); 255 256 pid_t pid = fork(); 257 if (pid == 0) { 258 // child process 259 extinfo_.flags |= ANDROID_DLEXT_WRITE_RELRO; 260 extinfo_.relro_fd = relro_fd; 261 void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); 262 if (handle == nullptr) { 263 fprintf(stderr, "in child: %s\n", dlerror()); 264 exit(1); 265 } 266 exit(0); 267 } 268 269 // continuing in parent 270 ASSERT_NOERROR(close(relro_fd)); 271 ASSERT_NOERROR(pid); 272 int status; 273 ASSERT_EQ(pid, waitpid(pid, &status, 0)); 274 ASSERT_TRUE(WIFEXITED(status)); 275 ASSERT_EQ(0, WEXITSTATUS(status)); 276 277 // reopen file for reading so it can be used 278 relro_fd = open(relro_file_, O_RDONLY); 279 ASSERT_NOERROR(relro_fd); 280 extinfo_.flags |= ANDROID_DLEXT_USE_RELRO; 281 extinfo_.relro_fd = relro_fd; 282 } 283 284 void TryUsingRelro(const char* lib) { 285 handle_ = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); 286 ASSERT_DL_NOTNULL(handle_); 287 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 288 ASSERT_DL_NOTNULL(f); 289 EXPECT_EQ(4, f()); 290 } 291 292 void SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, size_t* pss_out); 293 294 android_dlextinfo extinfo_; 295 char relro_file_[PATH_MAX]; 296}; 297 298TEST_F(DlExtRelroSharingTest, ChildWritesGoodData) { 299 ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME)); 300 ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME)); 301} 302 303TEST_F(DlExtRelroSharingTest, ChildWritesNoRelro) { 304 ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME_NORELRO)); 305 ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME_NORELRO)); 306} 307 308TEST_F(DlExtRelroSharingTest, RelroFileEmpty) { 309 int relro_fd = open(relro_file_, O_CREAT | O_RDWR | O_TRUNC, 0644); 310 ASSERT_NOERROR(relro_fd); 311 ASSERT_NOERROR(close(relro_fd)); 312 313 ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME)); 314} 315 316TEST_F(DlExtRelroSharingTest, VerifyMemorySaving) { 317 ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME)); 318 int relro_fd = open(relro_file_, O_RDONLY); 319 ASSERT_NOERROR(relro_fd); 320 extinfo_.flags |= ANDROID_DLEXT_USE_RELRO; 321 extinfo_.relro_fd = relro_fd; 322 int pipefd[2]; 323 ASSERT_NOERROR(pipe(pipefd)); 324 325 size_t without_sharing, with_sharing; 326 ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, false, &without_sharing)); 327 ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, true, &with_sharing)); 328 329 // We expect the sharing to save at least 10% of the total PSS. In practice 330 // it saves 40%+ for this test. 331 size_t expected_size = without_sharing - (without_sharing/10); 332 EXPECT_LT(with_sharing, expected_size); 333} 334 335void getPss(pid_t pid, size_t* pss_out) { 336 pm_kernel_t* kernel; 337 ASSERT_EQ(0, pm_kernel_create(&kernel)); 338 339 pm_process_t* process; 340 ASSERT_EQ(0, pm_process_create(kernel, pid, &process)); 341 342 pm_map_t** maps; 343 size_t num_maps; 344 ASSERT_EQ(0, pm_process_maps(process, &maps, &num_maps)); 345 346 size_t total_pss = 0; 347 for (size_t i = 0; i < num_maps; i++) { 348 pm_memusage_t usage; 349 ASSERT_EQ(0, pm_map_usage(maps[i], &usage)); 350 total_pss += usage.pss; 351 } 352 *pss_out = total_pss; 353 354 free(maps); 355 pm_process_destroy(process); 356 pm_kernel_destroy(kernel); 357} 358 359void DlExtRelroSharingTest::SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, 360 size_t* pss_out) { 361 const int CHILDREN = 20; 362 363 // Create children 364 pid_t childpid[CHILDREN]; 365 int childpipe[CHILDREN]; 366 for (int i=0; i<CHILDREN; ++i) { 367 char read_buf; 368 int child_done_pipe[2], parent_done_pipe[2]; 369 ASSERT_NOERROR(pipe(child_done_pipe)); 370 ASSERT_NOERROR(pipe(parent_done_pipe)); 371 372 pid_t child = fork(); 373 if (child == 0) { 374 // close the 'wrong' ends of the pipes in the child 375 close(child_done_pipe[0]); 376 close(parent_done_pipe[1]); 377 378 // open the library 379 void* handle; 380 if (share_relro) { 381 handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); 382 } else { 383 handle = dlopen(lib, RTLD_NOW); 384 } 385 if (handle == nullptr) { 386 fprintf(stderr, "in child: %s\n", dlerror()); 387 exit(1); 388 } 389 390 // close write end of child_done_pipe to signal the parent that we're done. 391 close(child_done_pipe[1]); 392 393 // wait for the parent to close parent_done_pipe, then exit 394 read(parent_done_pipe[0], &read_buf, 1); 395 exit(0); 396 } 397 398 ASSERT_NOERROR(child); 399 400 // close the 'wrong' ends of the pipes in the parent 401 close(child_done_pipe[1]); 402 close(parent_done_pipe[0]); 403 404 // wait for the child to be done 405 read(child_done_pipe[0], &read_buf, 1); 406 close(child_done_pipe[0]); 407 408 // save the child's pid and the parent_done_pipe 409 childpid[i] = child; 410 childpipe[i] = parent_done_pipe[1]; 411 } 412 413 // Sum the PSS of all the children 414 size_t total_pss = 0; 415 for (int i=0; i<CHILDREN; ++i) { 416 size_t child_pss; 417 ASSERT_NO_FATAL_FAILURE(getPss(childpid[i], &child_pss)); 418 total_pss += child_pss; 419 } 420 *pss_out = total_pss; 421 422 // Close pipes and wait for children to exit 423 for (int i=0; i<CHILDREN; ++i) { 424 ASSERT_NOERROR(close(childpipe[i])); 425 } 426 for (int i=0; i<CHILDREN; ++i) { 427 int status; 428 ASSERT_EQ(childpid[i], waitpid(childpid[i], &status, 0)); 429 ASSERT_TRUE(WIFEXITED(status)); 430 ASSERT_EQ(0, WEXITSTATUS(status)); 431 } 432} 433