llvm-project/lldb/unittests/Process/Utility/LinuxProcMapsTest.cpp
David Spickett a8a659458d
[lldb] Change MemoryRegionInfo::SetIsShadowStack into a "builder" method (#189565)
Meaning a method on an object, which returns a reference to self.

I am doing this because it was pointed out to me that MemoryRegionInfo
has lots of construction paramaters, and most of the time, we want the
default value for most of the things.

So now we can do:
MemoryRegionInfo(...); // Shadow stack is "don't know".
MemoryRegionInfo(...).SetIsShadowStack(eNo) // Shadow stack is "no".

Which removes one parameter from every use of the constructor.

Along the way I realised that the shadow stack "ss" flag is only tested
by the Guarded Control Stack tests, which only run on specific Arm
hardware. I've added a new "ss" test to LinuxProcMapsTest, which will
run on any system.
2026-03-31 08:56:39 +00:00

324 lines
15 KiB
C++

//===-- LinuxProcMapsTest.cpp ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "Plugins/Process/Utility/LinuxProcMaps.h"
#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Utility/Status.h"
#include <tuple>
using namespace lldb_private;
typedef std::tuple<const char *, MemoryRegionInfos, const char *>
LinuxProcMapsTestParams;
// Wrapper for convenience because Range is usually begin, size
static MemoryRegionInfo::RangeType make_range(lldb::addr_t begin,
lldb::addr_t end) {
MemoryRegionInfo::RangeType range(begin, 0);
range.SetRangeEnd(end);
return range;
}
class LinuxProcMapsTestFixture
: public ::testing::TestWithParam<LinuxProcMapsTestParams> {
protected:
Status error;
std::string err_str;
MemoryRegionInfos regions;
LinuxMapCallback callback;
void SetUp() override {
callback = [this](llvm::Expected<MemoryRegionInfo> Info) {
if (Info) {
err_str.clear();
regions.push_back(*Info);
return true;
}
err_str = toString(Info.takeError());
return false;
};
}
void check_regions(LinuxProcMapsTestParams params) {
EXPECT_THAT(std::get<1>(params), testing::ContainerEq(regions));
ASSERT_EQ(std::get<2>(params), err_str);
}
};
TEST_P(LinuxProcMapsTestFixture, ParseMapRegions) {
auto params = GetParam();
ParseLinuxMapRegions(std::get<0>(params), callback);
check_regions(params);
}
// Note: ConstString("") != ConstString(nullptr)
// When a region has no name, it will have the latter in the MemoryRegionInfo
INSTANTIATE_TEST_SUITE_P(
ProcMapTests, LinuxProcMapsTestFixture,
::testing::Values(
// Nothing in nothing out
std::make_tuple("", MemoryRegionInfos{}, ""),
// Various formatting error conditions
std::make_tuple("55a4512f7000/55a451b68000 rw-p 00000000 00:00 0",
MemoryRegionInfos{},
"malformed /proc/{pid}/maps entry, missing dash "
"between address range"),
std::make_tuple("0-0 rw", MemoryRegionInfos{},
"malformed /proc/{pid}/maps entry, missing some "
"portion of permissions"),
std::make_tuple("0-0 z--p 00000000 00:00 0", MemoryRegionInfos{},
"unexpected /proc/{pid}/maps read permission char"),
std::make_tuple("0-0 rz-p 00000000 00:00 0", MemoryRegionInfos{},
"unexpected /proc/{pid}/maps write permission char"),
std::make_tuple("0-0 rwzp 00000000 00:00 0", MemoryRegionInfos{},
"unexpected /proc/{pid}/maps exec permission char"),
// Stops at first parsing error
std::make_tuple(
"0-1 rw-p 00000000 00:00 0 [abc]\n"
"0-0 rwzp 00000000 00:00 0\n"
"2-3 r-xp 00000000 00:00 0 [def]\n",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0, 1), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString("[abc]"), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
},
"unexpected /proc/{pid}/maps exec permission char"),
// Single entry
std::make_tuple(
"55a4512f7000-55a451b68000 rw-p 00000000 00:00 0 [heap]",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x55a4512f7000, 0x55a451b68000),
MemoryRegionInfo::eYes, MemoryRegionInfo::eYes,
MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
MemoryRegionInfo::eYes, ConstString("[heap]"),
MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow,
MemoryRegionInfo::eDontKnow),
},
""),
// Multiple entries
std::make_tuple(
"7fc090021000-7fc094000000 ---p 00000000 00:00 0\n"
"7fc094000000-7fc094a00000 ---s 00000000 00:00 0\n"
"ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 "
"[vsyscall]",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0x7fc090021000, 0x7fc094000000),
MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
MemoryRegionInfo::eYes, ConstString(nullptr),
MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow,
MemoryRegionInfo::eDontKnow),
MemoryRegionInfo(make_range(0x7fc094000000, 0x7fc094a00000),
MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, ConstString(nullptr),
MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow,
MemoryRegionInfo::eDontKnow),
MemoryRegionInfo(
make_range(0xffffffffff600000, 0xffffffffff601000),
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eYes, ConstString("[vsyscall]"),
MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow,
MemoryRegionInfo::eDontKnow),
},
"")));
class LinuxProcSMapsTestFixture : public LinuxProcMapsTestFixture {};
INSTANTIATE_TEST_SUITE_P(
ProcSMapTests, LinuxProcSMapsTestFixture,
::testing::Values(
// Nothing in nothing out
std::make_tuple("", MemoryRegionInfos{}, ""),
// Uses the same parsing for first line, so same errors but referring to
// smaps
std::make_tuple("0/0 rw-p 00000000 00:00 0", MemoryRegionInfos{},
"malformed /proc/{pid}/smaps entry, missing dash "
"between address range"),
// Stop parsing at first error
std::make_tuple(
"1111-2222 rw-p 00000000 00:00 0 [foo]\n"
"0/0 rw-p 00000000 00:00 0",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0x1111, 0x2222), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString("[foo]"), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
},
"malformed /proc/{pid}/smaps entry, missing dash between address "
"range"),
// Property line without a region is an error
std::make_tuple("Referenced: 2188 kB\n"
"1111-2222 rw-p 00000000 00:00 0 [foo]\n"
"3333-4444 rw-p 00000000 00:00 0 [bar]\n",
MemoryRegionInfos{},
"Found a property line without a corresponding mapping "
"in /proc/{pid}/smaps"),
// Single region parses, has no flags
std::make_tuple(
"1111-2222 rw-p 00000000 00:00 0 [foo]",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0x1111, 0x2222), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString("[foo]"), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
},
""),
// Single shared region parses, has no flags
std::make_tuple(
"1111-2222 rw-s 00000000 00:00 0 [foo]",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0x1111, 0x2222), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eYes, MemoryRegionInfo::eYes,
ConstString("[foo]"), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
},
""),
// Single region with flags, other lines ignored
std::make_tuple(
"1111-2222 rw-p 00000000 00:00 0 [foo]\n"
"Referenced: 2188 kB\n"
"AnonHugePages: 0 kB\n"
"VmFlags: mt",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0x1111, 0x2222), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString("[foo]"), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eYes, MemoryRegionInfo::eDontKnow)
.SetIsShadowStack(MemoryRegionInfo::eNo),
},
""),
// Whitespace ignored
std::make_tuple(
"0-0 rw-p 00000000 00:00 0\n"
"VmFlags: mt ",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0, 0), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eYes, MemoryRegionInfo::eDontKnow)
.SetIsShadowStack(MemoryRegionInfo::eNo),
},
""),
// VmFlags line means it has flag info, but nothing is set
std::make_tuple(
"0-0 rw-p 00000000 00:00 0\n"
"VmFlags: ",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0, 0), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eNo, MemoryRegionInfo::eDontKnow)
.SetIsShadowStack(MemoryRegionInfo::eNo),
},
""),
// Handle some pages not having a flags line
std::make_tuple(
"1111-2222 rw-p 00000000 00:00 0 [foo]\n"
"Referenced: 2188 kB\n"
"AnonHugePages: 0 kB\n"
"3333-4444 r-xp 00000000 00:00 0 [bar]\n"
"VmFlags: mt",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0x1111, 0x2222), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString("[foo]"), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
MemoryRegionInfo(
make_range(0x3333, 0x4444), MemoryRegionInfo::eYes,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString("[bar]"), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eYes, MemoryRegionInfo::eDontKnow)
.SetIsShadowStack(MemoryRegionInfo::eNo),
},
""),
// Handle no pages having a flags line (older kernels)
std::make_tuple(
"1111-2222 rw-p 00000000 00:00 0\n"
"Referenced: 2188 kB\n"
"AnonHugePages: 0 kB\n"
"3333-4444 r-xp 00000000 00:00 0\n"
"KernelPageSize: 4 kB\n"
"MMUPageSize: 4 kB\n",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0x1111, 0x2222), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
MemoryRegionInfo(
make_range(0x3333, 0x4444), MemoryRegionInfo::eYes,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
},
""),
// We must look for exact flag strings, ignoring substrings of longer
// flag names.
std::make_tuple(
"0-0 rw-p 00000000 00:00 0\n"
"VmFlags: amt mtb amtb",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0, 0), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eNo, MemoryRegionInfo::eDontKnow)
.SetIsShadowStack(MemoryRegionInfo::eNo),
},
""),
// "ss" means shadow stack.
std::make_tuple(
"0-0 rw-p 00000000 00:00 0\n"
"VmFlags: ss",
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0, 0), MemoryRegionInfo::eYes,
MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0,
MemoryRegionInfo::eNo, MemoryRegionInfo::eDontKnow)
.SetIsShadowStack(MemoryRegionInfo::eYes),
},
"")));
TEST_P(LinuxProcSMapsTestFixture, ParseSMapRegions) {
auto params = GetParam();
ParseLinuxSMapRegions(std::get<0>(params), callback);
check_regions(params);
}