mm: implement prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME)

PiperOrigin-RevId: 696727156
This commit is contained in:
Jamie Liu
2024-11-14 19:06:07 -08:00
committed by gVisor bot
parent 0659b6035a
commit cf5841ba66
14 changed files with 350 additions and 68 deletions
+6
View File
@@ -149,6 +149,12 @@ const (
PR_SCHED_CORE_SCOPE_THREAD = 0
PR_SCHED_CORE_SCOPE_THREAD_GROUP = 1
// PR_SET_VMA sets VMA attributes.
PR_SET_VMA = 0x53564d41
PR_SET_VMA_ANON_NAME = 0
// From kernel/sys.c:
ANON_VMA_NAME_MAX_LEN = 80
// PR_SET_PTRACER allows a specific process (or any, if PR_SET_PTRACER_ANY is
// specified) to ptrace the current task.
PR_SET_PTRACER = 0x59616d61
+13
View File
@@ -80,6 +80,15 @@ type regularFile struct {
// Protected by dataMu.
seals uint32
// initiallyUnlinked is true if this file was created using NewZeroFile or
// NewMemfd => newUnlinkedRegularFileDescription. initiallyUnlinked should
// be true when the equivalent shmem file in Linux would use
// shmem_anon_vm_ops rather than shmem_vm_ops.
//
// initiallyUnlinked is immutable, but stored here since it fits into
// alignment padding.
initiallyUnlinked bool
// size is the size of data.
//
// Protected by both dataMu and inode.mu; reading it requires holding
@@ -114,6 +123,7 @@ func newUnlinkedRegularFileDescription(ctx context.Context, creds *auth.Credenti
}
inode := fs.newRegularFile(creds.EffectiveKUID, creds.EffectiveKGID, 0777, nil /* parentDir */)
inode.impl.(*regularFile).initiallyUnlinked = true
d := fs.newDentry(inode)
defer d.DecRef(ctx)
d.name = name
@@ -588,6 +598,9 @@ func (fd *regularFileFD) Seek(ctx context.Context, offset int64, whence int32) (
func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
file := fd.inode().impl.(*regularFile)
opts.SentryOwnedContent = true
if file.initiallyUnlinked {
opts.NameMut = memmap.NameMutAnonShmem
}
return vfs.GenericConfigureMMap(&fd.vfsfd, file, opts)
}
+23 -2
View File
@@ -367,11 +367,15 @@ type MMapOpts struct {
// MLockMode specifies the memory locking behavior of the mapping.
MLockMode MLockMode
// Hint is the name used for the mapping in /proc/[pid]/maps. If Hint is
// Name is the name used for the mapping in /proc/[pid]/maps. If Name is
// empty, MappingIdentity.MappedName() will be used instead.
//
// TODO(jamieliu): Replace entirely with MappingIdentity?
Hint string
Name string
// NameMut controls the effect of prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME)
// on this mapping.
NameMut NameMut
// Force means to skip validation checks of Addr and Length. It can be
// used to create special mappings below mm.layout.MinAddr and
@@ -393,6 +397,23 @@ type MMapOpts struct {
SentryOwnedContent bool
}
// NameMut is the type of MMapOpts.NameMut.
type NameMut uint8
// Possible values for MMapOpts.NameMut:
const (
// NameMutDisallowed indicates that PR_SET_VMA_ANON_NAME should fail.
NameMutDisallowed NameMut = iota
// NameMutAnon indicates that PR_SET_VMA_ANON_NAME should succeed, and
// treat the mapping as private anonymous memory.
NameMutAnon
// NameMutAnonShmem indicates that PR_SET_VMA_ANON_NAME should succeed, and
// treat the mapping as shared anonymous memory.
NameMutAnonShmem
)
// MMapPlatformEffect is the type of MMapOpts.PlatformEffect.
type MMapPlatformEffect uint8
+7 -4
View File
@@ -312,9 +312,11 @@ type vma struct {
// metadata shown in /proc/[pid]/maps, and the vma holds a reference.
id memmap.MappingIdentity
// If hint is non-empty, it is a description of the vma printed in
// /proc/[pid]/maps. hint takes priority over id.MappedName().
hint string
// If name is non-empty, it is a description of the vma printed in
// /proc/[pid]/maps. name takes priority over id.MappedName().
name string
nameMut memmap.NameMut
// lastFault records the last address that was paged faulted. It hints at
// which direction addresses in this vma are being accessed.
@@ -339,7 +341,8 @@ func (v *vma) copy() vma {
numaPolicy: v.numaPolicy,
numaNodemask: v.numaNodemask,
id: v.id,
hint: v.hint,
name: v.name,
nameMut: v.nameMut,
lastFault: atomic.LoadUintptr(&v.lastFault),
}
}
+2 -2
View File
@@ -123,8 +123,8 @@ func (mm *MemoryManager) appendVMAMapsEntryLocked(ctx context.Context, vseg vmaI
// Figure out our filename or hint.
var path string
if vma.hint != "" {
path = vma.hint
if vma.name != "" {
path = vma.name
} else if vma.id != nil {
path = vma.id.MappedName(ctx)
}
+133 -30
View File
@@ -292,7 +292,8 @@ func (mm *MemoryManager) MapStack(ctx context.Context) (hostarch.AddrRange, erro
Private: true,
GrowsDown: true,
MLockMode: mm.defMLockMode,
Hint: "[stack]",
Name: "[stack]",
NameMut: memmap.NameMutAnon,
}, nil /* droppedIDs */)
mm.mappingMu.Unlock()
for _, id := range droppedIDs {
@@ -462,7 +463,8 @@ func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr hostarch.Addr, oldS
GrowsDown: vma.growsDown,
Stack: vma.isStack,
MLockMode: vma.mlockMode,
Hint: vma.hint,
Name: vma.name,
NameMut: vma.nameMut,
}, droppedIDs)
if err == nil {
if vma.mlockMode == memmap.MLockEager {
@@ -799,7 +801,8 @@ func (mm *MemoryManager) Brk(ctx context.Context, addr hostarch.Addr) (hostarch.
// Linux: mm/mmap.c:sys_brk() => do_brk_flags() includes
// mm->def_flags.
MLockMode: mm.defMLockMode,
Hint: "[heap]",
Name: "[heap]",
NameMut: memmap.NameMutAnon,
}, droppedIDs)
if err != nil {
addr = mm.brk.End
@@ -1069,37 +1072,41 @@ func (mm *MemoryManager) SetNumaPolicy(addr hostarch.Addr, length uint64, policy
}
}
// SetDontFork implements the semantics of madvise MADV_DONTFORK.
func (mm *MemoryManager) SetDontFork(addr hostarch.Addr, length uint64, dontfork bool) error {
ar, ok := addr.ToRange(length)
// madviseAddrRange converts addr and length to an AddrRange as for madvise(2)
// (and prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME) =>
// mm/madvise.c:madvise_vma_anon_name().)
func madviseAddrRange(addr hostarch.Addr, length uint64) (hostarch.AddrRange, error) {
// All quotes from the man page:
// "madvise() only operates on whole pages, therefore addr must be
// page-aligned."
if !addr.IsPageAligned() {
// "EINVAL: addr is not page-aligned or length is negative." Note that
// length is size_t which is unsigned, so "length is negative" is
// impossible, but we take this as referring to the next check (for
// which Linux also returns EINVAL).
return hostarch.AddrRange{}, linuxerr.EINVAL
}
// "The value of length is rounded up to a multiple of page size."
lengthRounded, ok := hostarch.PageRoundUp(length)
if !ok {
return linuxerr.EINVAL
return hostarch.AddrRange{}, linuxerr.EINVAL
}
mm.mappingMu.Lock()
defer mm.mappingMu.Unlock()
defer func() {
mm.vmas.MergeInsideRange(ar)
mm.vmas.MergeOutsideRange(ar)
}()
for vseg := mm.vmas.LowerBoundSegment(ar.Start); vseg.Ok() && vseg.Start() < ar.End; vseg = vseg.NextSegment() {
vseg = mm.vmas.Isolate(vseg, ar)
vma := vseg.ValuePtr()
vma.dontfork = dontfork
ar, ok := addr.ToRange(lengthRounded)
if !ok {
// Not specified, but Linux also returns EINVAL in this case.
return hostarch.AddrRange{}, linuxerr.EINVAL
}
if mm.vmas.SpanRange(ar) != ar.Length() {
return linuxerr.ENOMEM
}
return nil
return ar, nil
}
// Decommit implements the semantics of Linux's madvise(MADV_DONTNEED).
func (mm *MemoryManager) Decommit(addr hostarch.Addr, length uint64) error {
ar, ok := addr.ToRange(length)
if !ok {
return linuxerr.EINVAL
ar, err := madviseAddrRange(addr, length)
if err != nil {
return err
}
if length == 0 {
return nil
}
mm.mappingMu.RLock()
@@ -1226,6 +1233,102 @@ func (mm *MemoryManager) Decommit(addr hostarch.Addr, length uint64) error {
return nil
}
// madviseMutateVMAs is similar to mm.vmas.MutateRange(), but:
//
// - madviseMutateVMAs locks mm.mappingMu for writing, as required to mutate
// mm.vmas.
//
// - If f returns a non-nil error, madviseMutateVMAs stops iteration and
// returns the error.
//
// - Consistent with Linux madvise(): "If there are some parts of the specified
// address range that are not mapped, the Linux version of madvise() ignores
// them and applies the call to the rest (but returns ENOMEM from the system
// call, as it should)."
func (mm *MemoryManager) madviseMutateVMAs(addr hostarch.Addr, length uint64, f func(vseg vmaIterator) error) error {
ar, err := madviseAddrRange(addr, length)
if err != nil {
return err
}
if length == 0 {
return nil
}
mm.mappingMu.Lock()
defer mm.mappingMu.Unlock()
vseg := mm.vmas.LowerBoundSegmentSplitBefore(ar.Start)
if !vseg.Ok() {
return linuxerr.ENOMEM
}
hadvgap := ar.Start < vseg.Start()
for vseg.Start() < ar.End {
vseg = mm.vmas.SplitAfter(vseg, ar.End)
err := f(vseg)
vseg = mm.vmas.MergePrev(vseg)
if err != nil {
mm.vmas.MergeNext(vseg)
return err
}
if ar.End <= vseg.End() {
break
}
vgap := vseg.NextGap()
if !vgap.IsEmpty() {
hadvgap = true
}
vseg = vgap.NextSegment()
}
mm.vmas.MergePrev(vseg)
if hadvgap {
return linuxerr.ENOMEM
}
return nil
}
// SetDontFork implements the semantics of madvise MADV_DONTFORK.
//
// Preconditions: addr and length are page-aligned.
func (mm *MemoryManager) SetDontFork(addr hostarch.Addr, length uint64, dontfork bool) error {
return mm.madviseMutateVMAs(addr, length, func(vseg vmaIterator) error {
vseg.ValuePtr().dontfork = dontfork
return nil
})
}
// SetVMAAnonName implements the semantics of Linux's
// prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME).
func (mm *MemoryManager) SetVMAAnonName(addr hostarch.Addr, length uint64, name string, nameIsNil bool) error {
// Check that name contains only valid characters; compare Linux
// kernel/sys.c:is_valid_name_char(). `for ... := range string`
// iterates Unicode runes, not bytes.
for i := 0; i < len(name); i++ {
c := name[i]
if c <= 0x1f || c >= 0x7f || c == '\\' || c == '`' || c == '$' || c == '[' || c == ']' {
return linuxerr.EINVAL
}
}
return mm.madviseMutateVMAs(addr, length, func(vseg vmaIterator) error {
vma := vseg.ValuePtr()
if vma.nameMut == memmap.NameMutDisallowed {
return linuxerr.EBADF
}
if nameIsNil {
vma.name = ""
return nil
}
switch vma.nameMut {
case memmap.NameMutAnon:
vma.name = fmt.Sprintf("[anon:%s]", name)
case memmap.NameMutAnonShmem:
vma.name = fmt.Sprintf("[anon_shmem:%s]", name)
default:
panic(fmt.Sprintf("unknown memmap.NameMut: %d", vma.nameMut))
}
return nil
})
}
// MSyncOpts holds options to MSync.
type MSyncOpts struct {
// Sync has the semantics of MS_SYNC.
@@ -1406,7 +1509,7 @@ func (mm *MemoryManager) IsMembarrierRSeqEnabled() bool {
}
// FindVMAByName finds a vma with the specified name and returns its start address and offset.
func (mm *MemoryManager) FindVMAByName(ar hostarch.AddrRange, hint string) (hostarch.Addr, uint64, error) {
func (mm *MemoryManager) FindVMAByName(ar hostarch.AddrRange, name string) (hostarch.Addr, uint64, error) {
mm.mappingMu.RLock()
defer mm.mappingMu.RUnlock()
@@ -1417,9 +1520,9 @@ func (mm *MemoryManager) FindVMAByName(ar hostarch.AddrRange, hint string) (host
}
vma := vseg.ValuePtr()
if vma.hint == hint {
if vma.name == name {
return start, vma.off, nil
}
}
return 0, 0, fmt.Errorf("could not find \"%s\" in %s", hint, ar)
return 0, 0, fmt.Errorf("could not find %q in %s", name, ar)
}
+5 -3
View File
@@ -126,7 +126,8 @@ func (mm *MemoryManager) createVMALocked(ctx context.Context, opts memmap.MMapOp
mlockMode: opts.MLockMode,
numaPolicy: linux.MPOL_DEFAULT,
id: opts.MappingIdentity,
hint: opts.Hint,
name: opts.Name,
nameMut: opts.NameMut,
}
vseg := mm.vmas.Insert(vgap, ar, v)
@@ -455,7 +456,7 @@ func (vmaSetFunctions) MaxKey() hostarch.Addr {
func (vmaSetFunctions) ClearValue(vma *vma) {
vma.mappable = nil
vma.id = nil
vma.hint = ""
vma.name = ""
atomic.StoreUintptr(&vma.lastFault, 0)
}
@@ -472,7 +473,8 @@ func (vmaSetFunctions) Merge(ar1 hostarch.AddrRange, vma1 vma, ar2 hostarch.Addr
vma1.numaNodemask != vma2.numaNodemask ||
vma1.dontfork != vma2.dontfork ||
vma1.id != vma2.id ||
vma1.hint != vma2.hint {
vma1.name != vma2.name ||
vma1.nameMut != vma2.nameMut {
return vma{}, false
}
@@ -96,7 +96,7 @@ func (s *State) newTrapLocked(ctx context.Context, mm memoryManager) (hostarch.A
// nextTrap is saved on the usertrap vma to handle the third and second
// cases.
if s.nextTrap == 0 {
addr, off, err := mm.FindVMAByName(trapTableAddrRange, tableHint)
addr, off, err := mm.FindVMAByName(trapTableAddrRange, tableVMAName)
if off != 0 {
return 0, fmt.Errorf("the usertrap vma has been overmounted")
}
@@ -152,7 +152,7 @@ var trapTableAddrRange = hostarch.AddrRange{Start: 0x60000, End: 0x70000}
const (
trapTableSize = hostarch.Addr(trapNR * trapSize)
tableHint = "[usertrap]"
tableVMAName = "[usertrap]"
)
// LoadUsertrap maps the usertrap table into the address space.
@@ -166,7 +166,7 @@ func loadUsertrap(ctx context.Context, mm memoryManager, addr hostarch.Addr) err
Addr: addr,
Length: uint64(size),
Private: true,
Hint: tableHint,
Name: tableVMAName,
MLockMode: memmap.MLockEager,
Perms: hostarch.AccessType{
Write: false,
+2 -15
View File
@@ -112,6 +112,8 @@ func Mmap(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *
if err := file.ConfigureMMap(t, &opts); err != nil {
return 0, nil, err
}
} else {
opts.NameMut = memmap.NameMutAnon
}
rv, err := t.MemoryManager().MMap(t, opts)
@@ -175,21 +177,6 @@ func Madvise(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr
length := uint64(args[1].SizeT())
adv := args[2].Int()
// "The Linux implementation requires that the address addr be
// page-aligned, and allows length to be zero." - madvise(2)
if addr.RoundDown() != addr {
return 0, nil, linuxerr.EINVAL
}
if length == 0 {
return 0, nil, nil
}
// Not explicitly stated: length need not be page-aligned.
lenAddr, ok := hostarch.Addr(length).RoundUp()
if !ok {
return 0, nil, linuxerr.EINVAL
}
length = uint64(lenAddr)
switch adv {
case linux.MADV_DONTNEED:
return 0, nil, t.MemoryManager().Decommit(addr, length)
+19
View File
@@ -237,6 +237,25 @@ func Prctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
_, err := primitive.CopyInt32Out(t, args[1].Pointer(), isSubreaper)
return 0, nil, err
case linux.PR_SET_VMA:
if args[1].Int() != linux.PR_SET_VMA_ANON_NAME {
return 0, nil, linuxerr.EINVAL
}
var (
name string
nameIsNil bool
)
if nameAddr := args[4].Pointer(); nameAddr == 0 {
nameIsNil = true
} else {
var err error
name, err = t.CopyInString(nameAddr, linux.ANON_VMA_NAME_MAX_LEN)
if err != nil {
return 0, nil, err
}
}
return 0, nil, t.MemoryManager().SetVMAAnonName(args[2].Pointer(), args[3].Uint64(), name, nameIsNil)
case linux.PR_GET_TIMING,
linux.PR_SET_TIMING,
linux.PR_GET_TSC,
+109 -9
View File
@@ -1127,23 +1127,123 @@ TEST(ProcSelfMaps, Mprotect) {
3 * kPageSize, PROT_READ)));
}
// Expected pathname for MAP_SHARED | MAP_ANONYMOUS mappings. See proc(5),
// "/proc/[pid]/map_files/".
constexpr char kSharedAnonPath[] = "/dev/zero (deleted)";
TEST(ProcSelfMaps, SharedAnon) {
const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
MmapAnon(kPageSize, PROT_READ, MAP_SHARED | MAP_ANONYMOUS));
const auto proc_self_maps =
ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
for (const auto& line : absl::StrSplit(proc_self_maps, '\n')) {
const auto entry = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMapsLine(line));
if (entry.start <= m.addr() && m.addr() < entry.end) {
// cf. proc(5), "/proc/[pid]/map_files/"
EXPECT_EQ(entry.filename, "/dev/zero (deleted)");
return;
}
}
FAIL() << "no maps entry containing mapping at " << m.ptr();
const auto entries = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(proc_self_maps));
const auto entry =
ASSERT_NO_ERRNO_AND_VALUE(FindUniqueMapsEntry(entries, m.addr()));
EXPECT_EQ(entry.filename, kSharedAnonPath);
}
#ifndef PR_SET_VMA
#define PR_SET_VMA 0x53564d41
#endif
#ifndef PR_SET_VMA_ANON_NAME
#define PR_SET_VMA_ANON_NAME 0
#endif
TEST(ProcSelfMaps, AnonNamePrivateAnon) {
const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
MmapAnon(kPageSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS));
int rv = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, m.addr(), m.len(), "test");
SKIP_IF(rv < 0 && errno == EINVAL);
ASSERT_THAT(rv, SyscallSucceeds());
auto proc_self_maps =
ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
auto entries = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(proc_self_maps));
auto entry =
ASSERT_NO_ERRNO_AND_VALUE(FindUniqueMapsEntry(entries, m.addr()));
EXPECT_EQ(entry.filename, "[anon:test]");
ASSERT_THAT(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, m.addr(), m.len(), ""),
SyscallSucceeds());
proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
entries = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(proc_self_maps));
entry = ASSERT_NO_ERRNO_AND_VALUE(FindUniqueMapsEntry(entries, m.addr()));
EXPECT_EQ(entry.filename, "[anon:]");
ASSERT_THAT(
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, m.addr(), m.len(), nullptr),
SyscallSucceeds());
proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
entries = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(proc_self_maps));
entry = ASSERT_NO_ERRNO_AND_VALUE(FindUniqueMapsEntry(entries, m.addr()));
EXPECT_EQ(entry.filename, "");
}
TEST(ProcSelfMaps, AnonNameSharedAnon) {
const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
MmapAnon(kPageSize, PROT_READ, MAP_SHARED | MAP_ANONYMOUS));
int rv = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, m.addr(), m.len(), "test");
SKIP_IF(rv < 0 && errno == EINVAL);
// Using PR_SET_VMA_ANON_NAME on shared anonymous mappings isn't permitted
// until d09e8ca6cb93 ("mm: anonymous shared memory naming"), Linux 6.2+.
SKIP_IF(rv < 0 && errno == EBADF);
ASSERT_THAT(rv, SyscallSucceeds());
auto proc_self_maps =
ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
auto entries = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(proc_self_maps));
auto entry =
ASSERT_NO_ERRNO_AND_VALUE(FindUniqueMapsEntry(entries, m.addr()));
EXPECT_EQ(entry.filename, "[anon_shmem:test]");
ASSERT_THAT(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, m.addr(), m.len(), ""),
SyscallSucceeds());
proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
entries = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(proc_self_maps));
entry = ASSERT_NO_ERRNO_AND_VALUE(FindUniqueMapsEntry(entries, m.addr()));
EXPECT_EQ(entry.filename, "[anon_shmem:]");
ASSERT_THAT(
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, m.addr(), m.len(), nullptr),
SyscallSucceeds());
proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
entries = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(proc_self_maps));
entry = ASSERT_NO_ERRNO_AND_VALUE(FindUniqueMapsEntry(entries, m.addr()));
EXPECT_EQ(entry.filename, kSharedAnonPath);
}
// Test parameterized by mmap flags.
class ProcSelfMapsMmapFileTest : public ::testing::TestWithParam<int> {};
TEST_P(ProcSelfMapsMmapFileTest, AnonNameFile) {
const auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY));
const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
Mmap(nullptr, kPageSize, PROT_READ, GetParam(), fd.get(), 0));
int rv = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, m.addr(), m.len(), "test");
SKIP_IF(rv < 0 && errno == EINVAL);
ASSERT_THAT(rv, SyscallFailsWithErrno(EBADF));
auto proc_self_maps =
ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
auto entries = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(proc_self_maps));
auto entry =
ASSERT_NO_ERRNO_AND_VALUE(FindUniqueMapsEntry(entries, m.addr()));
EXPECT_EQ(entry.filename, f.path());
ASSERT_THAT(
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, m.addr(), m.len(), nullptr),
SyscallFailsWithErrno(EBADF));
proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
entries = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(proc_self_maps));
entry = ASSERT_NO_ERRNO_AND_VALUE(FindUniqueMapsEntry(entries, m.addr()));
EXPECT_EQ(entry.filename, f.path());
}
INSTANTIATE_TEST_SUITE_P(SelfAndNumericPid, ProcSelfMapsMmapFileTest,
::testing::Values(MAP_SHARED, MAP_PRIVATE));
TEST(ProcSelfFd, OpenFd) {
int pipe_fds[2];
ASSERT_THAT(pipe2(pipe_fds, O_CLOEXEC), SyscallSucceeds());
+1
View File
@@ -85,6 +85,7 @@ cc_library(
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:optional",
],
)
+23
View File
@@ -14,6 +14,7 @@
#include "test/util/proc_util.h"
#include <stdint.h>
#include <sys/prctl.h>
#include <algorithm>
@@ -24,6 +25,7 @@
#include "absl/container/flat_hash_set.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "test/util/fs_util.h"
@@ -99,6 +101,27 @@ PosixErrorOr<std::vector<ProcMapsEntry>> ParseProcMaps(
return entries;
}
PosixErrorOr<ProcMapsEntry> FindUniqueMapsEntry(
std::vector<ProcMapsEntry> const& entries, uintptr_t addr) {
auto const pred = [&](ProcMapsEntry const& entry) {
return entry.start <= addr && addr < entry.end;
};
auto const it = absl::c_find_if(entries, pred);
if (it == entries.end()) {
return PosixError(EINVAL,
absl::StrFormat("no entry contains address %#x", addr));
}
auto const it2 = std::find_if(it + 1, entries.end(), pred);
if (it2 != entries.end()) {
return PosixError(
EINVAL,
absl::StrFormat("overlapping entries [%#x-%#x) and [%#x-%#x) both "
"contain address %#x",
it->start, it->end, it2->start, it2->end, addr));
}
return *it;
}
PosixErrorOr<bool> IsVsyscallEnabled() {
ASSIGN_OR_RETURN_ERRNO(auto contents, GetContents("/proc/self/maps"));
ASSIGN_OR_RETURN_ERRNO(auto maps, ParseProcMaps(contents));
+4
View File
@@ -84,6 +84,10 @@ PosixErrorOr<ProcMapsEntry> ParseProcMapsLine(absl::string_view line);
PosixErrorOr<std::vector<ProcMapsEntry>> ParseProcMaps(
absl::string_view contents);
// Returns the unique entry in entries containing the given address.
PosixErrorOr<ProcMapsEntry> FindUniqueMapsEntry(
std::vector<ProcMapsEntry> const&, uintptr_t);
// Returns true if vsyscall (emmulation or not) is enabled.
PosixErrorOr<bool> IsVsyscallEnabled();