Skip to content

Commit

Permalink
Untag user addresses before hanling them in the Sentry
Browse files Browse the repository at this point in the history
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: 715180364
  • Loading branch information
avagin authored and gvisor-bot committed Jan 14, 2025
1 parent 4ba931d commit 66cbf58
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 1 deletion.
6 changes: 6 additions & 0 deletions pkg/hostarch/hostarch_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions pkg/hostarch/hostarch_x86.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
1 change: 1 addition & 0 deletions pkg/sentry/kernel/futex/futex.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions pkg/sentry/mm/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions pkg/sentry/mm/syscalls.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/sentry/platform/kvm/machine_arm64_unsafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(&reg); err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions pkg/sentry/syscalls/linux/sys_mmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
15 changes: 15 additions & 0 deletions test/syscalls/linux/fault.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#define _GNU_SOURCE 1
#include <signal.h>
#include <sys/mman.h>
#include <ucontext.h>
#include <unistd.h>

Expand Down Expand Up @@ -63,6 +64,20 @@ void sigact_handler(int sig, siginfo_t* siginfo, void* context) {
}
}

#if defined(__aarch64__)
#define APPLY_ADDRESS_TAG(addr) ((void*)((uint64)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*)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.
Expand Down

0 comments on commit 66cbf58

Please sign in to comment.