1eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// found in the LICENSE file. 4eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 5eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/debug/proc_maps_linux.h" 6eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include <fcntl.h> 858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#if defined(OS_LINUX) || defined(OS_ANDROID) 10eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <inttypes.h> 11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#endif 12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/files/file_util.h" 14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/files/scoped_file.h" 15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/string_split.h" 16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 1723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#if defined(OS_ANDROID) && !defined(__LP64__) 1823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)// In 32-bit mode, Bionic's inttypes.h defines PRI/SCNxPTR as an 1923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)// unsigned long int, which is incompatible with Bionic's stdint.h 2023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)// defining uintptr_t as an unsigned int: 21eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// https://code.google.com/p/android/issues/detail?id=57218 22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#undef SCNxPTR 23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#define SCNxPTR "x" 24eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#endif 25eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace base { 27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace debug { 28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 2958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Scans |proc_maps| starting from |pos| returning true if the gate VMA was 3058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// found, otherwise returns false. 3158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)static bool ContainsGateVMA(std::string* proc_maps, size_t pos) { 3258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#if defined(ARCH_CPU_ARM_FAMILY) 3358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // The gate VMA on ARM kernels is the interrupt vectors page. 3458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return proc_maps->find(" [vectors]\n", pos) != std::string::npos; 3558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#elif defined(ARCH_CPU_X86_64) 3658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // The gate VMA on x86 64-bit kernels is the virtual system call page. 3758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return proc_maps->find(" [vsyscall]\n", pos) != std::string::npos; 3858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#else 3958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // Otherwise assume there is no gate VMA in which case we shouldn't 4058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // get duplicate entires. 4158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return false; 4258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#endif 4358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)} 4458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool ReadProcMaps(std::string* proc_maps) { 4658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // seq_file only writes out a page-sized amount on each call. Refer to header 4758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // file for details. 4858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) const long kReadSize = sysconf(_SC_PAGESIZE); 4958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::ScopedFD fd(HANDLE_EINTR(open("/proc/self/maps", O_RDONLY))); 51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!fd.is_valid()) { 5258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) DPLOG(ERROR) << "Couldn't open /proc/self/maps"; 5358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return false; 5458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) } 5558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) proc_maps->clear(); 5658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 5758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) while (true) { 5858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // To avoid a copy, resize |proc_maps| so read() can write directly into it. 5958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // Compute |buffer| afterwards since resize() may reallocate. 6058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) size_t pos = proc_maps->size(); 6158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) proc_maps->resize(pos + kReadSize); 6258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) void* buffer = &(*proc_maps)[pos]; 6358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), buffer, kReadSize)); 6558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) if (bytes_read < 0) { 6658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) DPLOG(ERROR) << "Couldn't read /proc/self/maps"; 6758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) proc_maps->clear(); 6858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return false; 6958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) } 7058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 7158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // ... and don't forget to trim off excess bytes. 7258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) proc_maps->resize(pos + bytes_read); 7358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 7458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) if (bytes_read == 0) 7558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) break; 7658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 7758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // The gate VMA is handled as a special case after seq_file has finished 7858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // iterating through all entries in the virtual memory table. 7958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // 8058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // Unfortunately, if additional entries are added at this point in time 8158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // seq_file gets confused and the next call to read() will return duplicate 8258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // entries including the gate VMA again. 8358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // 8458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) // Avoid this by searching for the gate VMA and breaking early. 8558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) if (ContainsGateVMA(proc_maps, pos)) 8658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) break; 8758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) } 8858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 8958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return true; 90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 92eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool ParseProcMaps(const std::string& input, 93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch std::vector<MappedMemoryRegion>* regions_out) { 94effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch CHECK(regions_out); 95eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch std::vector<MappedMemoryRegion> regions; 96eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 97eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // This isn't async safe nor terribly efficient, but it doesn't need to be at 98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // this point in time. 99eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch std::vector<std::string> lines; 100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch SplitString(input, '\n', &lines); 101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch for (size_t i = 0; i < lines.size(); ++i) { 103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Due to splitting on '\n' the last line should be empty. 104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (i == lines.size() - 1) { 105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!lines[i].empty()) { 106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DLOG(WARNING) << "Last line not empty"; 107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return false; 108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch break; 110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch MappedMemoryRegion region; 113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch const char* line = lines[i].c_str(); 114eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch char permissions[5] = {'\0'}; // Ensure NUL-terminated string. 115eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch uint8 dev_major = 0; 116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch uint8 dev_minor = 0; 117eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch long inode = 0; 118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch int path_index = 0; 119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Sample format from man 5 proc: 121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // 122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // address perms offset dev inode pathname 123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm 124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // 125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // The final %n term captures the offset in the input string, which is used 126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // to determine the path name. It *does not* increment the return value. 127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Refer to man 3 sscanf for details. 128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4c %llx %hhx:%hhx %ld %n", 129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch ®ion.start, ®ion.end, permissions, ®ion.offset, 130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch &dev_major, &dev_minor, &inode, &path_index) < 7) { 131effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DPLOG(WARNING) << "sscanf failed for line: " << line; 132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return false; 133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch region.permissions = 0; 136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (permissions[0] == 'r') 138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch region.permissions |= MappedMemoryRegion::READ; 139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch else if (permissions[0] != '-') 140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return false; 141eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (permissions[1] == 'w') 143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch region.permissions |= MappedMemoryRegion::WRITE; 144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch else if (permissions[1] != '-') 145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return false; 146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (permissions[2] == 'x') 148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch region.permissions |= MappedMemoryRegion::EXECUTE; 149eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch else if (permissions[2] != '-') 150eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return false; 151eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 152eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (permissions[3] == 'p') 153eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch region.permissions |= MappedMemoryRegion::PRIVATE; 154eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch else if (permissions[3] != 's' && permissions[3] != 'S') // Shared memory. 155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return false; 156eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Pushing then assigning saves us a string copy. 158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch regions.push_back(region); 159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch regions.back().path.assign(line + path_index); 160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch regions_out->swap(regions); 163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return true; 164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 165eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 166eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} // namespace debug 167eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} // namespace base 168