| OLD | NEW |
| (Empty) |
| 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 | |
| 5 #include "base/debug/proc_maps_linux.h" | |
| 6 #include "base/files/file_path.h" | |
| 7 #include "base/path_service.h" | |
| 8 #include "base/strings/stringprintf.h" | |
| 9 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | |
| 10 #include "base/threading/platform_thread.h" | |
| 11 #include "testing/gtest/include/gtest/gtest.h" | |
| 12 | |
| 13 namespace base { | |
| 14 namespace debug { | |
| 15 | |
| 16 TEST(ProcMapsTest, Empty) { | |
| 17 std::vector<MappedMemoryRegion> regions; | |
| 18 EXPECT_TRUE(ParseProcMaps("", ®ions)); | |
| 19 EXPECT_EQ(0u, regions.size()); | |
| 20 } | |
| 21 | |
| 22 TEST(ProcMapsTest, NoSpaces) { | |
| 23 static const char kNoSpaces[] = | |
| 24 "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat\n"; | |
| 25 | |
| 26 std::vector<MappedMemoryRegion> regions; | |
| 27 ASSERT_TRUE(ParseProcMaps(kNoSpaces, ®ions)); | |
| 28 ASSERT_EQ(1u, regions.size()); | |
| 29 | |
| 30 EXPECT_EQ(0x00400000u, regions[0].start); | |
| 31 EXPECT_EQ(0x0040b000u, regions[0].end); | |
| 32 EXPECT_EQ(0x00002200u, regions[0].offset); | |
| 33 EXPECT_EQ("/bin/cat", regions[0].path); | |
| 34 } | |
| 35 | |
| 36 TEST(ProcMapsTest, Spaces) { | |
| 37 static const char kSpaces[] = | |
| 38 "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/space cat\n"; | |
| 39 | |
| 40 std::vector<MappedMemoryRegion> regions; | |
| 41 ASSERT_TRUE(ParseProcMaps(kSpaces, ®ions)); | |
| 42 ASSERT_EQ(1u, regions.size()); | |
| 43 | |
| 44 EXPECT_EQ(0x00400000u, regions[0].start); | |
| 45 EXPECT_EQ(0x0040b000u, regions[0].end); | |
| 46 EXPECT_EQ(0x00002200u, regions[0].offset); | |
| 47 EXPECT_EQ("/bin/space cat", regions[0].path); | |
| 48 } | |
| 49 | |
| 50 TEST(ProcMapsTest, NoNewline) { | |
| 51 static const char kNoSpaces[] = | |
| 52 "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat"; | |
| 53 | |
| 54 std::vector<MappedMemoryRegion> regions; | |
| 55 ASSERT_FALSE(ParseProcMaps(kNoSpaces, ®ions)); | |
| 56 } | |
| 57 | |
| 58 TEST(ProcMapsTest, NoPath) { | |
| 59 static const char kNoPath[] = | |
| 60 "00400000-0040b000 rw-p 00000000 00:00 0 \n"; | |
| 61 | |
| 62 std::vector<MappedMemoryRegion> regions; | |
| 63 ASSERT_TRUE(ParseProcMaps(kNoPath, ®ions)); | |
| 64 ASSERT_EQ(1u, regions.size()); | |
| 65 | |
| 66 EXPECT_EQ(0x00400000u, regions[0].start); | |
| 67 EXPECT_EQ(0x0040b000u, regions[0].end); | |
| 68 EXPECT_EQ(0x00000000u, regions[0].offset); | |
| 69 EXPECT_EQ("", regions[0].path); | |
| 70 } | |
| 71 | |
| 72 TEST(ProcMapsTest, Heap) { | |
| 73 static const char kHeap[] = | |
| 74 "022ac000-022cd000 rw-p 00000000 00:00 0 [heap]\n"; | |
| 75 | |
| 76 std::vector<MappedMemoryRegion> regions; | |
| 77 ASSERT_TRUE(ParseProcMaps(kHeap, ®ions)); | |
| 78 ASSERT_EQ(1u, regions.size()); | |
| 79 | |
| 80 EXPECT_EQ(0x022ac000u, regions[0].start); | |
| 81 EXPECT_EQ(0x022cd000u, regions[0].end); | |
| 82 EXPECT_EQ(0x00000000u, regions[0].offset); | |
| 83 EXPECT_EQ("[heap]", regions[0].path); | |
| 84 } | |
| 85 | |
| 86 #if defined(ARCH_CPU_32_BITS) | |
| 87 TEST(ProcMapsTest, Stack32) { | |
| 88 static const char kStack[] = | |
| 89 "beb04000-beb25000 rw-p 00000000 00:00 0 [stack]\n"; | |
| 90 | |
| 91 std::vector<MappedMemoryRegion> regions; | |
| 92 ASSERT_TRUE(ParseProcMaps(kStack, ®ions)); | |
| 93 ASSERT_EQ(1u, regions.size()); | |
| 94 | |
| 95 EXPECT_EQ(0xbeb04000u, regions[0].start); | |
| 96 EXPECT_EQ(0xbeb25000u, regions[0].end); | |
| 97 EXPECT_EQ(0x00000000u, regions[0].offset); | |
| 98 EXPECT_EQ("[stack]", regions[0].path); | |
| 99 } | |
| 100 #elif defined(ARCH_CPU_64_BITS) | |
| 101 TEST(ProcMapsTest, Stack64) { | |
| 102 static const char kStack[] = | |
| 103 "7fff69c5b000-7fff69c7d000 rw-p 00000000 00:00 0 [stack]\n"; | |
| 104 | |
| 105 std::vector<MappedMemoryRegion> regions; | |
| 106 ASSERT_TRUE(ParseProcMaps(kStack, ®ions)); | |
| 107 ASSERT_EQ(1u, regions.size()); | |
| 108 | |
| 109 EXPECT_EQ(0x7fff69c5b000u, regions[0].start); | |
| 110 EXPECT_EQ(0x7fff69c7d000u, regions[0].end); | |
| 111 EXPECT_EQ(0x00000000u, regions[0].offset); | |
| 112 EXPECT_EQ("[stack]", regions[0].path); | |
| 113 } | |
| 114 #endif | |
| 115 | |
| 116 TEST(ProcMapsTest, Multiple) { | |
| 117 static const char kMultiple[] = | |
| 118 "00400000-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n" | |
| 119 "0060a000-0060b000 r--p 0000a000 fc:00 794418 /bin/cat\n" | |
| 120 "0060b000-0060c000 rw-p 0000b000 fc:00 794418 /bin/cat\n"; | |
| 121 | |
| 122 std::vector<MappedMemoryRegion> regions; | |
| 123 ASSERT_TRUE(ParseProcMaps(kMultiple, ®ions)); | |
| 124 ASSERT_EQ(3u, regions.size()); | |
| 125 | |
| 126 EXPECT_EQ(0x00400000u, regions[0].start); | |
| 127 EXPECT_EQ(0x0040b000u, regions[0].end); | |
| 128 EXPECT_EQ(0x00000000u, regions[0].offset); | |
| 129 EXPECT_EQ("/bin/cat", regions[0].path); | |
| 130 | |
| 131 EXPECT_EQ(0x0060a000u, regions[1].start); | |
| 132 EXPECT_EQ(0x0060b000u, regions[1].end); | |
| 133 EXPECT_EQ(0x0000a000u, regions[1].offset); | |
| 134 EXPECT_EQ("/bin/cat", regions[1].path); | |
| 135 | |
| 136 EXPECT_EQ(0x0060b000u, regions[2].start); | |
| 137 EXPECT_EQ(0x0060c000u, regions[2].end); | |
| 138 EXPECT_EQ(0x0000b000u, regions[2].offset); | |
| 139 EXPECT_EQ("/bin/cat", regions[2].path); | |
| 140 } | |
| 141 | |
| 142 TEST(ProcMapsTest, Permissions) { | |
| 143 static struct { | |
| 144 const char* input; | |
| 145 uint8 permissions; | |
| 146 } kTestCases[] = { | |
| 147 {"00400000-0040b000 ---s 00000000 fc:00 794418 /bin/cat\n", 0}, | |
| 148 {"00400000-0040b000 ---S 00000000 fc:00 794418 /bin/cat\n", 0}, | |
| 149 {"00400000-0040b000 r--s 00000000 fc:00 794418 /bin/cat\n", | |
| 150 MappedMemoryRegion::READ}, | |
| 151 {"00400000-0040b000 -w-s 00000000 fc:00 794418 /bin/cat\n", | |
| 152 MappedMemoryRegion::WRITE}, | |
| 153 {"00400000-0040b000 --xs 00000000 fc:00 794418 /bin/cat\n", | |
| 154 MappedMemoryRegion::EXECUTE}, | |
| 155 {"00400000-0040b000 rwxs 00000000 fc:00 794418 /bin/cat\n", | |
| 156 MappedMemoryRegion::READ | MappedMemoryRegion::WRITE | | |
| 157 MappedMemoryRegion::EXECUTE}, | |
| 158 {"00400000-0040b000 ---p 00000000 fc:00 794418 /bin/cat\n", | |
| 159 MappedMemoryRegion::PRIVATE}, | |
| 160 {"00400000-0040b000 r--p 00000000 fc:00 794418 /bin/cat\n", | |
| 161 MappedMemoryRegion::READ | MappedMemoryRegion::PRIVATE}, | |
| 162 {"00400000-0040b000 -w-p 00000000 fc:00 794418 /bin/cat\n", | |
| 163 MappedMemoryRegion::WRITE | MappedMemoryRegion::PRIVATE}, | |
| 164 {"00400000-0040b000 --xp 00000000 fc:00 794418 /bin/cat\n", | |
| 165 MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE}, | |
| 166 {"00400000-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n", | |
| 167 MappedMemoryRegion::READ | MappedMemoryRegion::WRITE | | |
| 168 MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE}, | |
| 169 }; | |
| 170 | |
| 171 for (size_t i = 0; i < arraysize(kTestCases); ++i) { | |
| 172 SCOPED_TRACE( | |
| 173 base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i].input)); | |
| 174 | |
| 175 std::vector<MappedMemoryRegion> regions; | |
| 176 EXPECT_TRUE(ParseProcMaps(kTestCases[i].input, ®ions)); | |
| 177 EXPECT_EQ(1u, regions.size()); | |
| 178 if (regions.empty()) | |
| 179 continue; | |
| 180 EXPECT_EQ(kTestCases[i].permissions, regions[0].permissions); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 #if defined(ADDRESS_SANITIZER) | |
| 185 // AddressSanitizer may move local variables to a dedicated "fake stack" which | |
| 186 // is outside the stack region listed in /proc/self/maps. We disable ASan | |
| 187 // instrumentation for this function to force the variable to be local. | |
| 188 __attribute__((no_sanitize_address)) | |
| 189 #endif | |
| 190 void CheckProcMapsRegions(const std::vector<MappedMemoryRegion> ®ions) { | |
| 191 // We should be able to find both the current executable as well as the stack | |
| 192 // mapped into memory. Use the address of |exe_path| as a way of finding the | |
| 193 // stack. | |
| 194 FilePath exe_path; | |
| 195 EXPECT_TRUE(PathService::Get(FILE_EXE, &exe_path)); | |
| 196 uintptr_t address = reinterpret_cast<uintptr_t>(&exe_path); | |
| 197 bool found_exe = false; | |
| 198 bool found_stack = false; | |
| 199 bool found_address = false; | |
| 200 | |
| 201 for (size_t i = 0; i < regions.size(); ++i) { | |
| 202 if (regions[i].path == exe_path.value()) { | |
| 203 // It's OK to find the executable mapped multiple times as there'll be | |
| 204 // multiple sections (e.g., text, data). | |
| 205 found_exe = true; | |
| 206 } | |
| 207 | |
| 208 // Valgrind uses its own allocated stacks instead of the kernel-provided | |
| 209 // stack without letting the kernel know via prctl(PR_SET_MM_START_STACK). | |
| 210 // Depending on which kernel you're running it'll impact the output of | |
| 211 // /proc/self/maps. | |
| 212 // | |
| 213 // Prior to version 3.4, the kernel completely ignores other stacks and | |
| 214 // always prints out the vma lying within mm->start_stack as [stack] even | |
| 215 // if the program was currently executing on a different stack. | |
| 216 // | |
| 217 // Starting in 3.4, the kernel will print out the vma containing the current | |
| 218 // stack pointer as [stack:TID] as long as that vma does not lie within | |
| 219 // mm->start_stack. | |
| 220 // | |
| 221 // Because this has gotten too complicated and brittle of a test, completely | |
| 222 // ignore checking for the stack and address when running under Valgrind. | |
| 223 // See http://crbug.com/431702 for more details. | |
| 224 if (!RunningOnValgrind() && regions[i].path == "[stack]") { | |
| 225 EXPECT_GE(address, regions[i].start); | |
| 226 EXPECT_LT(address, regions[i].end); | |
| 227 EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::READ); | |
| 228 EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::WRITE); | |
| 229 EXPECT_FALSE(regions[i].permissions & MappedMemoryRegion::EXECUTE); | |
| 230 EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::PRIVATE); | |
| 231 EXPECT_FALSE(found_stack) << "Found duplicate stacks"; | |
| 232 found_stack = true; | |
| 233 } | |
| 234 | |
| 235 if (address >= regions[i].start && address < regions[i].end) { | |
| 236 EXPECT_FALSE(found_address) << "Found same address in multiple regions"; | |
| 237 found_address = true; | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 EXPECT_TRUE(found_exe); | |
| 242 if (!RunningOnValgrind()) { | |
| 243 EXPECT_TRUE(found_stack); | |
| 244 EXPECT_TRUE(found_address); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 TEST(ProcMapsTest, ReadProcMaps) { | |
| 249 std::string proc_maps; | |
| 250 ASSERT_TRUE(ReadProcMaps(&proc_maps)); | |
| 251 | |
| 252 std::vector<MappedMemoryRegion> regions; | |
| 253 ASSERT_TRUE(ParseProcMaps(proc_maps, ®ions)); | |
| 254 ASSERT_FALSE(regions.empty()); | |
| 255 | |
| 256 CheckProcMapsRegions(regions); | |
| 257 } | |
| 258 | |
| 259 TEST(ProcMapsTest, ReadProcMapsNonEmptyString) { | |
| 260 std::string old_string("I forgot to clear the string"); | |
| 261 std::string proc_maps(old_string); | |
| 262 ASSERT_TRUE(ReadProcMaps(&proc_maps)); | |
| 263 EXPECT_EQ(std::string::npos, proc_maps.find(old_string)); | |
| 264 } | |
| 265 | |
| 266 TEST(ProcMapsTest, MissingFields) { | |
| 267 static const char* const kTestCases[] = { | |
| 268 "00400000\n", // Missing end + beyond. | |
| 269 "00400000-0040b000\n", // Missing perms + beyond. | |
| 270 "00400000-0040b000 r-xp\n", // Missing offset + beyond. | |
| 271 "00400000-0040b000 r-xp 00000000\n", // Missing device + beyond. | |
| 272 "00400000-0040b000 r-xp 00000000 fc:00\n", // Missing inode + beyond. | |
| 273 "00400000-0040b000 00000000 fc:00 794418 /bin/cat\n", // Missing perms. | |
| 274 "00400000-0040b000 r-xp fc:00 794418 /bin/cat\n", // Missing offset. | |
| 275 "00400000-0040b000 r-xp 00000000 fc:00 /bin/cat\n", // Missing inode. | |
| 276 "00400000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing end. | |
| 277 "-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing start. | |
| 278 "00400000-0040b000 r-xp 00000000 794418 /bin/cat\n", // Missing device. | |
| 279 }; | |
| 280 | |
| 281 for (size_t i = 0; i < arraysize(kTestCases); ++i) { | |
| 282 SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i])); | |
| 283 std::vector<MappedMemoryRegion> regions; | |
| 284 EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions)); | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 TEST(ProcMapsTest, InvalidInput) { | |
| 289 static const char* const kTestCases[] = { | |
| 290 "thisisal-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n", | |
| 291 "0040000d-linvalid rwxp 00000000 fc:00 794418 /bin/cat\n", | |
| 292 "00400000-0040b000 inpu 00000000 fc:00 794418 /bin/cat\n", | |
| 293 "00400000-0040b000 rwxp tforproc fc:00 794418 /bin/cat\n", | |
| 294 "00400000-0040b000 rwxp 00000000 ma:ps 794418 /bin/cat\n", | |
| 295 "00400000-0040b000 rwxp 00000000 fc:00 parse! /bin/cat\n", | |
| 296 }; | |
| 297 | |
| 298 for (size_t i = 0; i < arraysize(kTestCases); ++i) { | |
| 299 SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i])); | |
| 300 std::vector<MappedMemoryRegion> regions; | |
| 301 EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions)); | |
| 302 } | |
| 303 } | |
| 304 | |
| 305 TEST(ProcMapsTest, ParseProcMapsEmptyString) { | |
| 306 std::vector<MappedMemoryRegion> regions; | |
| 307 EXPECT_TRUE(ParseProcMaps("", ®ions)); | |
| 308 EXPECT_EQ(0ULL, regions.size()); | |
| 309 } | |
| 310 | |
| 311 // Testing a couple of remotely possible weird things in the input: | |
| 312 // - Line ending with \r\n or \n\r. | |
| 313 // - File name contains quotes. | |
| 314 // - File name has whitespaces. | |
| 315 TEST(ProcMapsTest, ParseProcMapsWeirdCorrectInput) { | |
| 316 std::vector<MappedMemoryRegion> regions; | |
| 317 const std::string kContents = | |
| 318 "00400000-0040b000 r-xp 00000000 fc:00 2106562 " | |
| 319 " /bin/cat\r\n" | |
| 320 "7f53b7dad000-7f53b7f62000 r-xp 00000000 fc:00 263011 " | |
| 321 " /lib/x86_64-linux-gnu/libc-2.15.so\n\r" | |
| 322 "7f53b816d000-7f53b818f000 r-xp 00000000 fc:00 264284 " | |
| 323 " /lib/x86_64-linux-gnu/ld-2.15.so\n" | |
| 324 "7fff9c7ff000-7fff9c800000 r-xp 00000000 00:00 0 " | |
| 325 " \"vd so\"\n" | |
| 326 "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 " | |
| 327 " [vsys call]\n"; | |
| 328 EXPECT_TRUE(ParseProcMaps(kContents, ®ions)); | |
| 329 EXPECT_EQ(5ULL, regions.size()); | |
| 330 EXPECT_EQ("/bin/cat", regions[0].path); | |
| 331 EXPECT_EQ("/lib/x86_64-linux-gnu/libc-2.15.so", regions[1].path); | |
| 332 EXPECT_EQ("/lib/x86_64-linux-gnu/ld-2.15.so", regions[2].path); | |
| 333 EXPECT_EQ("\"vd so\"", regions[3].path); | |
| 334 EXPECT_EQ("[vsys call]", regions[4].path); | |
| 335 } | |
| 336 | |
| 337 } // namespace debug | |
| 338 } // namespace base | |
| OLD | NEW |