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