xref: /aosp_15_r20/system/libprocinfo/process_map_test.cpp (revision e7c5e80fc9b28c04f5db9de8d2855377d05126c5)
1 /*
2  * Copyright (C) 2018 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 <procinfo/process_map.h>
18 
19 #include <inttypes.h>
20 #include <sys/mman.h>
21 #include <unistd.h>
22 
23 #include <string>
24 #include <vector>
25 
26 #include <android-base/file.h>
27 #include <android-base/stringprintf.h>
28 
29 #include <gtest/gtest.h>
30 
31 using android::procinfo::MapInfo;
32 
TEST(process_map,ReadMapFile)33 TEST(process_map, ReadMapFile) {
34   std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
35   std::vector<android::procinfo::MapInfo> maps;
36   ASSERT_TRUE(android::procinfo::ReadMapFile(
37       map_file, [&](const android::procinfo::MapInfo& mapinfo) { maps.emplace_back(mapinfo); }));
38   ASSERT_EQ(2043u, maps.size());
39   ASSERT_EQ(maps[0].start, 0x12c00000ULL);
40   ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
41   ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
42   ASSERT_EQ(maps[0].pgoff, 0ULL);
43   ASSERT_EQ(maps[0].inode, 10267643UL);
44   ASSERT_EQ(maps[0].name, "[anon:dalvik-main space (region space)]");
45   ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
46   ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
47   ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
48   ASSERT_EQ(maps[876].pgoff, 0ULL);
49   ASSERT_EQ(maps[876].inode, 2407UL);
50   ASSERT_EQ(maps[876].name, "/system/lib64/libutils.so");
51   ASSERT_EQ(maps[1260].start, 0x70e96fa000ULL);
52   ASSERT_EQ(maps[1260].end, 0x70e96fb000ULL);
53   ASSERT_EQ(maps[1260].flags, PROT_READ);
54   ASSERT_EQ(maps[1260].pgoff, 0ULL);
55   ASSERT_EQ(maps[1260].inode, 10266154UL);
56   ASSERT_EQ(maps[1260].name,
57             "[anon:dalvik-classes.dex extracted in memory from "
58             "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
59 }
60 
TEST(process_map,ReadProcessMaps)61 TEST(process_map, ReadProcessMaps) {
62   std::vector<android::procinfo::MapInfo> maps;
63   ASSERT_TRUE(android::procinfo::ReadProcessMaps(
64       getpid(), [&](const android::procinfo::MapInfo& mapinfo) { maps.emplace_back(mapinfo); }));
65   ASSERT_GT(maps.size(), 0u);
66   maps.clear();
67   ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
68   ASSERT_GT(maps.size(), 0u);
69 }
70 
71 extern "C" void malloc_disable();
72 extern "C" void malloc_enable();
73 
74 struct TestMapInfo {
75   TestMapInfo() = default;
TestMapInfoTestMapInfo76   TestMapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
77               const char* new_name, bool isShared)
78       : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode), isShared(isShared) {
79     strcpy(name, new_name);
80   }
81   uint64_t start = 0;
82   uint64_t end = 0;
83   uint16_t flags = 0;
84   uint64_t pgoff = 0;
85   ino_t inode = 0;
86   char name[100] = {};
87   bool isShared = false;
88 };
89 
VerifyReadMapFileAsyncSafe(const char * maps_data,const std::vector<TestMapInfo> & expected_info)90 void VerifyReadMapFileAsyncSafe(const char* maps_data,
91                                 const std::vector<TestMapInfo>& expected_info) {
92   TemporaryFile tf;
93   ASSERT_TRUE(android::base::WriteStringToFd(maps_data, tf.fd));
94 
95   std::vector<TestMapInfo> saved_info(expected_info.size());
96   size_t num_maps = 0;
97 
98   auto callback = [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
99                       const char* name, bool shared) {
100     if (num_maps != saved_info.size()) {
101       TestMapInfo& saved = saved_info[num_maps];
102       saved.start = start;
103       saved.end = end;
104       saved.flags = flags;
105       saved.pgoff = pgoff;
106       saved.inode = inode;
107       strcpy(saved.name, name);
108       saved.isShared = shared;
109     }
110     num_maps++;
111   };
112 
113   std::vector<char> buffer(64 * 1024);
114 
115 #if defined(__BIONIC__)
116   // Any allocations will block after this call.
117   malloc_disable();
118 #endif
119 
120   bool parsed =
121       android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer.data(), buffer.size(), callback);
122 
123 #if defined(__BIONIC__)
124   malloc_enable();
125 #endif
126 
127   ASSERT_TRUE(parsed) << "Parsing of data failed:\n" << maps_data;
128   ASSERT_EQ(expected_info.size(), num_maps);
129   for (size_t i = 0; i < expected_info.size(); i++) {
130     const TestMapInfo& expected = expected_info[i];
131     const TestMapInfo& saved = saved_info[i];
132     EXPECT_EQ(expected.start, saved.start);
133     EXPECT_EQ(expected.end, saved.end);
134     EXPECT_EQ(expected.flags, saved.flags);
135     EXPECT_EQ(expected.pgoff, saved.pgoff);
136     EXPECT_EQ(expected.inode, saved.inode);
137     EXPECT_STREQ(expected.name, saved.name);
138     EXPECT_EQ(expected.isShared, saved.isShared);
139   }
140 }
141 
TEST(process_map,ReadMapFileAsyncSafe_invalid)142 TEST(process_map, ReadMapFileAsyncSafe_invalid) {
143   std::vector<TestMapInfo> expected_info;
144 
145   VerifyReadMapFileAsyncSafe("12c00000-2ac00000", expected_info);
146 }
147 
TEST(process_map,ReadMapFileAsyncSafe_single)148 TEST(process_map, ReadMapFileAsyncSafe_single) {
149   std::vector<TestMapInfo> expected_info;
150   expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
151                              "/lib/fake.so", false);
152 
153   VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so",
154                              expected_info);
155 }
156 
TEST(process_map,ReadMapFileAsyncSafe_single_with_newline)157 TEST(process_map, ReadMapFileAsyncSafe_single_with_newline) {
158   std::vector<TestMapInfo> expected_info;
159   expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
160                              "/lib/fake.so", false);
161 
162   VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so\n",
163                              expected_info);
164 }
165 
TEST(process_map,ReadMapFileAsyncSafe_single_no_library)166 TEST(process_map, ReadMapFileAsyncSafe_single_no_library) {
167   std::vector<TestMapInfo> expected_info;
168   expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 0xb00, 101, "",
169                              false);
170 
171   VerifyReadMapFileAsyncSafe("a0000-c0000 rwxp 00000b00 00:05 101", expected_info);
172 }
173 
TEST(process_map,ReadMapFileAsyncSafe_multiple)174 TEST(process_map, ReadMapFileAsyncSafe_multiple) {
175   std::vector<TestMapInfo> expected_info;
176   expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 1, 100, "",
177                              false);
178   expected_info.emplace_back(0xd0000, 0xe0000, PROT_READ, 2, 101, "/lib/libsomething1.so", false);
179   expected_info.emplace_back(0xf0000, 0x100000, PROT_WRITE, 3, 102, "/lib/libsomething2.so", false);
180   expected_info.emplace_back(0x110000, 0x120000, PROT_EXEC, 4, 103, "[anon:something or another]",
181                              false);
182   expected_info.emplace_back(0x130000, 0x140000, PROT_READ, 5, 104, "/lib/libsomething3.so", true);
183 
184   std::string map_data =
185       "0a0000-0c0000 rwxp 00000001 00:05 100\n"
186       "0d0000-0e0000 r--p 00000002 00:05 101  /lib/libsomething1.so\n"
187       "0f0000-100000 -w-p 00000003 00:05 102  /lib/libsomething2.so\n"
188       "110000-120000 --xp 00000004 00:05 103  [anon:something or another]\n"
189       "130000-140000 r--s 00000005 00:05 104  /lib/libsomething3.so\n";
190 
191   VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
192 }
193 
TEST(process_map,ReadMapFileAsyncSafe_multiple_reads)194 TEST(process_map, ReadMapFileAsyncSafe_multiple_reads) {
195   std::vector<TestMapInfo> expected_info;
196   std::string map_data;
197   uint64_t start = 0xa0000;
198   for (size_t i = 0; i < 10000; i++) {
199     map_data += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r--p %zx 01:20 %zu fake.so\n",
200                                             start, start + 0x1000, i, 1000 + i);
201     expected_info.emplace_back(start, start + 0x1000, PROT_READ, i, 1000 + i, "fake.so", false);
202   }
203 
204   VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
205 }
206 
TEST(process_map,ReadMapFileAsyncSafe_buffer_nullptr)207 TEST(process_map, ReadMapFileAsyncSafe_buffer_nullptr) {
208   size_t num_calls = 0;
209   auto callback = [&](const android::procinfo::MapInfo&) { num_calls++; };
210 
211 #if defined(__BIONIC__)
212   // Any allocations will block after this call.
213   malloc_disable();
214 #endif
215 
216   bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", nullptr, 10, callback);
217 
218 #if defined(__BIONIC__)
219   malloc_enable();
220 #endif
221 
222   ASSERT_FALSE(parsed);
223   EXPECT_EQ(0UL, num_calls);
224 }
225 
TEST(process_map,ReadMapFileAsyncSafe_buffer_size_zero)226 TEST(process_map, ReadMapFileAsyncSafe_buffer_size_zero) {
227   size_t num_calls = 0;
228   auto callback = [&](const android::procinfo::MapInfo&) { num_calls++; };
229 
230 #if defined(__BIONIC__)
231   // Any allocations will block after this call.
232   malloc_disable();
233 #endif
234 
235   char buffer[10];
236   bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, 0, callback);
237 
238 #if defined(__BIONIC__)
239   malloc_enable();
240 #endif
241 
242   ASSERT_FALSE(parsed);
243   EXPECT_EQ(0UL, num_calls);
244 }
245 
TEST(process_map,ReadMapFileAsyncSafe_buffer_too_small_no_calls)246 TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_no_calls) {
247   size_t num_calls = 0;
248   auto callback = [&](const android::procinfo::MapInfo&) { num_calls++; };
249 
250 #if defined(__BIONIC__)
251   // Any allocations will block after this call.
252   malloc_disable();
253 #endif
254 
255   char buffer[10];
256   bool parsed =
257       android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, sizeof(buffer), callback);
258 
259 #if defined(__BIONIC__)
260   malloc_enable();
261 #endif
262 
263   ASSERT_FALSE(parsed);
264   EXPECT_EQ(0UL, num_calls);
265 }
266 
TEST(process_map,ReadMapFileAsyncSafe_buffer_too_small_could_parse)267 TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_could_parse) {
268   TemporaryFile tf;
269   ASSERT_TRUE(android::base::WriteStringToFd(
270       "0a0000-0c0000 rwxp 00000001 00:05 100    /fake/lib.so\n", tf.fd));
271 
272   size_t num_calls = 0;
273   auto callback = [&](const android::procinfo::MapInfo&) { num_calls++; };
274 
275 #if defined(__BIONIC__)
276   // Any allocations will block after this call.
277   malloc_disable();
278 #endif
279 
280   char buffer[39];
281   bool parsed = android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer, sizeof(buffer), callback);
282 
283 #if defined(__BIONIC__)
284   malloc_enable();
285 #endif
286 
287   ASSERT_FALSE(parsed);
288   EXPECT_EQ(0UL, num_calls);
289 }
290 
291 class ProcessMapMappedFileSize : public ::testing::Test {
292   protected:
SetUp()293     void SetUp() override {
294       ASSERT_NE(tf.fd, -1) << "open failed: " << strerror(errno);
295       ASSERT_EQ(ftruncate(tf.fd, kFileSize), 0) << "ftruncate failed: " << strerror(errno);
296     }
297 
TearDown()298     void TearDown() override {
299       ASSERT_EQ(munmap(reinterpret_cast<void*>(map.start), map.end-map.start), 0)
300                 << "munmap failed: " << strerror(errno);
301     }
302 
PageAlign(uint64_t x)303     uint64_t PageAlign(uint64_t x) {
304       const uint64_t kPageSize = getpagesize();
305       return (x + kPageSize - 1) & ~(kPageSize - 1);
306     }
307 
CreateFileMapping(uint64_t size,uint64_t offset)308     bool CreateFileMapping(uint64_t size, uint64_t offset) {
309       void *addr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, tf.fd, offset);
310       if (addr == MAP_FAILED) {
311         return false;
312       }
313 
314       map.start = reinterpret_cast<uint64_t>(addr);
315       map.end = PageAlign(map.start + size);
316       map.pgoff = offset;
317 
318       return true;
319     }
320 
321     TemporaryFile tf;
322     const size_t kFileSize = 65536;
323     android::procinfo::MapInfo map = android::procinfo::MapInfo(0 /* start */, 0 /* end */,
324                                                                 PROT_READ, 0 /* pgoff */,
325                                                                 0 /* inode */, tf.path, false);
326 };
327 
TEST_F(ProcessMapMappedFileSize,map_size_greater_than_file_size)328 TEST_F(ProcessMapMappedFileSize, map_size_greater_than_file_size) {
329   uint64_t size = 2 * kFileSize;
330   uint64_t offset = 0;
331 
332   ASSERT_TRUE(CreateFileMapping(size, offset));
333   uint64_t mapped_file_size = android::procinfo::MappedFileSize(map);
334 
335   ASSERT_EQ(mapped_file_size, kFileSize);
336 }
337 
TEST_F(ProcessMapMappedFileSize,map_size_less_than_file_size)338 TEST_F(ProcessMapMappedFileSize, map_size_less_than_file_size) {
339   uint64_t size = kFileSize / 2;
340   uint64_t offset = 0;
341 
342   ASSERT_TRUE(CreateFileMapping(size, offset));
343   uint64_t mapped_file_size = android::procinfo::MappedFileSize(map);
344 
345   ASSERT_EQ(mapped_file_size, size);
346 }
347 
TEST_F(ProcessMapMappedFileSize,map_size_equal_file_size)348 TEST_F(ProcessMapMappedFileSize, map_size_equal_file_size) {
349   uint64_t size = kFileSize;
350   uint64_t offset = 0;
351 
352   ASSERT_TRUE(CreateFileMapping(size, offset));
353   uint64_t mapped_file_size = android::procinfo::MappedFileSize(map);
354 
355   ASSERT_EQ(mapped_file_size, kFileSize);
356 
357 }
358 
TEST_F(ProcessMapMappedFileSize,offset_greater_than_file_size)359 TEST_F(ProcessMapMappedFileSize, offset_greater_than_file_size) {
360   uint64_t size = kFileSize;
361   uint64_t offset = kFileSize * 2;
362 
363   ASSERT_TRUE(CreateFileMapping(size, offset));
364   uint64_t mapped_file_size = android::procinfo::MappedFileSize(map);
365 
366   ASSERT_EQ(mapped_file_size, 0UL);
367 }
368 
TEST_F(ProcessMapMappedFileSize,invalid_map_name)369 TEST_F(ProcessMapMappedFileSize, invalid_map_name) {
370   uint64_t size = kFileSize;
371   uint64_t offset = 0;
372 
373   ASSERT_TRUE(CreateFileMapping(size, offset));
374 
375   // Name is empty
376   map.name = "";
377   uint64_t mapped_file_size = android::procinfo::MappedFileSize(map);
378   ASSERT_EQ(mapped_file_size, 0UL);
379 
380   // Is device path
381   map.name = "/dev/";
382   mapped_file_size = android::procinfo::MappedFileSize(map);
383   ASSERT_EQ(mapped_file_size, 0UL);
384 
385   // Does not start with '/'
386   map.name = "[anon:bss]";
387   mapped_file_size = android::procinfo::MappedFileSize(map);
388   ASSERT_EQ(mapped_file_size, 0UL);
389 
390   // File non-existent
391   map.name = "/tmp/non_existent_file";
392   mapped_file_size = android::procinfo::MappedFileSize(map);
393   ASSERT_EQ(mapped_file_size, 0UL);
394 }
395 
CreateMapWithOnlyName(const char * name)396 static MapInfo CreateMapWithOnlyName(const char* name) {
397   return MapInfo(0, 0, 0, UINT64_MAX, 0, name, false);
398 }
399 
TEST(process_map,TaggedMappingNames)400 TEST(process_map, TaggedMappingNames) {
401   MapInfo info = CreateMapWithOnlyName(
402       "[anon:mt:/data/local/tmp/debuggerd_test/arm64/debuggerd_test64+108000]");
403   ASSERT_EQ(info.name, "/data/local/tmp/debuggerd_test/arm64/debuggerd_test64");
404   ASSERT_EQ(info.pgoff, 0x108000ull);
405 
406   info = CreateMapWithOnlyName("[anon:mt:/data/local/tmp/debuggerd_test/arm64/debuggerd_test64+0]");
407   ASSERT_EQ(info.name, "/data/local/tmp/debuggerd_test/arm64/debuggerd_test64");
408   ASSERT_EQ(info.pgoff, 0x0ull);
409 
410   info =
411       CreateMapWithOnlyName("[anon:mt:/data/local/tmp/debuggerd_test/arm64/debuggerd_test64+0000]");
412   ASSERT_EQ(info.name, "/data/local/tmp/debuggerd_test/arm64/debuggerd_test64");
413   ASSERT_EQ(info.pgoff, 0x0ull);
414 
415   info = CreateMapWithOnlyName(
416       "[anon:mt:...ivetest64/bionic-unit-tests/bionic-loader-test-libs/libdlext_test.so+e000]");
417   ASSERT_EQ(info.name, "...ivetest64/bionic-unit-tests/bionic-loader-test-libs/libdlext_test.so");
418   ASSERT_EQ(info.pgoff, 0xe000ull);
419 
420   info = CreateMapWithOnlyName("[anon:mt:/bin/x+e000]");
421   ASSERT_EQ(info.name, "/bin/x");
422   ASSERT_EQ(info.pgoff, 0xe000ull);
423 
424   info = CreateMapWithOnlyName("[anon:mt:/bin/x+0]");
425   ASSERT_EQ(info.name, "/bin/x");
426   ASSERT_EQ(info.pgoff, 0x0ull);
427 
428   info = CreateMapWithOnlyName("[anon:mt:/bin/x+1]");
429   ASSERT_EQ(info.name, "/bin/x");
430   ASSERT_EQ(info.pgoff, 0x1ull);
431 
432   info = CreateMapWithOnlyName("[anon:mt:/bin/x+f]");
433   ASSERT_EQ(info.name, "/bin/x");
434   ASSERT_EQ(info.pgoff, 0xfull);
435 
436   info = CreateMapWithOnlyName("[anon:mt:/bin/with/plus+/x+f]");
437   ASSERT_EQ(info.name, "/bin/with/plus+/x");
438   ASSERT_EQ(info.pgoff, 0xfull);
439 
440   info = CreateMapWithOnlyName("[anon:mt:/bin/+with/mu+ltiple/plus+/x+f]");
441   ASSERT_EQ(info.name, "/bin/+with/mu+ltiple/plus+/x");
442   ASSERT_EQ(info.pgoff, 0xfull);
443 
444   info = CreateMapWithOnlyName("[anon:mt:/bin/trailing/plus++f]");
445   ASSERT_EQ(info.name, "/bin/trailing/plus+");
446   ASSERT_EQ(info.pgoff, 0xfull);
447 
448   info = CreateMapWithOnlyName("[anon:mt:++f]");
449   ASSERT_EQ(info.name, "+");
450   ASSERT_EQ(info.pgoff, 0xfull);
451 }
452 
TEST(process_map,AlmostTaggedMappingNames)453 TEST(process_map, AlmostTaggedMappingNames) {
454   for (const char* almost_tagged_name :
455        {"[anon:mt:/bin/x+]",
456         "[anon:mt:/bin/x]"
457         "[anon:mt:+]",
458         "[anon:mt", "[anon:mt:/bin/x+1", "[anon:mt:/bin/x+e000",
459         "anon:mt:/data/local/tmp/debuggerd_test/arm64/debuggerd_test64+e000]"}) {
460     MapInfo info = CreateMapWithOnlyName(almost_tagged_name);
461     ASSERT_EQ(info.name, almost_tagged_name);
462     ASSERT_EQ(info.pgoff, UINT64_MAX) << almost_tagged_name;
463   }
464 }
465