1 | //===-- sanitizer_procmaps_test.cpp ---------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of ThreadSanitizer/AddressSanitizer runtime. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | #if !defined(_WIN32) // There are no /proc/maps on Windows. |
13 | |
14 | # include "sanitizer_common/sanitizer_procmaps.h" |
15 | |
16 | # include <stdlib.h> |
17 | # include <string.h> |
18 | |
19 | # include <vector> |
20 | |
21 | # include "gtest/gtest.h" |
22 | |
23 | static void noop() {} |
24 | extern const char *argv0; |
25 | |
26 | namespace __sanitizer { |
27 | |
28 | # if SANITIZER_LINUX && !SANITIZER_ANDROID |
29 | TEST(MemoryMappingLayout, CodeRange) { |
30 | uptr start, end; |
31 | bool res = GetCodeRangeForFile(module: "[vdso]" , start: &start, end: &end); |
32 | EXPECT_EQ(res, true); |
33 | EXPECT_GT(start, 0U); |
34 | EXPECT_LT(start, end); |
35 | } |
36 | # endif |
37 | |
38 | TEST(MemoryMappingLayout, DumpListOfModules) { |
39 | const char *last_slash = strrchr(s: argv0, c: '/'); |
40 | const char *binary_name = last_slash ? last_slash + 1 : argv0; |
41 | MemoryMappingLayout memory_mapping(false); |
42 | const uptr kMaxModules = 100; |
43 | InternalMmapVector<LoadedModule> modules; |
44 | modules.reserve(new_size: kMaxModules); |
45 | memory_mapping.DumpListOfModules(modules: &modules); |
46 | EXPECT_GT(modules.size(), 0U); |
47 | bool found = false; |
48 | for (uptr i = 0; i < modules.size(); ++i) { |
49 | if (modules[i].containsAddress(address: (uptr)&noop)) { |
50 | // Verify that the module name is sane. |
51 | if (strstr(haystack: modules[i].full_name(), needle: binary_name) != 0) |
52 | found = true; |
53 | } |
54 | modules[i].clear(); |
55 | } |
56 | EXPECT_TRUE(found); |
57 | } |
58 | |
59 | TEST(MemoryMapping, LoadedModuleArchAndUUID) { |
60 | if (SANITIZER_APPLE) { |
61 | MemoryMappingLayout memory_mapping(false); |
62 | const uptr kMaxModules = 100; |
63 | InternalMmapVector<LoadedModule> modules; |
64 | modules.reserve(new_size: kMaxModules); |
65 | memory_mapping.DumpListOfModules(modules: &modules); |
66 | for (uptr i = 0; i < modules.size(); ++i) { |
67 | ModuleArch arch = modules[i].arch(); |
68 | // Darwin unit tests are only run on i386/x86_64/x86_64h/arm64. |
69 | if (SANITIZER_WORDSIZE == 32) { |
70 | EXPECT_EQ(arch, kModuleArchI386); |
71 | } else if (SANITIZER_WORDSIZE == 64) { |
72 | EXPECT_TRUE(arch == kModuleArchX86_64 || arch == kModuleArchX86_64H || |
73 | arch == kModuleArchARM64); |
74 | } |
75 | const u8 *uuid = modules[i].uuid(); |
76 | u8 null_uuid[kModuleUUIDSize] = {0}; |
77 | EXPECT_NE(memcmp(s1: null_uuid, s2: uuid, n: kModuleUUIDSize), 0); |
78 | } |
79 | } |
80 | } |
81 | |
82 | # if (SANITIZER_LINUX || SANITIZER_NETBSD || SANITIZER_SOLARIS) && \ |
83 | defined(_LP64) |
84 | const char *const parse_unix_input = R"( |
85 | 7fb9862f1000-7fb9862f3000 rw-p 00000000 00:00 0 |
86 | Size: 8 kB |
87 | Rss: 4 kB |
88 | 7fb9864ae000-7fb9864b1000 r--p 001ba000 fd:01 22413919 /lib/x86_64-linux-gnu/libc-2.32.so |
89 | Size: 12 kB |
90 | Rss: 12 kB |
91 | )" ; |
92 | |
93 | TEST(MemoryMapping, ParseUnixMemoryProfile) { |
94 | struct entry { |
95 | uptr p; |
96 | uptr rss; |
97 | bool file; |
98 | }; |
99 | typedef std::vector<entry> entries_t; |
100 | entries_t entries; |
101 | std::vector<char> input(parse_unix_input, |
102 | parse_unix_input + strlen(parse_unix_input)); |
103 | ParseUnixMemoryProfile( |
104 | [](uptr p, uptr rss, bool file, uptr *mem) { |
105 | reinterpret_cast<entries_t *>(mem)->push_back({p, rss, file}); |
106 | }, |
107 | reinterpret_cast<uptr *>(&entries), &input[0], input.size()); |
108 | EXPECT_EQ(entries.size(), 2ul); |
109 | EXPECT_EQ(entries[0].p, 0x7fb9862f1000ul); |
110 | EXPECT_EQ(entries[0].rss, 4ul << 10); |
111 | EXPECT_EQ(entries[0].file, false); |
112 | EXPECT_EQ(entries[1].p, 0x7fb9864ae000ul); |
113 | EXPECT_EQ(entries[1].rss, 12ul << 10); |
114 | EXPECT_EQ(entries[1].file, true); |
115 | } |
116 | |
117 | TEST(MemoryMapping, ParseUnixMemoryProfileTruncated) { |
118 | // ParseUnixMemoryProfile used to crash on truncated inputs. |
119 | // This test allocates 2 pages, protects the second one |
120 | // and places the input at the very end of the first page |
121 | // to test for over-reads. |
122 | uptr page = GetPageSizeCached(); |
123 | char *mem = static_cast<char *>( |
124 | MmapOrDie(2 * page, "ParseUnixMemoryProfileTruncated" )); |
125 | EXPECT_TRUE(MprotectNoAccess(reinterpret_cast<uptr>(mem + page), page)); |
126 | const uptr len = strlen(parse_unix_input); |
127 | for (uptr i = 0; i < len; i++) { |
128 | char *smaps = mem + page - len + i; |
129 | memcpy(smaps, parse_unix_input, len - i); |
130 | ParseUnixMemoryProfile([](uptr p, uptr rss, bool file, uptr *mem) {}, |
131 | nullptr, smaps, len - i); |
132 | } |
133 | UnmapOrDie(mem, 2 * page); |
134 | } |
135 | # endif |
136 | |
137 | } // namespace __sanitizer |
138 | #endif // !defined(_WIN32) |
139 | |