mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Merge pull request #14008 from luiscape:luis/map-fixed
PiperOrigin-RevId: 964917211
This commit is contained in:
+16
-15
@@ -33,21 +33,22 @@ const (
|
||||
|
||||
// Flags for mmap(2).
|
||||
const (
|
||||
MAP_SHARED = 1 << 0
|
||||
MAP_PRIVATE = 1 << 1
|
||||
MAP_DROPPABLE = 1 << 3
|
||||
MAP_FIXED = 1 << 4
|
||||
MAP_ANONYMOUS = 1 << 5
|
||||
MAP_32BIT = 1 << 6 // arch/x86/include/uapi/asm/mman.h
|
||||
MAP_GROWSDOWN = 1 << 8
|
||||
MAP_DENYWRITE = 1 << 11
|
||||
MAP_EXECUTABLE = 1 << 12
|
||||
MAP_LOCKED = 1 << 13
|
||||
MAP_NORESERVE = 1 << 14
|
||||
MAP_POPULATE = 1 << 15
|
||||
MAP_NONBLOCK = 1 << 16
|
||||
MAP_STACK = 1 << 17
|
||||
MAP_HUGETLB = 1 << 18
|
||||
MAP_SHARED = 1 << 0
|
||||
MAP_PRIVATE = 1 << 1
|
||||
MAP_DROPPABLE = 1 << 3
|
||||
MAP_FIXED = 1 << 4
|
||||
MAP_ANONYMOUS = 1 << 5
|
||||
MAP_32BIT = 1 << 6 // arch/x86/include/uapi/asm/mman.h
|
||||
MAP_GROWSDOWN = 1 << 8
|
||||
MAP_DENYWRITE = 1 << 11
|
||||
MAP_EXECUTABLE = 1 << 12
|
||||
MAP_LOCKED = 1 << 13
|
||||
MAP_NORESERVE = 1 << 14
|
||||
MAP_POPULATE = 1 << 15
|
||||
MAP_NONBLOCK = 1 << 16
|
||||
MAP_STACK = 1 << 17
|
||||
MAP_HUGETLB = 1 << 18
|
||||
MAP_FIXED_NOREPLACE = 1 << 20
|
||||
)
|
||||
|
||||
// Flags for mremap(2).
|
||||
|
||||
@@ -333,6 +333,12 @@ type MMapOpts struct {
|
||||
// be replaced. If Unmap is true, Fixed must be true.
|
||||
Unmap bool
|
||||
|
||||
// NoReplace is true if MAP_FIXED_NOREPLACE semantics apply: the mapping
|
||||
// must be located at Addr, and EEXIST (rather than ENOMEM) is returned if
|
||||
// any mapping already exists in the range. If NoReplace is true, Fixed
|
||||
// must be true and Unmap must be false.
|
||||
NoReplace bool
|
||||
|
||||
// If Map32Bit is true, all addresses in the created mapping must fit in a
|
||||
// 32-bit integer. (Note that the "end address" of the mapping, i.e. the
|
||||
// address of the first byte *after* the mapping, need not fit in a 32-bit
|
||||
|
||||
@@ -110,6 +110,9 @@ func (mm *MemoryManager) MMap(ctx context.Context, opts memmap.MMapOpts) (hostar
|
||||
if opts.Unmap && !opts.Fixed {
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
if opts.NoReplace && (!opts.Fixed || opts.Unmap) {
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
if opts.GrowsDown && opts.Mappable != nil {
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
+18
-2
@@ -50,6 +50,7 @@ func (mm *MemoryManager) createVMALocked(ctx context.Context, opts memmap.MMapOp
|
||||
Stack: opts.Stack,
|
||||
Private: opts.Private,
|
||||
Unmap: opts.Unmap,
|
||||
NoReplace: opts.NoReplace,
|
||||
Map32Bit: opts.Map32Bit,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -148,6 +149,9 @@ type findAvailableOpts struct {
|
||||
// - Addr must be page-aligned.
|
||||
//
|
||||
// - Unmap allows existing guard pages in the returned range.
|
||||
//
|
||||
// - NoReplace allows existing guard pages (but not existing vmas) in the
|
||||
// returned range.
|
||||
|
||||
Addr hostarch.Addr
|
||||
Fixed bool
|
||||
@@ -155,6 +159,7 @@ type findAvailableOpts struct {
|
||||
Stack bool
|
||||
Private bool
|
||||
Unmap bool
|
||||
NoReplace bool
|
||||
Map32Bit bool
|
||||
}
|
||||
|
||||
@@ -183,8 +188,19 @@ func (mm *MemoryManager) findAvailableLocked(length uint64, opts findAvailableOp
|
||||
if opts.Unmap {
|
||||
return ar.Start, nil
|
||||
}
|
||||
vgap := mm.vmas.FindGap(ar.Start)
|
||||
if opts.NoReplace {
|
||||
// MAP_FIXED_NOREPLACE fails with EEXIST iff the requested
|
||||
// range overlaps an existing vma; like Linux, it may map over
|
||||
// guard pages of adjacent MAP_GROWSDOWN mappings. Compare
|
||||
// Linux's mm/mmap.c:do_mmap() => find_vma_intersection().
|
||||
if vgap.Ok() && vgap.Range().IsSupersetOf(ar) {
|
||||
return ar.Start, nil
|
||||
}
|
||||
return 0, linuxerr.EEXIST
|
||||
}
|
||||
// Check for the presence of an existing vma or guard page.
|
||||
if vgap := mm.vmas.FindGap(ar.Start); vgap.Ok() && vgap.availableRange().IsSupersetOf(ar) {
|
||||
if vgap.Ok() && vgap.availableRange().IsSupersetOf(ar) {
|
||||
return ar.Start, nil
|
||||
}
|
||||
}
|
||||
@@ -603,7 +619,7 @@ func (vseg vmaIterator) seekNextLowerBound(addr hostarch.Addr) vmaIterator {
|
||||
}
|
||||
|
||||
// availableRange returns the subset of vgap.Range() in which new vmas may be
|
||||
// created without MMapOpts.Unmap == true.
|
||||
// created without MMapOpts.Unmap or MMapOpts.NoReplace set.
|
||||
func (vgap vmaGapIterator) availableRange() hostarch.AddrRange {
|
||||
ar := vgap.Range()
|
||||
next := vgap.NextSegment()
|
||||
|
||||
@@ -89,4 +89,8 @@ var MmapFlagSet = abi.FlagSet{
|
||||
Flag: linux.MAP_HUGETLB,
|
||||
Name: "MAP_HUGETLB",
|
||||
},
|
||||
{
|
||||
Flag: linux.MAP_FIXED_NOREPLACE,
|
||||
Name: "MAP_FIXED_NOREPLACE",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ func Mmap(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *
|
||||
flags := args[3].Int()
|
||||
fd := args[4].Int()
|
||||
fixed := flags&linux.MAP_FIXED != 0
|
||||
noReplace := flags&linux.MAP_FIXED_NOREPLACE != 0
|
||||
private := flags&linux.MAP_PRIVATE != 0
|
||||
shared := flags&linux.MAP_SHARED != 0
|
||||
anon := flags&linux.MAP_ANONYMOUS != 0
|
||||
@@ -58,13 +59,14 @@ func Mmap(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *
|
||||
}
|
||||
|
||||
opts := memmap.MMapOpts{
|
||||
Length: args[1].Uint64(),
|
||||
Offset: args[5].Uint64(),
|
||||
Addr: args[0].Pointer(),
|
||||
Fixed: fixed,
|
||||
Unmap: fixed,
|
||||
Map32Bit: map32bit,
|
||||
Private: private,
|
||||
Length: args[1].Uint64(),
|
||||
Offset: args[5].Uint64(),
|
||||
Addr: args[0].Pointer(),
|
||||
Fixed: fixed || noReplace,
|
||||
Unmap: fixed && !noReplace,
|
||||
NoReplace: noReplace,
|
||||
Map32Bit: map32bit,
|
||||
Private: private,
|
||||
Perms: hostarch.AccessType{
|
||||
Read: linux.PROT_READ&prot != 0,
|
||||
Write: linux.PROT_WRITE&prot != 0,
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
@@ -51,6 +52,10 @@ using ::testing::AnyOf;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Gt;
|
||||
|
||||
#ifndef MAP_FIXED_NOREPLACE
|
||||
#define MAP_FIXED_NOREPLACE 0x100000
|
||||
#endif
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
@@ -385,6 +390,89 @@ TEST_F(MMapTest, MapFixed64) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Kernels predating Linux 4.17 silently ignore MAP_FIXED_NOREPLACE, treating
|
||||
// addr as a hint; partially overlapping requests return EEXIST only from 4.19
|
||||
// (Linux commit 7aa867dd8952 "mm/mmap.c: don't clobber partially overlapping
|
||||
// mappings with MAP_FIXED_NOREPLACE").
|
||||
#define SKIP_IF_NO_MAP_FIXED_NOREPLACE() \
|
||||
do { \
|
||||
if (!IsRunningOnGvisor()) { \
|
||||
auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); \
|
||||
SKIP_IF(version.major < 4 || \
|
||||
(version.major == 4 && version.minor < 19)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// MAP_FIXED_NOREPLACE gives us exactly the requested address when the range
|
||||
// is free.
|
||||
TEST_F(MMapTest, MapFixedNoReplaceEmpty) {
|
||||
SKIP_IF_NO_MAP_FIXED_NOREPLACE();
|
||||
EXPECT_THAT(Map(0x30000000, kPageSize, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0),
|
||||
SyscallSucceedsWithValue(0x30000000));
|
||||
}
|
||||
|
||||
// MAP_FIXED_NOREPLACE fails with EEXIST if any mapping already exists in the
|
||||
// requested range; it must not fall back to a different address or clobber
|
||||
// the existing mapping.
|
||||
TEST_F(MMapTest, MapFixedNoReplaceExisting) {
|
||||
SKIP_IF_NO_MAP_FIXED_NOREPLACE();
|
||||
// Establish a writable mapping and store a sentinel value in it.
|
||||
ASSERT_THAT(Map(0x30000000, kPageSize, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0),
|
||||
SyscallSucceedsWithValue(0x30000000));
|
||||
constexpr uint32_t kSentinel = 0xdeadbeef;
|
||||
auto* val = reinterpret_cast<volatile uint32_t*>(addr_);
|
||||
*val = kSentinel;
|
||||
|
||||
// MAP_FIXED_NOREPLACE over the occupied range must fail with EEXIST, without
|
||||
// relocating to a different address.
|
||||
EXPECT_THAT(reinterpret_cast<uintptr_t>(mmap(
|
||||
reinterpret_cast<void*>(0x30000000), kPageSize, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0)),
|
||||
SyscallFailsWithErrno(EEXIST));
|
||||
|
||||
// The existing mapping must be untouched: the sentinel is still there.
|
||||
EXPECT_EQ(*val, kSentinel);
|
||||
}
|
||||
|
||||
// MAP_FIXED_NOREPLACE fails with EEXIST even if the requested range only
|
||||
// partially overlaps an existing mapping. Compare Linux commit 7aa867dd8952
|
||||
// ("mm/mmap.c: don't clobber partially overlapping mappings with
|
||||
// MAP_FIXED_NOREPLACE").
|
||||
TEST_F(MMapTest, MapFixedNoReplacePartialOverlap) {
|
||||
SKIP_IF_NO_MAP_FIXED_NOREPLACE();
|
||||
ASSERT_THAT(Map(0x30001000, kPageSize, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0),
|
||||
SyscallSucceedsWithValue(0x30001000));
|
||||
EXPECT_THAT(reinterpret_cast<uintptr_t>(mmap(
|
||||
reinterpret_cast<void*>(0x30000000), 2 * kPageSize, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0)),
|
||||
SyscallFailsWithErrno(EEXIST));
|
||||
}
|
||||
|
||||
// MAP_FIXED_NOREPLACE takes precedence over MAP_FIXED: existing mappings are
|
||||
// not clobbered.
|
||||
TEST_F(MMapTest, MapFixedNoReplaceWithMapFixed) {
|
||||
SKIP_IF_NO_MAP_FIXED_NOREPLACE();
|
||||
ASSERT_THAT(Map(0x30000000, kPageSize, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0),
|
||||
SyscallSucceedsWithValue(0x30000000));
|
||||
EXPECT_THAT(reinterpret_cast<uintptr_t>(mmap(
|
||||
reinterpret_cast<void*>(0x30000000), kPageSize, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_FIXED_NOREPLACE,
|
||||
-1, 0)),
|
||||
SyscallFailsWithErrno(EEXIST));
|
||||
}
|
||||
|
||||
// Like MAP_FIXED, MAP_FIXED_NOREPLACE requires a page-aligned address.
|
||||
TEST_F(MMapTest, MapFixedNoReplaceAlignment) {
|
||||
SKIP_IF_NO_MAP_FIXED_NOREPLACE();
|
||||
EXPECT_THAT(Map(0x30000001, kPageSize, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
}
|
||||
|
||||
// MAP_STACK allowed.
|
||||
// There isn't a good way to verify it did anything.
|
||||
TEST_F(MMapTest, MapStack) {
|
||||
|
||||
Reference in New Issue
Block a user