Mremap.lean
Linux Virtual Memory mremap Syscall
Trusted model of the Linux AArch64 mremap syscall (mm/, include/), defining Memory., SYS_MREMAP, MREMAP_MAYMOVE, and execMremap.
@[expose] public sectionnamespace Arm64namespace Memorydef remapRw
Move or resize a non-frozen Perm. range [oldBase, oldBase + oldSize) onto [newBase, newBase + newSize):
-
frozen bytes (
(m.) are unconditionally preserved;perm a).frozen -
for any non-frozen byte
ain[newBase, newBase + newSize)at offsetoff := (a - newBase).toNat, mapssome (some p, b)where foroff < oldSize(src := oldBase + off)b = m.and p = {read8 src Perm.withrw lazyFree:= m.isLazyFree src, anon := m.isAnon src }, and foroff ≥ oldSizeb = 0#8and p =Perm.;rw -
for any remaining non-frozen byte
ain[oldBase, oldBase + oldSize), unmaps (none); -
all other bytes keep
m..cells a
def remapRw (m : Memory) (oldBase : BitVec 64) (oldSize : Nat)
(newBase : BitVec 64) (newSize : Nat) : Memory :=
⟨fun a =>
if (m.perm a).frozen then
m.cells a
else if (a - newBase).toNat < newSize then
let off := (a - newBase).toNat
let src := oldBase + BitVec.ofNat 64 off
let b := if off < oldSize then m.read8 src else 0#8
let p := if off < oldSize then
{ Perm.rw with lazyFree := m.isLazyFree src, anon := m.isAnon src }
else Perm.rw
some (some p, b)
else if (a - oldBase).toNat < oldSize then
none
else
m.cells a⟩end Memorydef SYS_MREMAP
Linux AArch64 mremap syscall number (__NR_mremap = 216, include/, mm/).
def SYS_MREMAP : BitVec 64 := 216#64def MREMAP_MAYMOVE
mremap flags bit MREMAP_MAYMOVE (1, include/).
def MREMAP_MAYMOVE : BitVec 64 := 1#64def execMremap
mremap(old_address, old_size, new_size, flags) (__NR_mremap = 216, SYSCALL_DEFINE5(mremap) and vma_to_resize / move_vma in mm/), modeled for flags == MREMAP_MAYMOVE (1) on a single mapped Perm. anonymous range:
-
[INTENTIONAL_SCOPE]: any flags ≠MREMAP_MAYMOVEfails closed with .Exception"unmodeled mremap flags"; -
old_address(X0) not page-aligned,new_size == 0(X2 == 0), or 64-bitPAGE_ALIGNoverflow onnew_size(newLen > 2 ^ 64 -PAGE_SIZE) returns-EINVAL(offset_in_pageand!PAGE_ALIGN(new_len)inmm/mremap.c:1819 check_mremap_params); -
old_size == 0(X1 == 0) fails closed with .Exception"unmodeled mremap (old_size == 0)"; -
with oldAligned :=
pageAlignUpoldLen and newAligned :=pageAlignUpnewLen:-
if oldAddr.
toNat + oldAligned >USER_VA_LIMIT, returns-EINVAL; -
if
[oldAddr, oldAddr + oldAligned)intersects any frozen byte, fails closed with .Exception"mremap on frozen memory"; -
if
[oldAddr, oldAddr + oldAligned)is completely unmapped (s.), returnsmem. canMap oldAddr oldAligned -EFAULT(vma_lookupmiss invma_to_resize,mm/);mremap.c:1110 -
if
[oldAddr, oldAddr + oldAligned)is not uniformly somePerm., fails closed with .rw Exception"unmodeled mremap (range not uniformly PROT_READ|PROT_WRITE)"; -
otherwise queries
s.(oracleIdx [INTENTIONAL_OVERAPPROX]):-
if
s.isoracle. mremapFail s. oracleIdx true, consumes the oracle query (s.), leavesnextOracle s.unchanged, and returnsmem -ENOMEM; -
otherwise computes
mUnmapped := s.and sanitizesmem. unmapNonFrozen oldAddr oldAligned s.viaoracle. mmap s. oracleIdx sanitizeMmapAddrmUnmapped cand newAligned.
-
-
def execMremap (s : State) : State :=
let oldAddr := s.readReg64 Reg.x0
let oldLen := (s.readReg64 Reg.x1).toNat
let newLen := (s.readReg64 Reg.x2).toNat
let flags := s.readReg64 Reg.x3
if flags != MREMAP_MAYMOVE then
s.setStatus (.Exception "unmodeled mremap flags")
else if oldAddr.toNat % PAGE_SIZE != 0 || newLen == 0 || !decide (newLen ≤ 2 ^ 64 - PAGE_SIZE) then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if oldLen == 0 then
s.setStatus (.Exception "unmodeled mremap (old_size == 0)")
else
let oldAligned := pageAlignUp oldLen
let newAligned := pageAlignUp newLen
if !decide (oldAddr.toNat + oldAligned ≤ USER_VA_LIMIT) then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if !s.mem.allPerm (fun p => !p.frozen) oldAddr oldAligned then
s.setStatus (.Exception "mremap on frozen memory")
else if s.mem.canMap oldAddr oldAligned then
(s.writeReg64 Reg.x0 EFAULT_RET).advancePC
else if !s.mem.allPerm (· == some Perm.rw) oldAddr oldAligned then
s.setStatus (.Exception "unmodeled mremap (range not uniformly PROT_READ|PROT_WRITE)")
else if !s.mem.allAnon oldAddr oldAligned then
s.setStatus (.Exception "unmodeled mremap on non-anonymous memory")
else if s.oracle.mremapFail s.oracleIdx then
((s.nextOracle).writeReg64 Reg.x0 (-ENOMEM)).advancePC
else
let mUnmapped := s.mem.unmapNonFrozen oldAddr oldAligned
match sanitizeMmapAddr mUnmapped (s.oracle.mmap s.oracleIdx) newAligned with
| none =>
((s.nextOracle).writeReg64 Reg.x0 (-ENOMEM)).advancePC
| some newBase =>
if !decide (newAligned ≤ s.physMemAvail + oldAligned) then
s.setStatus .OutOfMemory
else
let mem' := s.mem.remapRw oldAddr oldAligned newBase newAligned
((((s.setMem mem').setPhysMemAvail
(s.physMemAvail + oldAligned - newAligned)).nextOracle).writeReg64
Reg.x0 newBase).advancePCend Arm64