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