mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Untag user addresses before handling them in the Sentry
Top-Byte-Ignore (TBI) is a feature on all ARMv8.0 CPUs that causes the top byte
of virtual addresses to be ignored on loads and stores. Instead, bit 55 is
extended over bits 56-63 before address translation. This feature allows use of
the (ignored) top byte as a tag or for other in-band metadata.
In Linux, brk()/mmap()/mremap() syscalls don't untag addresses. More details
are in dcde237319e6 ("mm: Avoid creating virtual address aliases in
brk()/mmap()/mremap()")
PiperOrigin-RevId: 715885990
This commit is contained in:
@@ -86,6 +86,12 @@ func ESRAccessType(code uint64) AccessType {
|
||||
}
|
||||
}
|
||||
|
||||
// UntaggedUserAddr clears the tag from the address pointer. Top-Byte-Ignore (TBI0)
|
||||
// is enabled in Linux, so bits[63:56] of user space addresses are ignored.
|
||||
func UntaggedUserAddr(addr Addr) Addr {
|
||||
return Addr(int64(addr<<8) >> 8)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Make sure the page size is 4K on arm64 platform.
|
||||
if size := unix.Getpagesize(); size != PageSize {
|
||||
|
||||
@@ -43,3 +43,8 @@ var (
|
||||
// ByteOrder is the native byte order (little endian).
|
||||
ByteOrder = binary.LittleEndian
|
||||
)
|
||||
|
||||
// UntaggedUserAddr is no-op on x86.
|
||||
func UntaggedUserAddr(addr Addr) Addr {
|
||||
return addr
|
||||
}
|
||||
|
||||
@@ -328,6 +328,7 @@ const (
|
||||
|
||||
// getKey returns a Key representing address addr in c.
|
||||
func getKey(t Target, addr hostarch.Addr, private bool) (Key, error) {
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
// Ensure the address is aligned.
|
||||
// It must be a DWORD boundary.
|
||||
if addr&0x3 != 0 {
|
||||
|
||||
@@ -70,6 +70,7 @@ const (
|
||||
//
|
||||
// Preconditions: length >= 0.
|
||||
func (mm *MemoryManager) CheckIORange(addr hostarch.Addr, length int64) (hostarch.AddrRange, bool) {
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
// Note that access_ok() constrains end even if length == 0.
|
||||
ar, ok := addr.ToRange(uint64(length))
|
||||
return ar, (ok && ar.End <= mm.layout.MaxAddr)
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
//
|
||||
// Preconditions: mm.as != nil.
|
||||
func (mm *MemoryManager) HandleUserFault(ctx context.Context, addr hostarch.Addr, at hostarch.AccessType, sp hostarch.Addr) error {
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
ar, ok := addr.RoundDown().ToRange(hostarch.PageSize)
|
||||
if !ok {
|
||||
return linuxerr.EFAULT
|
||||
@@ -304,6 +305,7 @@ func (mm *MemoryManager) MapStack(ctx context.Context) (hostarch.AddrRange, erro
|
||||
|
||||
// MUnmap implements the semantics of Linux's munmap(2).
|
||||
func (mm *MemoryManager) MUnmap(ctx context.Context, addr hostarch.Addr, length uint64) error {
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
if addr != addr.RoundDown() {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
@@ -358,6 +360,8 @@ const (
|
||||
|
||||
// MRemap implements the semantics of Linux's mremap(2).
|
||||
func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr hostarch.Addr, oldSize uint64, newSize uint64, opts MRemapOpts) (hostarch.Addr, error) {
|
||||
oldAddr = hostarch.UntaggedUserAddr(oldAddr)
|
||||
|
||||
// "Note that old_address has to be page aligned." - mremap(2)
|
||||
if oldAddr.RoundDown() != oldAddr {
|
||||
return 0, linuxerr.EINVAL
|
||||
@@ -627,6 +631,7 @@ func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr hostarch.Addr, oldS
|
||||
|
||||
// MProtect implements the semantics of Linux's mprotect(2).
|
||||
func (mm *MemoryManager) MProtect(addr hostarch.Addr, length uint64, realPerms hostarch.AccessType, growsDown bool) error {
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
if addr.RoundDown() != addr {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
@@ -831,6 +836,7 @@ func (mm *MemoryManager) Brk(ctx context.Context, addr hostarch.Addr) (hostarch.
|
||||
// MLock implements the semantics of Linux's mlock()/mlock2()/munlock(),
|
||||
// depending on mode.
|
||||
func (mm *MemoryManager) MLock(ctx context.Context, addr hostarch.Addr, length uint64, mode memmap.MLockMode) error {
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
// Linux allows this to overflow.
|
||||
la, _ := hostarch.Addr(length + addr.PageOffset()).RoundUp()
|
||||
ar, ok := addr.RoundDown().ToRange(uint64(la))
|
||||
@@ -1101,6 +1107,7 @@ func madviseAddrRange(addr hostarch.Addr, length uint64) (hostarch.AddrRange, er
|
||||
|
||||
// Decommit implements the semantics of Linux's madvise(MADV_DONTNEED).
|
||||
func (mm *MemoryManager) Decommit(addr hostarch.Addr, length uint64) error {
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
ar, err := madviseAddrRange(addr, length)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1293,6 +1300,7 @@ func (mm *MemoryManager) madviseMutateVMAs(addr hostarch.Addr, length uint64, f
|
||||
//
|
||||
// Preconditions: addr and length are page-aligned.
|
||||
func (mm *MemoryManager) SetDontFork(addr hostarch.Addr, length uint64, dontfork bool) error {
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
return mm.madviseMutateVMAs(addr, length, func(vseg vmaIterator) error {
|
||||
vseg.ValuePtr().dontfork = dontfork
|
||||
return nil
|
||||
@@ -1344,6 +1352,7 @@ type MSyncOpts struct {
|
||||
|
||||
// MSync implements the semantics of Linux's msync().
|
||||
func (mm *MemoryManager) MSync(ctx context.Context, addr hostarch.Addr, length uint64, opts MSyncOpts) error {
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
if addr != addr.RoundDown() {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
|
||||
@@ -83,7 +83,8 @@ func (c *vCPU) initArchState() error {
|
||||
}
|
||||
|
||||
// tcr_el1
|
||||
data = _TCR_TXSZ_VA48 | _TCR_CACHE_FLAGS | _TCR_SHARED | _TCR_TG_FLAGS | _TCR_ASID16 | _TCR_IPS_40BITS
|
||||
data = _TCR_TXSZ_VA48 | _TCR_CACHE_FLAGS | _TCR_SHARED | _TCR_TG_FLAGS |
|
||||
_TCR_ASID16 | _TCR_IPS_40BITS | _TCR_TBI0
|
||||
reg.id = _KVM_ARM64_REGS_TCR_EL1
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
|
||||
@@ -214,6 +214,7 @@ func Mincore(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr
|
||||
length := args[1].SizeT()
|
||||
vec := args[2].Pointer()
|
||||
|
||||
addr = hostarch.UntaggedUserAddr(addr)
|
||||
if addr != addr.RoundDown() {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -63,6 +64,20 @@ void sigact_handler(int sig, siginfo_t* siginfo, void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__aarch64__)
|
||||
#define APPLY_ADDRESS_TAG(addr) ((void*)((uint64_t)addr | (1ULL << 57)))
|
||||
|
||||
TEST(TaggedAddressesTest, MemoryFault) {
|
||||
void* addr = mmap(0, kPageSize, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
ASSERT_NE(addr, MAP_FAILED);
|
||||
addr = APPLY_ADDRESS_TAG(addr);
|
||||
((uint64_t*)addr)[0] =
|
||||
5; // trigger a memory fault that is handled in the Sentry.
|
||||
EXPECT_THAT(munmap(addr, kPageSize), SyscallSucceeds());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(FaultTest, InRange) {
|
||||
// Reset the signal handler to do nothing so that it doesn't freak out
|
||||
// the test runner when we fire an alarm.
|
||||
|
||||
Reference in New Issue
Block a user