AlphaProof Code: Verifying Signal’s Security at the Machine-Code Level

Paper › arm64-semantics/Arm64/Trusted/Syscalls/Mm › Madvise.leanTier 3 · arm64-semantics

Madvise.lean🔗

Tier 3 · arm64-semantics used by the symbolic proof 2 headline theorems depend on it 88 of 126 lines trusted source ↗

Trusted, reviewed with the engine. The AArch64 model in arm64-semantics is Tier 3: it is reviewed in its own repository (check-trust-guards). The declarations that the headline statements use are shown for completeness.

Declarations on this page (14)

Linux Virtual Memory madvise Syscall

Models the Linux 7.3 madvise (__NR_madvise = 233) syscall (mm/madvise.c:2003 do_madvise), including synchronous MADV_DONTNEED zero-fill (Memory.zeroNonFrozen), asynchronous MADV_FREE lazy-reclaim marking (Memory.madvFreeNonFrozen, mm/madvise.c:808 and mm/rmap.c:2078), and no-op behavioral, transparent-hugepage, and coredump advice constants (mm/madvise.c:1425, include/uapi/asm-generic/mman-common.h:42).

@[expose] public section
namespace Arm64
namespace Memory

def zeroNonFrozenL31–41 ↗#

Zero-fill all non-frozen mapped bytes in [base, base + len), clearing lazyFree := false and preserving their protection bits (m.perm) and anon state; frozen and unmapped bytes (and bytes outside the range) are unchanged. Used for madvise(..., MADV_DONTNEED) on private anonymous memory (mm/madvise.c:936 madvise_dontneed_free, which synchronously zaps PTEs so subsequent faults allocate fresh zero pages). Never touches frozen bytes.

