• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 != NULL) << "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 
43 typedef 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 "%s/nativetest64/libdlext_test_fd/libdlext_test_fd.so"
50 #else
51 #define LIBPATH "%s/nativetest/libdlext_test_fd/libdlext_test_fd.so"
52 #endif
53 
54 class DlExtTest : public ::testing::Test {
55 protected:
SetUp()56   virtual void SetUp() {
57     handle_ = NULL;
58     // verify that we don't have the library loaded already
59     ASSERT_EQ(NULL, dlsym(RTLD_DEFAULT, "getRandomNumber"));
60     // call dlerror() to swallow the error, and check it was the one we wanted
61     ASSERT_STREQ("undefined symbol: getRandomNumber", dlerror());
62   }
63 
TearDown()64   virtual void TearDown() {
65     if (handle_ != NULL) {
66       ASSERT_DL_ZERO(dlclose(handle_));
67     }
68   }
69 
70   void* handle_;
71 };
72 
TEST_F(DlExtTest,ExtInfoNull)73 TEST_F(DlExtTest, ExtInfoNull) {
74   handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, NULL);
75   ASSERT_DL_NOTNULL(handle_);
76   fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
77   ASSERT_DL_NOTNULL(f);
78   EXPECT_EQ(4, f());
79 }
80 
TEST_F(DlExtTest,ExtInfoNoFlags)81 TEST_F(DlExtTest, ExtInfoNoFlags) {
82   android_dlextinfo extinfo;
83   extinfo.flags = 0;
84   handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
85   ASSERT_DL_NOTNULL(handle_);
86   fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
87   ASSERT_DL_NOTNULL(f);
88   EXPECT_EQ(4, f());
89 }
90 
TEST_F(DlExtTest,ExtInfoUseFd)91 TEST_F(DlExtTest, ExtInfoUseFd) {
92   const char* android_data = getenv("ANDROID_DATA");
93   ASSERT_TRUE(android_data != NULL);
94   char lib_path[PATH_MAX];
95   snprintf(lib_path, sizeof(lib_path), LIBPATH, android_data);
96 
97   android_dlextinfo extinfo;
98   extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD;
99   extinfo.library_fd = TEMP_FAILURE_RETRY(open(lib_path, O_RDONLY | O_CLOEXEC));
100   ASSERT_TRUE(extinfo.library_fd != -1);
101   handle_ = android_dlopen_ext(lib_path, RTLD_NOW, &extinfo);
102   ASSERT_DL_NOTNULL(handle_);
103   fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
104   ASSERT_DL_NOTNULL(f);
105   EXPECT_EQ(4, f());
106 }
107 
TEST_F(DlExtTest,Reserved)108 TEST_F(DlExtTest, Reserved) {
109   void* start = mmap(NULL, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
110                      -1, 0);
111   ASSERT_TRUE(start != MAP_FAILED);
112   android_dlextinfo extinfo;
113   extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
114   extinfo.reserved_addr = start;
115   extinfo.reserved_size = LIBSIZE;
116   handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
117   ASSERT_DL_NOTNULL(handle_);
118   fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
119   ASSERT_DL_NOTNULL(f);
120   EXPECT_GE(f, start);
121   EXPECT_LT(reinterpret_cast<void*>(f),
122             reinterpret_cast<char*>(start) + LIBSIZE);
123   EXPECT_EQ(4, f());
124 }
125 
TEST_F(DlExtTest,ReservedTooSmall)126 TEST_F(DlExtTest, ReservedTooSmall) {
127   void* start = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
128                      -1, 0);
129   ASSERT_TRUE(start != MAP_FAILED);
130   android_dlextinfo extinfo;
131   extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
132   extinfo.reserved_addr = start;
133   extinfo.reserved_size = PAGE_SIZE;
134   handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
135   EXPECT_EQ(NULL, handle_);
136 }
137 
TEST_F(DlExtTest,ReservedHint)138 TEST_F(DlExtTest, ReservedHint) {
139   void* start = mmap(NULL, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
140                      -1, 0);
141   ASSERT_TRUE(start != MAP_FAILED);
142   android_dlextinfo extinfo;
143   extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT;
144   extinfo.reserved_addr = start;
145   extinfo.reserved_size = LIBSIZE;
146   handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
147   ASSERT_DL_NOTNULL(handle_);
148   fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
149   ASSERT_DL_NOTNULL(f);
150   EXPECT_GE(f, start);
151   EXPECT_LT(reinterpret_cast<void*>(f),
152             reinterpret_cast<char*>(start) + LIBSIZE);
153   EXPECT_EQ(4, f());
154 }
155 
TEST_F(DlExtTest,ReservedHintTooSmall)156 TEST_F(DlExtTest, ReservedHintTooSmall) {
157   void* start = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
158                      -1, 0);
159   ASSERT_TRUE(start != MAP_FAILED);
160   android_dlextinfo extinfo;
161   extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT;
162   extinfo.reserved_addr = start;
163   extinfo.reserved_size = PAGE_SIZE;
164   handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
165   ASSERT_DL_NOTNULL(handle_);
166   fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
167   ASSERT_DL_NOTNULL(f);
168   EXPECT_TRUE(f < start || (reinterpret_cast<void*>(f) >=
169                             reinterpret_cast<char*>(start) + PAGE_SIZE));
170   EXPECT_EQ(4, f());
171 }
172 
173 class DlExtRelroSharingTest : public DlExtTest {
174 protected:
SetUp()175   virtual void SetUp() {
176     DlExtTest::SetUp();
177     void* start = mmap(NULL, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
178                        -1, 0);
179     ASSERT_TRUE(start != MAP_FAILED);
180     extinfo_.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
181     extinfo_.reserved_addr = start;
182     extinfo_.reserved_size = LIBSIZE;
183     extinfo_.relro_fd = -1;
184 
185     const char* android_data = getenv("ANDROID_DATA");
186     ASSERT_TRUE(android_data != NULL);
187     snprintf(relro_file_, sizeof(relro_file_), "%s/local/tmp/libdlext_test.relro", android_data);
188   }
189 
TearDown()190   virtual void TearDown() {
191     DlExtTest::TearDown();
192     if (extinfo_.relro_fd != -1) {
193       ASSERT_NOERROR(close(extinfo_.relro_fd));
194     }
195   }
196 
CreateRelroFile(const char * lib)197   void CreateRelroFile(const char* lib) {
198     int relro_fd = open(relro_file_, O_CREAT | O_RDWR | O_TRUNC, 0644);
199     ASSERT_NOERROR(relro_fd);
200 
201     pid_t pid = fork();
202     if (pid == 0) {
203       // child process
204       extinfo_.flags |= ANDROID_DLEXT_WRITE_RELRO;
205       extinfo_.relro_fd = relro_fd;
206       void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_);
207       if (handle == NULL) {
208         fprintf(stderr, "in child: %s\n", dlerror());
209         exit(1);
210       }
211       exit(0);
212     }
213 
214     // continuing in parent
215     ASSERT_NOERROR(close(relro_fd));
216     ASSERT_NOERROR(pid);
217     int status;
218     ASSERT_EQ(pid, waitpid(pid, &status, 0));
219     ASSERT_TRUE(WIFEXITED(status));
220     ASSERT_EQ(0, WEXITSTATUS(status));
221 
222     // reopen file for reading so it can be used
223     relro_fd = open(relro_file_, O_RDONLY);
224     ASSERT_NOERROR(relro_fd);
225     extinfo_.flags |= ANDROID_DLEXT_USE_RELRO;
226     extinfo_.relro_fd = relro_fd;
227   }
228 
TryUsingRelro(const char * lib)229   void TryUsingRelro(const char* lib) {
230     handle_ = android_dlopen_ext(lib, RTLD_NOW, &extinfo_);
231     ASSERT_DL_NOTNULL(handle_);
232     fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
233     ASSERT_DL_NOTNULL(f);
234     EXPECT_EQ(4, f());
235   }
236 
237   void SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, size_t* pss_out);
238 
239   android_dlextinfo extinfo_;
240   char relro_file_[PATH_MAX];
241 };
242 
TEST_F(DlExtRelroSharingTest,ChildWritesGoodData)243 TEST_F(DlExtRelroSharingTest, ChildWritesGoodData) {
244   ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME));
245   ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME));
246 }
247 
TEST_F(DlExtRelroSharingTest,ChildWritesNoRelro)248 TEST_F(DlExtRelroSharingTest, ChildWritesNoRelro) {
249   ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME_NORELRO));
250   ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME_NORELRO));
251 }
252 
TEST_F(DlExtRelroSharingTest,RelroFileEmpty)253 TEST_F(DlExtRelroSharingTest, RelroFileEmpty) {
254   int relro_fd = open(relro_file_, O_CREAT | O_RDWR | O_TRUNC, 0644);
255   ASSERT_NOERROR(relro_fd);
256   ASSERT_NOERROR(close(relro_fd));
257 
258   ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME));
259 }
260 
TEST_F(DlExtRelroSharingTest,VerifyMemorySaving)261 TEST_F(DlExtRelroSharingTest, VerifyMemorySaving) {
262   ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME));
263   int relro_fd = open(relro_file_, O_RDONLY);
264   ASSERT_NOERROR(relro_fd);
265   extinfo_.flags |= ANDROID_DLEXT_USE_RELRO;
266   extinfo_.relro_fd = relro_fd;
267   int pipefd[2];
268   ASSERT_NOERROR(pipe(pipefd));
269 
270   size_t without_sharing, with_sharing;
271   ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, false, &without_sharing));
272   ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, true, &with_sharing));
273 
274   // We expect the sharing to save at least 10% of the total PSS. In practice
275   // it saves 40%+ for this test.
276   size_t expected_size = without_sharing - (without_sharing/10);
277   EXPECT_LT(with_sharing, expected_size);
278 }
279 
getPss(pid_t pid,size_t * pss_out)280 void getPss(pid_t pid, size_t* pss_out) {
281   pm_kernel_t* kernel;
282   ASSERT_EQ(0, pm_kernel_create(&kernel));
283 
284   pm_process_t* process;
285   ASSERT_EQ(0, pm_process_create(kernel, pid, &process));
286 
287   pm_map_t** maps;
288   size_t num_maps;
289   ASSERT_EQ(0, pm_process_maps(process, &maps, &num_maps));
290 
291   size_t total_pss = 0;
292   for (size_t i = 0; i < num_maps; i++) {
293     pm_memusage_t usage;
294     ASSERT_EQ(0, pm_map_usage(maps[i], &usage));
295     total_pss += usage.pss;
296   }
297   *pss_out = total_pss;
298 
299   free(maps);
300   pm_process_destroy(process);
301   pm_kernel_destroy(kernel);
302 }
303 
SpawnChildrenAndMeasurePss(const char * lib,bool share_relro,size_t * pss_out)304 void DlExtRelroSharingTest::SpawnChildrenAndMeasurePss(const char* lib, bool share_relro,
305                                                        size_t* pss_out) {
306   const int CHILDREN = 20;
307 
308   // Create children
309   pid_t childpid[CHILDREN];
310   int childpipe[CHILDREN];
311   for (int i=0; i<CHILDREN; ++i) {
312     char read_buf;
313     int child_done_pipe[2], parent_done_pipe[2];
314     ASSERT_NOERROR(pipe(child_done_pipe));
315     ASSERT_NOERROR(pipe(parent_done_pipe));
316 
317     pid_t child = fork();
318     if (child == 0) {
319       // close the 'wrong' ends of the pipes in the child
320       close(child_done_pipe[0]);
321       close(parent_done_pipe[1]);
322 
323       // open the library
324       void* handle;
325       if (share_relro) {
326         handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_);
327       } else {
328         handle = dlopen(lib, RTLD_NOW);
329       }
330       if (handle == NULL) {
331         fprintf(stderr, "in child: %s\n", dlerror());
332         exit(1);
333       }
334 
335       // close write end of child_done_pipe to signal the parent that we're done.
336       close(child_done_pipe[1]);
337 
338       // wait for the parent to close parent_done_pipe, then exit
339       read(parent_done_pipe[0], &read_buf, 1);
340       exit(0);
341     }
342 
343     ASSERT_NOERROR(child);
344 
345     // close the 'wrong' ends of the pipes in the parent
346     close(child_done_pipe[1]);
347     close(parent_done_pipe[0]);
348 
349     // wait for the child to be done
350     read(child_done_pipe[0], &read_buf, 1);
351     close(child_done_pipe[0]);
352 
353     // save the child's pid and the parent_done_pipe
354     childpid[i] = child;
355     childpipe[i] = parent_done_pipe[1];
356   }
357 
358   // Sum the PSS of all the children
359   size_t total_pss = 0;
360   for (int i=0; i<CHILDREN; ++i) {
361     size_t child_pss;
362     ASSERT_NO_FATAL_FAILURE(getPss(childpid[i], &child_pss));
363     total_pss += child_pss;
364   }
365   *pss_out = total_pss;
366 
367   // Close pipes and wait for children to exit
368   for (int i=0; i<CHILDREN; ++i) {
369     ASSERT_NOERROR(close(childpipe[i]));
370   }
371   for (int i=0; i<CHILDREN; ++i) {
372     int status;
373     ASSERT_EQ(childpid[i], waitpid(childpid[i], &status, 0));
374     ASSERT_TRUE(WIFEXITED(status));
375     ASSERT_EQ(0, WEXITSTATUS(status));
376   }
377 }
378