Skip to content

Commit

Permalink
Use /proc/<pid>/smaps instead of /proc/pid/maps
Browse files Browse the repository at this point in the history
This is a bit slower, but allows us to find other information. Of
particular interest is the vmflags - for pstack or canal, we want to not
scan memory that is mapped as being in an IO range, as that can interact
badly with hardware.
  • Loading branch information
peadar committed Sep 27, 2024
1 parent 8b5fb83 commit 3471d51
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 20 deletions.
6 changes: 6 additions & 0 deletions canal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,12 @@ mainExcept(int argc, char *argv[])
IOFlagSave _(*debug);
*debug << "scan " << hex << segment.start << " to " << segment.start + segment.fileEnd;
}
if (segment.vmflags.find( pstack::Procman::AddressRange::VmFlag::memory_mapped_io ) != segment.vmflags.end() ) {
if (verbose) {
*debug << "skipping IO mapping\n";
}
continue;
}
if (findstr != "") {
findString( *process, segment, findstr );
} else {
Expand Down
10 changes: 5 additions & 5 deletions dead.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,15 +235,15 @@ CoreProcess::addressSpace() const {
if (ub->first >= hdr.p_vaddr && ub->second.second.end <= hdr.p_vaddr + hdr.p_memsz)
name = ub->second.first;
}
std::set<AddressRange::Flags> flags;
std::set<AddressRange::Permission> flags;
if ((hdr.p_flags & PF_W) != 0)
flags.insert(AddressRange::Flags::write);
flags.insert(AddressRange::Permission::write);
if ((hdr.p_flags & PF_R) != 0)
flags.insert(AddressRange::Flags::read);
flags.insert(AddressRange::Permission::read);
if ((hdr.p_flags & PF_X) != 0)
flags.insert(AddressRange::Flags::exec);
flags.insert(AddressRange::Permission::exec);
rv.push_back( { hdr.p_vaddr, hdr.p_vaddr + hdr.p_memsz,
hdr.p_vaddr + hdr.p_filesz, 0, {0, 0, 0, name}, {}});
hdr.p_vaddr + hdr.p_filesz, 0, {0, 0, 0, name}, {}, {}});
}
return rv;
}
Expand Down
19 changes: 17 additions & 2 deletions libpstack/proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,23 @@ struct AddressRange {
off_t offset;
DevNode backing;

enum class Flags { read,write,exec,priv,count, shared };
std::set<Flags> permissions;
enum class VmFlag {
readable, writeable, executable, shared, may_read, may_write,
may_execute, may_share, stack_grows_down, pure_pfn_range,
disabled_write, pages_locked, memory_mapped_io, sequential_read_advised,
random_read_advised, dont_copy_on_fork, dont_expand_on_remap,
accountable, swap_not_reserved, huge_tlb_pages, synchronous_page_fault,
architecture_specific, wipe_on_fork, dont_dump, soft_dirty, mixed_map,
huge_page_advised, no_huge_page_advised, mergeable_advised,
arm64_BTI_guarded_page, arm64_MTE_allocation_tags,
userfaultfd_missing_tracking, userfaultfd_wr_protect_tracking,
shadow_stack, sealed
};
static std::optional<VmFlag> vmflag(std::string_view);

enum class Permission { read,write,exec,priv,count, shared };
std::set<Permission> permissions;
std::set<VmFlag> vmflags;
};

// An ELF object mapped at an address. We don't actually create the Elf::Object
Expand Down
96 changes: 83 additions & 13 deletions live.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,14 +207,14 @@ LiveProcess::stop(lwpid_t tid) {
*debug << "suspend LWP " << tid << std::endl;
}

// Parse [s]maps for this pid. We use "smaps" just for vmflags for now.
std::vector<AddressRange>
LiveProcess::addressSpace() const { return procAddressSpace(procname(pid, "maps")); }
LiveProcess::addressSpace() const { return procAddressSpace(procname(pid, "smaps")); }

