[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 1 | // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
avi | ebe805c | 2015-12-24 08:20:28 | [diff] [blame] | 5 | #include <stddef.h> |
| 6 | #include <stdint.h> |
| 7 | |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 8 | #include "base/debug/proc_maps_linux.h" |
| 9 | #include "base/files/file_path.h" |
avi | ebe805c | 2015-12-24 08:20:28 | [diff] [blame] | 10 | #include "base/macros.h" |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 11 | #include "base/path_service.h" |
| 12 | #include "base/strings/stringprintf.h" |
scherkus | aff972d | 2014-11-21 01:36:04 | [diff] [blame] | 13 | #include "base/threading/platform_thread.h" |
avi | ebe805c | 2015-12-24 08:20:28 | [diff] [blame] | 14 | #include "build/build_config.h" |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 15 | #include "testing/gtest/include/gtest/gtest.h" |
| 16 | |
| 17 | namespace base { |
| 18 | namespace debug { |
| 19 | |
| 20 | TEST(ProcMapsTest, Empty) { |
| 21 | std::vector<MappedMemoryRegion> regions; |
| 22 | EXPECT_TRUE(ParseProcMaps("", ®ions)); |
| 23 | EXPECT_EQ(0u, regions.size()); |
| 24 | } |
| 25 | |
| 26 | TEST(ProcMapsTest, NoSpaces) { |
| 27 | static const char kNoSpaces[] = |
| 28 | "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat\n"; |
| 29 | |
| 30 | std::vector<MappedMemoryRegion> regions; |
| 31 | ASSERT_TRUE(ParseProcMaps(kNoSpaces, ®ions)); |
| 32 | ASSERT_EQ(1u, regions.size()); |
| 33 | |
| 34 | EXPECT_EQ(0x00400000u, regions[0].start); |
| 35 | EXPECT_EQ(0x0040b000u, regions[0].end); |
| 36 | EXPECT_EQ(0x00002200u, regions[0].offset); |
| 37 | EXPECT_EQ("/bin/cat", regions[0].path); |
| 38 | } |
| 39 | |
| 40 | TEST(ProcMapsTest, Spaces) { |
| 41 | static const char kSpaces[] = |
| 42 | "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/space cat\n"; |
| 43 | |
| 44 | std::vector<MappedMemoryRegion> regions; |
| 45 | ASSERT_TRUE(ParseProcMaps(kSpaces, ®ions)); |
| 46 | ASSERT_EQ(1u, regions.size()); |
| 47 | |
| 48 | EXPECT_EQ(0x00400000u, regions[0].start); |
| 49 | EXPECT_EQ(0x0040b000u, regions[0].end); |
| 50 | EXPECT_EQ(0x00002200u, regions[0].offset); |
| 51 | EXPECT_EQ("/bin/space cat", regions[0].path); |
| 52 | } |
| 53 | |
| 54 | TEST(ProcMapsTest, NoNewline) { |
| 55 | static const char kNoSpaces[] = |
| 56 | "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat"; |
| 57 | |
| 58 | std::vector<MappedMemoryRegion> regions; |
| 59 | ASSERT_FALSE(ParseProcMaps(kNoSpaces, ®ions)); |
| 60 | } |
| 61 | |
| 62 | TEST(ProcMapsTest, NoPath) { |
| 63 | static const char kNoPath[] = |
| 64 | "00400000-0040b000 rw-p 00000000 00:00 0 \n"; |
| 65 | |
| 66 | std::vector<MappedMemoryRegion> regions; |
| 67 | ASSERT_TRUE(ParseProcMaps(kNoPath, ®ions)); |
| 68 | ASSERT_EQ(1u, regions.size()); |
| 69 | |
| 70 | EXPECT_EQ(0x00400000u, regions[0].start); |
| 71 | EXPECT_EQ(0x0040b000u, regions[0].end); |
| 72 | EXPECT_EQ(0x00000000u, regions[0].offset); |
| 73 | EXPECT_EQ("", regions[0].path); |
| 74 | } |
| 75 | |
| 76 | TEST(ProcMapsTest, Heap) { |
| 77 | static const char kHeap[] = |
| 78 | "022ac000-022cd000 rw-p 00000000 00:00 0 [heap]\n"; |
| 79 | |
| 80 | std::vector<MappedMemoryRegion> regions; |
| 81 | ASSERT_TRUE(ParseProcMaps(kHeap, ®ions)); |
| 82 | ASSERT_EQ(1u, regions.size()); |
| 83 | |
| 84 | EXPECT_EQ(0x022ac000u, regions[0].start); |
| 85 | EXPECT_EQ(0x022cd000u, regions[0].end); |
| 86 | EXPECT_EQ(0x00000000u, regions[0].offset); |
| 87 | EXPECT_EQ("[heap]", regions[0].path); |
| 88 | } |
| 89 | |
| 90 | #if defined(ARCH_CPU_32_BITS) |
| 91 | TEST(ProcMapsTest, Stack32) { |
| 92 | static const char kStack[] = |
| 93 | "beb04000-beb25000 rw-p 00000000 00:00 0 [stack]\n"; |
| 94 | |
| 95 | std::vector<MappedMemoryRegion> regions; |
| 96 | ASSERT_TRUE(ParseProcMaps(kStack, ®ions)); |
| 97 | ASSERT_EQ(1u, regions.size()); |
| 98 | |
| 99 | EXPECT_EQ(0xbeb04000u, regions[0].start); |
| 100 | EXPECT_EQ(0xbeb25000u, regions[0].end); |
| 101 | EXPECT_EQ(0x00000000u, regions[0].offset); |
| 102 | EXPECT_EQ("[stack]", regions[0].path); |
| 103 | } |
| 104 | #elif defined(ARCH_CPU_64_BITS) |
| 105 | TEST(ProcMapsTest, Stack64) { |
| 106 | static const char kStack[] = |
| 107 | "7fff69c5b000-7fff69c7d000 rw-p 00000000 00:00 0 [stack]\n"; |
| 108 | |
| 109 | std::vector<MappedMemoryRegion> regions; |
| 110 | ASSERT_TRUE(ParseProcMaps(kStack, ®ions)); |
| 111 | ASSERT_EQ(1u, regions.size()); |
| 112 | |
| 113 | EXPECT_EQ(0x7fff69c5b000u, regions[0].start); |
| 114 | EXPECT_EQ(0x7fff69c7d000u, regions[0].end); |
| 115 | EXPECT_EQ(0x00000000u, regions[0].offset); |
| 116 | EXPECT_EQ("[stack]", regions[0].path); |
| 117 | } |
| 118 | #endif |
| 119 | |
| 120 | TEST(ProcMapsTest, Multiple) { |
| 121 | static const char kMultiple[] = |
| 122 | "00400000-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n" |
| 123 | "0060a000-0060b000 r--p 0000a000 fc:00 794418 /bin/cat\n" |
| 124 | "0060b000-0060c000 rw-p 0000b000 fc:00 794418 /bin/cat\n"; |
| 125 | |
| 126 | std::vector<MappedMemoryRegion> regions; |
| 127 | ASSERT_TRUE(ParseProcMaps(kMultiple, ®ions)); |
| 128 | ASSERT_EQ(3u, regions.size()); |
| 129 | |
| 130 | EXPECT_EQ(0x00400000u, regions[0].start); |
| 131 | EXPECT_EQ(0x0040b000u, regions[0].end); |
| 132 | EXPECT_EQ(0x00000000u, regions[0].offset); |
| 133 | EXPECT_EQ("/bin/cat", regions[0].path); |
| 134 | |
| 135 | EXPECT_EQ(0x0060a000u, regions[1].start); |
| 136 | EXPECT_EQ(0x0060b000u, regions[1].end); |
| 137 | EXPECT_EQ(0x0000a000u, regions[1].offset); |
| 138 | EXPECT_EQ("/bin/cat", regions[1].path); |
| 139 | |
| 140 | EXPECT_EQ(0x0060b000u, regions[2].start); |
| 141 | EXPECT_EQ(0x0060c000u, regions[2].end); |
| 142 | EXPECT_EQ(0x0000b000u, regions[2].offset); |
| 143 | EXPECT_EQ("/bin/cat", regions[2].path); |
| 144 | } |
| 145 | |
| 146 | TEST(ProcMapsTest, Permissions) { |
| 147 | static struct { |
| 148 | const char* input; |
avi | ebe805c | 2015-12-24 08:20:28 | [diff] [blame] | 149 | uint8_t permissions; |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 150 | } kTestCases[] = { |
| 151 | {"00400000-0040b000 ---s 00000000 fc:00 794418 /bin/cat\n", 0}, |
| 152 | {"00400000-0040b000 ---S 00000000 fc:00 794418 /bin/cat\n", 0}, |
| 153 | {"00400000-0040b000 r--s 00000000 fc:00 794418 /bin/cat\n", |
| 154 | MappedMemoryRegion::READ}, |
| 155 | {"00400000-0040b000 -w-s 00000000 fc:00 794418 /bin/cat\n", |
| 156 | MappedMemoryRegion::WRITE}, |
| 157 | {"00400000-0040b000 --xs 00000000 fc:00 794418 /bin/cat\n", |
| 158 | MappedMemoryRegion::EXECUTE}, |
| 159 | {"00400000-0040b000 rwxs 00000000 fc:00 794418 /bin/cat\n", |
| 160 | MappedMemoryRegion::READ | MappedMemoryRegion::WRITE | |
| 161 | MappedMemoryRegion::EXECUTE}, |
| 162 | {"00400000-0040b000 ---p 00000000 fc:00 794418 /bin/cat\n", |
| 163 | MappedMemoryRegion::PRIVATE}, |
| 164 | {"00400000-0040b000 r--p 00000000 fc:00 794418 /bin/cat\n", |
| 165 | MappedMemoryRegion::READ | MappedMemoryRegion::PRIVATE}, |
| 166 | {"00400000-0040b000 -w-p 00000000 fc:00 794418 /bin/cat\n", |
| 167 | MappedMemoryRegion::WRITE | MappedMemoryRegion::PRIVATE}, |
| 168 | {"00400000-0040b000 --xp 00000000 fc:00 794418 /bin/cat\n", |
| 169 | MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE}, |
| 170 | {"00400000-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n", |
| 171 | MappedMemoryRegion::READ | MappedMemoryRegion::WRITE | |
| 172 | MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE}, |
| 173 | }; |
| 174 | |
viettrungluu | 805eabb | 2014-10-16 04:02:49 | [diff] [blame] | 175 | for (size_t i = 0; i < arraysize(kTestCases); ++i) { |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 176 | SCOPED_TRACE( |
| 177 | base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i].input)); |
| 178 | |
| 179 | std::vector<MappedMemoryRegion> regions; |
| 180 | EXPECT_TRUE(ParseProcMaps(kTestCases[i].input, ®ions)); |
| 181 | EXPECT_EQ(1u, regions.size()); |
| 182 | if (regions.empty()) |
| 183 | continue; |
| 184 | EXPECT_EQ(kTestCases[i].permissions, regions[0].permissions); |
| 185 | } |
| 186 | } |
| 187 | |
glider | fb699a7e | 2014-12-05 12:20:28 | [diff] [blame] | 188 | #if defined(ADDRESS_SANITIZER) |
| 189 | // AddressSanitizer may move local variables to a dedicated "fake stack" which |
| 190 | // is outside the stack region listed in /proc/self/maps. We disable ASan |
| 191 | // instrumentation for this function to force the variable to be local. |
| 192 | __attribute__((no_sanitize_address)) |
| 193 | #endif |
| 194 | void CheckProcMapsRegions(const std::vector<MappedMemoryRegion> ®ions) { |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 195 | // We should be able to find both the current executable as well as the stack |
glider | fb699a7e | 2014-12-05 12:20:28 | [diff] [blame] | 196 | // mapped into memory. Use the address of |exe_path| as a way of finding the |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 197 | // stack. |
| 198 | FilePath exe_path; |
| 199 | EXPECT_TRUE(PathService::Get(FILE_EXE, &exe_path)); |
glider | fb699a7e | 2014-12-05 12:20:28 | [diff] [blame] | 200 | uintptr_t address = reinterpret_cast<uintptr_t>(&exe_path); |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 201 | bool found_exe = false; |
| 202 | bool found_stack = false; |
[email protected] | fe7bc8df | 2013-07-04 06:06:09 | [diff] [blame] | 203 | bool found_address = false; |
scherkus | aff972d | 2014-11-21 01:36:04 | [diff] [blame] | 204 | |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 205 | for (size_t i = 0; i < regions.size(); ++i) { |
| 206 | if (regions[i].path == exe_path.value()) { |
[email protected] | fe7bc8df | 2013-07-04 06:06:09 | [diff] [blame] | 207 | // It's OK to find the executable mapped multiple times as there'll be |
| 208 | // multiple sections (e.g., text, data). |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 209 | found_exe = true; |
| 210 | } |
| 211 | |
Mostyn Bramley-Moore | d0ecd6a | 2017-12-06 19:13:21 | [diff] [blame^] | 212 | if (regions[i].path == "[stack]") { |
jcivelli | f4462a35 | 2017-01-10 04:45:59 | [diff] [blame] | 213 | // On Android the test is run on a background thread, since [stack] is for |
| 214 | // the main thread, we cannot test this. |
| 215 | #if !defined(OS_ANDROID) |
scherkus | aff972d | 2014-11-21 01:36:04 | [diff] [blame] | 216 | EXPECT_GE(address, regions[i].start); |
| 217 | EXPECT_LT(address, regions[i].end); |
jcivelli | f4462a35 | 2017-01-10 04:45:59 | [diff] [blame] | 218 | #endif |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 219 | EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::READ); |
| 220 | EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::WRITE); |
scherkus | a9ffa63f | 2014-11-24 15:47:29 | [diff] [blame] | 221 | EXPECT_FALSE(regions[i].permissions & MappedMemoryRegion::EXECUTE); |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 222 | EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::PRIVATE); |
| 223 | EXPECT_FALSE(found_stack) << "Found duplicate stacks"; |
| 224 | found_stack = true; |
| 225 | } |
[email protected] | fe7bc8df | 2013-07-04 06:06:09 | [diff] [blame] | 226 | |
| 227 | if (address >= regions[i].start && address < regions[i].end) { |
| 228 | EXPECT_FALSE(found_address) << "Found same address in multiple regions"; |
| 229 | found_address = true; |
| 230 | } |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 231 | } |
| 232 | |
| 233 | EXPECT_TRUE(found_exe); |
Mostyn Bramley-Moore | d0ecd6a | 2017-12-06 19:13:21 | [diff] [blame^] | 234 | EXPECT_TRUE(found_stack); |
| 235 | EXPECT_TRUE(found_address); |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 236 | } |
| 237 | |
glider | fb699a7e | 2014-12-05 12:20:28 | [diff] [blame] | 238 | TEST(ProcMapsTest, ReadProcMaps) { |
| 239 | std::string proc_maps; |
| 240 | ASSERT_TRUE(ReadProcMaps(&proc_maps)); |
| 241 | |
| 242 | std::vector<MappedMemoryRegion> regions; |
| 243 | ASSERT_TRUE(ParseProcMaps(proc_maps, ®ions)); |
| 244 | ASSERT_FALSE(regions.empty()); |
| 245 | |
| 246 | CheckProcMapsRegions(regions); |
| 247 | } |
| 248 | |
[email protected] | 35c8083 | 2013-09-06 05:07:50 | [diff] [blame] | 249 | TEST(ProcMapsTest, ReadProcMapsNonEmptyString) { |
| 250 | std::string old_string("I forgot to clear the string"); |
| 251 | std::string proc_maps(old_string); |
| 252 | ASSERT_TRUE(ReadProcMaps(&proc_maps)); |
| 253 | EXPECT_EQ(std::string::npos, proc_maps.find(old_string)); |
| 254 | } |
| 255 | |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 256 | TEST(ProcMapsTest, MissingFields) { |
thestig | 073d514d | 2014-10-21 03:11:21 | [diff] [blame] | 257 | static const char* const kTestCases[] = { |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 258 | "00400000\n", // Missing end + beyond. |
| 259 | "00400000-0040b000\n", // Missing perms + beyond. |
| 260 | "00400000-0040b000 r-xp\n", // Missing offset + beyond. |
| 261 | "00400000-0040b000 r-xp 00000000\n", // Missing device + beyond. |
| 262 | "00400000-0040b000 r-xp 00000000 fc:00\n", // Missing inode + beyond. |
| 263 | "00400000-0040b000 00000000 fc:00 794418 /bin/cat\n", // Missing perms. |
| 264 | "00400000-0040b000 r-xp fc:00 794418 /bin/cat\n", // Missing offset. |
| 265 | "00400000-0040b000 r-xp 00000000 fc:00 /bin/cat\n", // Missing inode. |
| 266 | "00400000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing end. |
| 267 | "-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing start. |
| 268 | "00400000-0040b000 r-xp 00000000 794418 /bin/cat\n", // Missing device. |
| 269 | }; |
| 270 | |
| 271 | for (size_t i = 0; i < arraysize(kTestCases); ++i) { |
| 272 | SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i])); |
| 273 | std::vector<MappedMemoryRegion> regions; |
| 274 | EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions)); |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | TEST(ProcMapsTest, InvalidInput) { |
thestig | 073d514d | 2014-10-21 03:11:21 | [diff] [blame] | 279 | static const char* const kTestCases[] = { |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 280 | "thisisal-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n", |
| 281 | "0040000d-linvalid rwxp 00000000 fc:00 794418 /bin/cat\n", |
| 282 | "00400000-0040b000 inpu 00000000 fc:00 794418 /bin/cat\n", |
| 283 | "00400000-0040b000 rwxp tforproc fc:00 794418 /bin/cat\n", |
| 284 | "00400000-0040b000 rwxp 00000000 ma:ps 794418 /bin/cat\n", |
| 285 | "00400000-0040b000 rwxp 00000000 fc:00 parse! /bin/cat\n", |
| 286 | }; |
| 287 | |
| 288 | for (size_t i = 0; i < arraysize(kTestCases); ++i) { |
| 289 | SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i])); |
| 290 | std::vector<MappedMemoryRegion> regions; |
| 291 | EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions)); |
| 292 | } |
| 293 | } |
| 294 | |
[email protected] | ad9a012 | 2014-03-22 00:34:52 | [diff] [blame] | 295 | TEST(ProcMapsTest, ParseProcMapsEmptyString) { |
| 296 | std::vector<MappedMemoryRegion> regions; |
| 297 | EXPECT_TRUE(ParseProcMaps("", ®ions)); |
| 298 | EXPECT_EQ(0ULL, regions.size()); |
| 299 | } |
| 300 | |
| 301 | // Testing a couple of remotely possible weird things in the input: |
| 302 | // - Line ending with \r\n or \n\r. |
| 303 | // - File name contains quotes. |
| 304 | // - File name has whitespaces. |
| 305 | TEST(ProcMapsTest, ParseProcMapsWeirdCorrectInput) { |
| 306 | std::vector<MappedMemoryRegion> regions; |
| 307 | const std::string kContents = |
| 308 | "00400000-0040b000 r-xp 00000000 fc:00 2106562 " |
| 309 | " /bin/cat\r\n" |
| 310 | "7f53b7dad000-7f53b7f62000 r-xp 00000000 fc:00 263011 " |
| 311 | " /lib/x86_64-linux-gnu/libc-2.15.so\n\r" |
| 312 | "7f53b816d000-7f53b818f000 r-xp 00000000 fc:00 264284 " |
| 313 | " /lib/x86_64-linux-gnu/ld-2.15.so\n" |
| 314 | "7fff9c7ff000-7fff9c800000 r-xp 00000000 00:00 0 " |
| 315 | " \"vd so\"\n" |
| 316 | "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 " |
| 317 | " [vsys call]\n"; |
| 318 | EXPECT_TRUE(ParseProcMaps(kContents, ®ions)); |
| 319 | EXPECT_EQ(5ULL, regions.size()); |
| 320 | EXPECT_EQ("/bin/cat", regions[0].path); |
| 321 | EXPECT_EQ("/lib/x86_64-linux-gnu/libc-2.15.so", regions[1].path); |
| 322 | EXPECT_EQ("/lib/x86_64-linux-gnu/ld-2.15.so", regions[2].path); |
| 323 | EXPECT_EQ("\"vd so\"", regions[3].path); |
| 324 | EXPECT_EQ("[vsys call]", regions[4].path); |
| 325 | } |
| 326 | |
[email protected] | 959a8bf | 2013-07-03 02:02:23 | [diff] [blame] | 327 | } // namespace debug |
| 328 | } // namespace base |