dlext_test.cpp revision 07e5bc152d8a3ad4c50808bb86f3c0f2c5e2f514
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 "dlext_test.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(reinterpret_cast<void*>(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(reinterpret_cast<void*>(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(reinterpret_cast<void*>(f) < start || 224 (reinterpret_cast<void*>(f) >= 225 reinterpret_cast<char*>(start) + PAGE_SIZE)); 226 EXPECT_EQ(4, f()); 227} 228 229class DlExtRelroSharingTest : public DlExtTest { 230protected: 231 virtual void SetUp() { 232 DlExtTest::SetUp(); 233 void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 234 -1, 0); 235 ASSERT_TRUE(start != MAP_FAILED); 236 extinfo_.flags = ANDROID_DLEXT_RESERVED_ADDRESS; 237 extinfo_.reserved_addr = start; 238 extinfo_.reserved_size = LIBSIZE; 239 extinfo_.relro_fd = -1; 240 241 const char* android_data = getenv("ANDROID_DATA"); 242 ASSERT_TRUE(android_data != nullptr); 243 snprintf(relro_file_, sizeof(relro_file_), "%s/local/tmp/libdlext_test.relro", android_data); 244 } 245 246 virtual void TearDown() { 247 DlExtTest::TearDown(); 248 if (extinfo_.relro_fd != -1) { 249 ASSERT_NOERROR(close(extinfo_.relro_fd)); 250 } 251 } 252 253 void CreateRelroFile(const char* lib) { 254 int relro_fd = open(relro_file_, O_CREAT | O_RDWR | O_TRUNC, 0644); 255 ASSERT_NOERROR(relro_fd); 256 257 pid_t pid = fork(); 258 if (pid == 0) { 259 // child process 260 extinfo_.flags |= ANDROID_DLEXT_WRITE_RELRO; 261 extinfo_.relro_fd = relro_fd; 262 void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); 263 if (handle == nullptr) { 264 fprintf(stderr, "in child: %s\n", dlerror()); 265 exit(1); 266 } 267 exit(0); 268 } 269 270 // continuing in parent 271 ASSERT_NOERROR(close(relro_fd)); 272 ASSERT_NOERROR(pid); 273 int status; 274 ASSERT_EQ(pid, waitpid(pid, &status, 0)); 275 ASSERT_TRUE(WIFEXITED(status)); 276 ASSERT_EQ(0, WEXITSTATUS(status)); 277 278 // reopen file for reading so it can be used 279 relro_fd = open(relro_file_, O_RDONLY); 280 ASSERT_NOERROR(relro_fd); 281 extinfo_.flags |= ANDROID_DLEXT_USE_RELRO; 282 extinfo_.relro_fd = relro_fd; 283 } 284 285 void TryUsingRelro(const char* lib) { 286 handle_ = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); 287 ASSERT_DL_NOTNULL(handle_); 288 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 289 ASSERT_DL_NOTNULL(f); 290 EXPECT_EQ(4, f()); 291 } 292 293 void SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, size_t* pss_out); 294 295 android_dlextinfo extinfo_; 296 char relro_file_[PATH_MAX]; 297}; 298 299TEST_F(DlExtRelroSharingTest, ChildWritesGoodData) { 300 ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME)); 301 ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME)); 302} 303 304TEST_F(DlExtRelroSharingTest, ChildWritesNoRelro) { 305 ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME_NORELRO)); 306 ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME_NORELRO)); 307} 308 309TEST_F(DlExtRelroSharingTest, RelroFileEmpty) { 310 int relro_fd = open(relro_file_, O_CREAT | O_RDWR | O_TRUNC, 0644); 311 ASSERT_NOERROR(relro_fd); 312 ASSERT_NOERROR(close(relro_fd)); 313 314 ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME)); 315} 316 317TEST_F(DlExtRelroSharingTest, VerifyMemorySaving) { 318 if (geteuid() != 0) { 319 GTEST_LOG_(INFO) << "This test must be run as root.\n"; 320 return; 321 } 322 323 ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME)); 324 int relro_fd = open(relro_file_, O_RDONLY); 325 ASSERT_NOERROR(relro_fd); 326 extinfo_.flags |= ANDROID_DLEXT_USE_RELRO; 327 extinfo_.relro_fd = relro_fd; 328 int pipefd[2]; 329 ASSERT_NOERROR(pipe(pipefd)); 330 331 size_t without_sharing, with_sharing; 332 ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, false, &without_sharing)); 333 ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, true, &with_sharing)); 334 335 // We expect the sharing to save at least 10% of the total PSS. In practice 336 // it saves 40%+ for this test. 337 size_t expected_size = without_sharing - (without_sharing/10); 338 EXPECT_LT(with_sharing, expected_size); 339} 340 341void getPss(pid_t pid, size_t* pss_out) { 342 pm_kernel_t* kernel; 343 ASSERT_EQ(0, pm_kernel_create(&kernel)); 344 345 pm_process_t* process; 346 ASSERT_EQ(0, pm_process_create(kernel, pid, &process)); 347 348 pm_map_t** maps; 349 size_t num_maps; 350 ASSERT_EQ(0, pm_process_maps(process, &maps, &num_maps)); 351 352 size_t total_pss = 0; 353 for (size_t i = 0; i < num_maps; i++) { 354 pm_memusage_t usage; 355 ASSERT_EQ(0, pm_map_usage(maps[i], &usage)); 356 total_pss += usage.pss; 357 } 358 *pss_out = total_pss; 359 360 free(maps); 361 pm_process_destroy(process); 362 pm_kernel_destroy(kernel); 363} 364 365void DlExtRelroSharingTest::SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, 366 size_t* pss_out) { 367 const int CHILDREN = 20; 368 369 // Create children 370 pid_t childpid[CHILDREN]; 371 int childpipe[CHILDREN]; 372 for (int i=0; i<CHILDREN; ++i) { 373 char read_buf; 374 int child_done_pipe[2], parent_done_pipe[2]; 375 ASSERT_NOERROR(pipe(child_done_pipe)); 376 ASSERT_NOERROR(pipe(parent_done_pipe)); 377 378 pid_t child = fork(); 379 if (child == 0) { 380 // close the 'wrong' ends of the pipes in the child 381 close(child_done_pipe[0]); 382 close(parent_done_pipe[1]); 383 384 // open the library 385 void* handle; 386 if (share_relro) { 387 handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); 388 } else { 389 handle = dlopen(lib, RTLD_NOW); 390 } 391 if (handle == nullptr) { 392 fprintf(stderr, "in child: %s\n", dlerror()); 393 exit(1); 394 } 395 396 // close write end of child_done_pipe to signal the parent that we're done. 397 close(child_done_pipe[1]); 398 399 // wait for the parent to close parent_done_pipe, then exit 400 read(parent_done_pipe[0], &read_buf, 1); 401 exit(0); 402 } 403 404 ASSERT_NOERROR(child); 405 406 // close the 'wrong' ends of the pipes in the parent 407 close(child_done_pipe[1]); 408 close(parent_done_pipe[0]); 409 410 // wait for the child to be done 411 read(child_done_pipe[0], &read_buf, 1); 412 close(child_done_pipe[0]); 413 414 // save the child's pid and the parent_done_pipe 415 childpid[i] = child; 416 childpipe[i] = parent_done_pipe[1]; 417 } 418 419 // Sum the PSS of all the children 420 size_t total_pss = 0; 421 for (int i=0; i<CHILDREN; ++i) { 422 size_t child_pss; 423 ASSERT_NO_FATAL_FAILURE(getPss(childpid[i], &child_pss)); 424 total_pss += child_pss; 425 } 426 *pss_out = total_pss; 427 428 // Close pipes and wait for children to exit 429 for (int i=0; i<CHILDREN; ++i) { 430 ASSERT_NOERROR(close(childpipe[i])); 431 } 432 for (int i=0; i<CHILDREN; ++i) { 433 int status; 434 ASSERT_EQ(childpid[i], waitpid(childpid[i], &status, 0)); 435 ASSERT_TRUE(WIFEXITED(status)); 436 ASSERT_EQ(0, WEXITSTATUS(status)); 437 } 438} 439