std::vector<AddressRange>
Process::procAddressSpace(const std::string &fn) {
std::vector<AddressRange> rv;
std::ifstream input(fn);
for (;;) {
for (std::ifstream input{fn}; input && input.peek() != EOF; ) {
uintptr_t start, end;
off_t offset;
int major, minor;
Expand All @@ -223,22 +223,46 @@ Process::procAddressSpace(const std::string &fn) {
char colon, minus;
input >> std::hex >> start >> minus >> end >> perms >> offset >>
major >> colon >> minor >> std::dec >> inode;

std::getline( input, path );
if (input.eof() || !input.good())
break;
std::set<AddressRange::Flags> flags;
size_t trim = path.find_first_not_of(" ");
if ( trim != std::string::npos ) {
path = path.substr( trim );
} else {
path = "";
}
std::set<AddressRange::Permission> flags;
for (auto c : perms) {
static const std::map<char, AddressRange::Flags> flagmap {
{ 'r', AddressRange::Flags::read },
{ 'w', AddressRange::Flags::write },
{ 'x', AddressRange::Flags::exec },
{ 'p', AddressRange::Flags::priv },
{ 's', AddressRange::Flags::shared },
static const std::map<char, AddressRange::Permission> flagmap {
{ 'r', AddressRange::Permission::read },
{ 'w', AddressRange::Permission::write },
{ 'x', AddressRange::Permission::exec },
{ 'p', AddressRange::Permission::priv },
{ 's', AddressRange::Permission::shared },
};
if (c != '-')
flags.insert(flagmap.at(c));
}
rv.push_back({start, end, end, offset, {major, minor, inode, path}, flags });
std::set<AddressRange::VmFlag> vmflags;
while (isupper(input.peek())) {
std::string key;
input >> std::ws >> key >> std::ws;
if (key == "VmFlags:") {
std::string str;
std::getline(input, str);
for (size_t cur = 0; cur < str.size(); ) {
int next = str.find_first_of(' ', cur);
auto tok = str.substr(cur, next - cur);
cur = next + 1;
auto flag = AddressRange::vmflag(tok);
if (flag)
vmflags.insert( *flag );
}
} else {
input.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
}
}
rv.push_back({start, end, end, offset, {major, minor, inode, path }, flags, vmflags });
}
return rv;
}
Expand All @@ -251,4 +275,50 @@ LiveProcess::loadSharedObjectsFromFileNote()
// it mostly exists for truncated core files, so don't bother now.
return false;
}

std::optional<AddressRange::VmFlag> AddressRange::vmflag(std::string_view sv) {
static const std::map<std::string_view, AddressRange::VmFlag> flagmap {
{ "rd", VmFlag::readable },
{ "wr", VmFlag::writeable },
{ "ex", VmFlag::executable },
{ "sh", VmFlag::shared },
{ "mr", VmFlag::may_read },
{ "mw", VmFlag::may_write },
{ "me", VmFlag::may_execute },
{ "ms", VmFlag::may_share },
{ "gd", VmFlag::stack_grows_down },
{ "pf", VmFlag::pure_pfn_range },
{ "dw", VmFlag::disabled_write },
{ "lo", VmFlag::pages_locked },
{ "io", VmFlag::memory_mapped_io },
{ "sr", VmFlag::sequential_read_advised },
{ "rr", VmFlag::random_read_advised },
{ "dc", VmFlag::dont_copy_on_fork },
{ "de", VmFlag::dont_expand_on_remap },
{ "ac", VmFlag::accountable },
{ "nr", VmFlag::swap_not_reserved },
{ "ht", VmFlag::huge_tlb_pages },
{ "sf", VmFlag::synchronous_page_fault },
{ "ar", VmFlag::architecture_specific },
{ "wf", VmFlag::wipe_on_fork },
{ "dd", VmFlag::dont_dump },
{ "sd", VmFlag::soft_dirty },
{ "mm", VmFlag::mixed_map },
{ "hg", VmFlag::huge_page_advised },
{ "nh", VmFlag::no_huge_page_advised },
{ "mg", VmFlag::mergeable_advised },
{ "bt", VmFlag::arm64_BTI_guarded_page },
{ "mt", VmFlag::arm64_MTE_allocation_tags },
{ "um", VmFlag::userfaultfd_missing_tracking },
{ "uw", VmFlag::userfaultfd_wr_protect_tracking },
{ "ss", VmFlag::shadow_stack },
{ "sl", VmFlag::sealed },
};
auto it = flagmap.find( sv );
if (it != flagmap.end()) {
return it->second;
}
return std::nullopt;
}

}

0 comments on commit 3471d51

Please sign in to comment.