def zeroNonFrozen (m : Memory) (base : BitVec 64) (len : Nat) : Memory := ⟨fun a => if (a - base).toNat < len && (m.perm a).mapped && !(m.perm a).frozen then some ((m.perm a).map (fun p => { p with anon := m.isAnon a }), 0#8) else m.cells a⟩

def madvFreeNonFrozenL43–54 ↗#

Mark all non-frozen mapped bytes in [base, base + len) with lazyFree := true, preserving their protection bits (m.perm), anon state, and byte values; frozen and unmapped bytes (and bytes outside the range) are unchanged. Used for madvise(..., MADV_FREE) on private anonymous memory (mm/madvise.c:808 madvise_free_single_vma, where Linux 7.3 marks folios lazyfree so ttu_anon_lazyfree_folio may asynchronously reclaim unwritten pages to zero at any later point until dirtied by a store). Never touches frozen bytes.

def madvFreeNonFrozen (m : Memory) (base : BitVec 64) (len : Nat) : Memory := ⟨fun a => if (a - base).toNat < len && (m.perm a).mapped && !(m.perm a).frozen then some ((m.perm a).map (fun p => { p with lazyFree := true, anon := m.isAnon a }), m.read8 a) else m.cells a⟩
end Memory

def MADV_NORMALL58–59 ↗#

Linux madvise advice constants (include/uapi/asm-generic/mman-common.h:42-70).

def MADV_NORMAL : BitVec 64 := 0#64

def MADV_RANDOML60 ↗#

def MADV_RANDOM : BitVec 64 := 1#64

def MADV_SEQUENTIALL61 ↗#

def MADV_SEQUENTIAL : BitVec 64 := 2#64

def MADV_WILLNEEDL62 ↗#

def MADV_WILLNEED : BitVec 64 := 3#64

def MADV_DONTNEEDL63 ↗#

def MADV_DONTNEED : BitVec 64 := 4#64

def MADV_FREEL64 ↗#

def MADV_FREE : BitVec 64 := 8#64

def MADV_HUGEPAGEL65 ↗#

def MADV_HUGEPAGE : BitVec 64 := 14#64

def MADV_NOHUGEPAGEL66 ↗#

def MADV_NOHUGEPAGE : BitVec 64 := 15#64

def MADV_DONTDUMPL67 ↗#

def MADV_DONTDUMP : BitVec 64 := 16#64

def MADV_DODUMPL68 ↗#

def MADV_DODUMP : BitVec 64 := 17#64

def SYS_MADVISEL70–71 ↗#

Linux AArch64 madvise syscall number (__NR_madvise = 233, include/uapi/asm-generic/unistd.h:642, mm/madvise.c:2003).

def SYS_MADVISE : BitVec 64 := 233#64

def execMadviseL73–127 ↗#

madvise(addr, len, advice) (__NR_madvise = 233, mm/madvise.c:2003 do_madvise) on non-frozen mapped memory. Models:

  • MADV_DONTNEED (4): synchronously zero-fills private anonymous memory and clears lazyFree;

  • MADV_FREE (8): marks private anonymous memory lazyFree := true so reading before an intervening store fails closed (.Exception, preventing proofs from assuming load1 == load2 across asynchronous ttu_anon_lazyfree_folio reclaim on Linux 7.3);

  • pure behavioral, transparent-hugepage (VM_HUGEPAGE / VM_NOHUGEPAGE), and coredump hints (MADV_NORMAL = 0, MADV_RANDOM = 1, MADV_SEQUENTIAL = 2, MADV_WILLNEED = 3, MADV_HUGEPAGE = 14, MADV_NOHUGEPAGE = 15, MADV_DONTDUMP = 16, MADV_DODUMP = 17);

  • [INTENTIONAL_SCOPE]: MADV_HUGEPAGE = 14 and MADV_NOHUGEPAGE = 15 (mm/huge_memory.c / mm/khugepaged.c hugepage_madvise) assume a CONFIG_TRANSPARENT_HUGEPAGE=y Linux 7.3 kernel profile on normal user memory (!VM_IO && !VM_PFNMAP && !VM_DAX), where setting or clearing VM_HUGEPAGE / VM_NOHUGEPAGE always succeeds with 0 (whereas a kernel built with CONFIG_TRANSPARENT_HUGEPAGE=n or special device VMAs would return -EINVAL);

  • [INTENTIONAL_SCOPE]: MADV_DONTNEED and MADV_FREE on non-anonymous (!isAnon, e.g. file-backed ELF .data) memory fail closed with .Exception "madvise on non-anonymous memory" (on Linux 7.3, MADV_FREE returns -EINVAL when !vma_is_anonymous in mm/madvise.c:821, and MADV_DONTNEED re-faults underlying file bytes instead of zeroing); all other advice values fail closed with .Exception "unmodeled madvise advice".

def execMadvise (s : State) : State := let addr := s.readReg64 Reg.x0 let len := (s.readReg64 Reg.x1).toNat let advice := s.readReg64 Reg.x2 let size := pageAlignUp len let isNoopHint := advice == MADV_NORMAL || advice == MADV_RANDOM || advice == MADV_SEQUENTIAL || advice == MADV_WILLNEED || advice == MADV_HUGEPAGE || advice == MADV_NOHUGEPAGE || advice == MADV_DONTDUMP || advice == MADV_DODUMP if advice != MADV_DONTNEED && advice != MADV_FREE && !isNoopHint then s.setStatus (.Exception "unmodeled madvise advice") else if addr.toNat % PAGE_SIZE != 0 || !decide (addr.toNat + size ≤ 2 ^ 64) then (s.writeReg64 Reg.x0 (-EINVAL)).advancePC else if !decide (addr.toNat + size ≤ 2 ^ 48) then s.setStatus (.Exception "madvise range exceeds user VA space") else if len == 0 then (s.writeReg64 Reg.x0 0#64).advancePC else if !s.mem.allPerm (fun p => !p.frozen) addr size then s.setStatus (.Exception "madvise on frozen memory") else if !s.mem.allPerm (·.mapped) addr size then if s.mem.canMap addr size then (s.writeReg64 Reg.x0 (-ENOMEM)).advancePC else s.setStatus (.Exception "madvise on partially mapped range") else if (advice == MADV_DONTNEED || advice == MADV_FREE) && !s.mem.allAnon addr size then s.setStatus (.Exception "madvise on non-anonymous memory") else if advice == MADV_DONTNEED then ((s.setMem (s.mem.zeroNonFrozen addr size)).writeReg64 Reg.x0 0#64).advancePC else if advice == MADV_FREE then ((s.setMem (s.mem.madvFreeNonFrozen addr size)).writeReg64 Reg.x0 0#64).advancePC else (s.writeReg64 Reg.x0 0#64).advancePC
end Arm64