dlext_test.cpp revision a6c1279098f24a675d0df74ce1946f5d534b425e
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_FD_OFFSET; 125 extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC)); 126 extinfo.library_fd_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_FD_OFFSET; 145 extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC)); 146 extinfo.library_fd_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 extinfo.library_fd_offset = (5LL<<58) + PAGE_SIZE; 153 handle_ = android_dlopen_ext("libname_placeholder", RTLD_NOW, &extinfo); 154 155 ASSERT_TRUE(handle_ == nullptr); 156 // TODO: Better error message when reading with offset > file_size 157 ASSERT_STREQ("dlopen failed: \"libname_placeholder\" has bad ELF magic", dlerror()); 158 159 close(extinfo.library_fd); 160} 161 162TEST_F(DlExtTest, ExtInfoUseOffsetWihtoutFd) { 163 android_dlextinfo extinfo; 164 extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET; 165 extinfo.library_fd_offset = LIBZIP_OFFSET; 166 167 handle_ = android_dlopen_ext("/some/lib/that/does_not_exist", RTLD_NOW, &extinfo); 168 ASSERT_TRUE(handle_ == nullptr); 169 ASSERT_STREQ("dlopen failed: invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without ANDROID_DLEXT_USE_LIBRARY_FD): 0x20", dlerror()); 170} 171 172TEST_F(DlExtTest, Reserved) { 173 void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 174 -1, 0); 175 ASSERT_TRUE(start != MAP_FAILED); 176 android_dlextinfo extinfo; 177 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS; 178 extinfo.reserved_addr = start; 179 extinfo.reserved_size = LIBSIZE; 180 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); 181 ASSERT_DL_NOTNULL(handle_); 182 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 183 ASSERT_DL_NOTNULL(f); 184 EXPECT_GE(reinterpret_cast<void*>(f), start); 185 EXPECT_LT(reinterpret_cast<void*>(f), 186 reinterpret_cast<char*>(start) + LIBSIZE); 187 EXPECT_EQ(4, f()); 188} 189 190TEST_F(DlExtTest, ReservedTooSmall) { 191 void* start = mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 192 -1, 0); 193 ASSERT_TRUE(start != MAP_FAILED); 194 android_dlextinfo extinfo; 195 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS; 196 extinfo.reserved_addr = start; 197 extinfo.reserved_size = PAGE_SIZE; 198 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); 199 EXPECT_EQ(nullptr, handle_); 200} 201 202TEST_F(DlExtTest, ReservedHint) { 203 void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 204 -1, 0); 205 ASSERT_TRUE(start != MAP_FAILED); 206 android_dlextinfo extinfo; 207 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT; 208 extinfo.reserved_addr = start; 209 extinfo.reserved_size = LIBSIZE; 210 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); 211 ASSERT_DL_NOTNULL(handle_); 212 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 213 ASSERT_DL_NOTNULL(f); 214 EXPECT_GE(reinterpret_cast<void*>(f), start); 215 EXPECT_LT(reinterpret_cast<void*>(f), 216 reinterpret_cast<char*>(start) + LIBSIZE); 217 EXPECT_EQ(4, f()); 218} 219 220TEST_F(DlExtTest, ReservedHintTooSmall) { 221 void* start = mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 222 -1, 0); 223 ASSERT_TRUE(start != MAP_FAILED); 224 android_dlextinfo extinfo; 225 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT; 226 extinfo.reserved_addr = start; 227 extinfo.reserved_size = PAGE_SIZE; 228 handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); 229 ASSERT_DL_NOTNULL(handle_); 230 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 231 ASSERT_DL_NOTNULL(f); 232 EXPECT_TRUE(reinterpret_cast<void*>(f) < start || 233 (reinterpret_cast<void*>(f) >= 234 reinterpret_cast<char*>(start) + PAGE_SIZE)); 235 EXPECT_EQ(4, f()); 236} 237 238class DlExtRelroSharingTest : public DlExtTest { 239protected: 240 virtual void SetUp() { 241 DlExtTest::SetUp(); 242 void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 243 -1, 0); 244 ASSERT_TRUE(start != MAP_FAILED); 245 extinfo_.flags = ANDROID_DLEXT_RESERVED_ADDRESS; 246 extinfo_.reserved_addr = start; 247 extinfo_.reserved_size = LIBSIZE; 248 extinfo_.relro_fd = -1; 249 250 const char* android_data = getenv("ANDROID_DATA"); 251 ASSERT_TRUE(android_data != nullptr); 252 snprintf(relro_file_, sizeof(relro_file_), "%s/local/tmp/libdlext_test.relro", android_data); 253 } 254 255 virtual void TearDown() { 256 DlExtTest::TearDown(); 257 if (extinfo_.relro_fd != -1) { 258 ASSERT_NOERROR(close(extinfo_.relro_fd)); 259 } 260 } 261 262 void CreateRelroFile(const char* lib) { 263 int relro_fd = open(relro_file_, O_CREAT | O_RDWR | O_TRUNC, 0644); 264 ASSERT_NOERROR(relro_fd); 265 266 pid_t pid = fork(); 267 if (pid == 0) { 268 // child process 269 extinfo_.flags |= ANDROID_DLEXT_WRITE_RELRO; 270 extinfo_.relro_fd = relro_fd; 271 void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); 272 if (handle == nullptr) { 273 fprintf(stderr, "in child: %s\n", dlerror()); 274 exit(1); 275 } 276 exit(0); 277 } 278 279 // continuing in parent 280 ASSERT_NOERROR(close(relro_fd)); 281 ASSERT_NOERROR(pid); 282 int status; 283 ASSERT_EQ(pid, waitpid(pid, &status, 0)); 284 ASSERT_TRUE(WIFEXITED(status)); 285 ASSERT_EQ(0, WEXITSTATUS(status)); 286 287 // reopen file for reading so it can be used 288 relro_fd = open(relro_file_, O_RDONLY); 289 ASSERT_NOERROR(relro_fd); 290 extinfo_.flags |= ANDROID_DLEXT_USE_RELRO; 291 extinfo_.relro_fd = relro_fd; 292 } 293 294 void TryUsingRelro(const char* lib) { 295 handle_ = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); 296 ASSERT_DL_NOTNULL(handle_); 297 fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); 298 ASSERT_DL_NOTNULL(f); 299 EXPECT_EQ(4, f()); 300 } 301 302 void SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, size_t* pss_out); 303 304 android_dlextinfo extinfo_; 305 char relro_file_[PATH_MAX]; 306}; 307 308TEST_F(DlExtRelroSharingTest, ChildWritesGoodData) { 309 ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME)); 310 ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME)); 311} 312 313TEST_F(DlExtRelroSharingTest, ChildWritesNoRelro) { 314 ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME_NORELRO)); 315 ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME_NORELRO)); 316} 317 318TEST_F(DlExtRelroSharingTest, RelroFileEmpty) { 319 int relro_fd = open(relro_file_, O_CREAT | O_RDWR | O_TRUNC, 0644); 320 ASSERT_NOERROR(relro_fd); 321 ASSERT_NOERROR(close(relro_fd)); 322 323 ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME)); 324} 325 326TEST_F(DlExtRelroSharingTest, VerifyMemorySaving) { 327 if (geteuid() != 0) { 328 GTEST_LOG_(INFO) << "This test must be run as root.\n"; 329 return; 330 } 331 332 ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME)); 333 int relro_fd = open(relro_file_, O_RDONLY); 334 ASSERT_NOERROR(relro_fd); 335 extinfo_.flags |= ANDROID_DLEXT_USE_RELRO; 336 extinfo_.relro_fd = relro_fd; 337 int pipefd[2]; 338 ASSERT_NOERROR(pipe(pipefd)); 339 340 size_t without_sharing, with_sharing; 341 ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, false, &without_sharing)); 342 ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, true, &with_sharing)); 343 344 // We expect the sharing to save at least 10% of the total PSS. In practice 345 // it saves 40%+ for this test. 346 size_t expected_size = without_sharing - (without_sharing/10); 347 EXPECT_LT(with_sharing, expected_size); 348} 349 350void getPss(pid_t pid, size_t* pss_out) { 351 pm_kernel_t* kernel; 352 ASSERT_EQ(0, pm_kernel_create(&kernel)); 353 354 pm_process_t* process; 355 ASSERT_EQ(0, pm_process_create(kernel, pid, &process)); 356 357 pm_map_t** maps; 358 size_t num_maps; 359 ASSERT_EQ(0, pm_process_maps(process, &maps, &num_maps)); 360 361 size_t total_pss = 0; 362 for (size_t i = 0; i < num_maps; i++) { 363 pm_memusage_t usage; 364 ASSERT_EQ(0, pm_map_usage(maps[i], &usage)); 365 total_pss += usage.pss; 366 } 367 *pss_out = total_pss; 368 369 free(maps); 370 pm_process_destroy(process); 371 pm_kernel_destroy(kernel); 372} 373 374void DlExtRelroSharingTest::SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, 375 size_t* pss_out) { 376 const int CHILDREN = 20; 377 378 // Create children 379 pid_t childpid[CHILDREN]; 380 int childpipe[CHILDREN]; 381 for (int i=0; i<CHILDREN; ++i) { 382 char read_buf; 383 int child_done_pipe[2], parent_done_pipe[2]; 384 ASSERT_NOERROR(pipe(child_done_pipe)); 385 ASSERT_NOERROR(pipe(parent_done_pipe)); 386 387 pid_t child = fork(); 388 if (child == 0) { 389 // close the 'wrong' ends of the pipes in the child 390 close(child_done_pipe[0]); 391 close(parent_done_pipe[1]); 392 393 // open the library 394 void* handle; 395 if (share_relro) { 396 handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); 397 } else { 398 handle = dlopen(lib, RTLD_NOW); 399 } 400 if (handle == nullptr) { 401 fprintf(stderr, "in child: %s\n", dlerror()); 402 exit(1); 403 } 404 405 // close write end of child_done_pipe to signal the parent that we're done. 406 close(child_done_pipe[1]); 407 408 // wait for the parent to close parent_done_pipe, then exit 409 read(parent_done_pipe[0], &read_buf, 1); 410 exit(0); 411 } 412 413 ASSERT_NOERROR(child); 414 415 // close the 'wrong' ends of the pipes in the parent 416 close(child_done_pipe[1]); 417 close(parent_done_pipe[0]); 418 419 // wait for the child to be done 420 read(child_done_pipe[0], &read_buf, 1); 421 close(child_done_pipe[0]); 422 423 // save the child's pid and the parent_done_pipe 424 childpid[i] = child; 425 childpipe[i] = parent_done_pipe[1]; 426 } 427 428 // Sum the PSS of all the children 429 size_t total_pss = 0; 430 for (int i=0; i<CHILDREN; ++i) { 431 size_t child_pss; 432 ASSERT_NO_FATAL_FAILURE(getPss(childpid[i], &child_pss)); 433 total_pss += child_pss; 434 } 435 *pss_out = total_pss; 436 437 // Close pipes and wait for children to exit 438 for (int i=0; i<CHILDREN; ++i) { 439 ASSERT_NOERROR(close(childpipe[i])); 440 } 441 for (int i=0; i<CHILDREN; ++i) { 442 int status; 443 ASSERT_EQ(childpid[i], waitpid(childpid[i], &status, 0)); 444 ASSERT_TRUE(WIFEXITED(status)); 445 ASSERT_EQ(0, WEXITSTATUS(status)); 446 } 447} 448