Memory.lean
Declarations on this page (45)
- structure Perm
- def protNone
- def rw
- def rx
- def rwx
- def frozen
-
def _root_.
Option. mapped -
def _root_.
Option. r -
def _root_.
Option. w -
def _root_.
Option. x -
def _root_.
Option. frozen - structure Memory
- def rawPerm
- def perm
- def isLazyFree
- def isAnon
- def read8
- def write8
- def read16
- def write16
- def read32
- def write32
- def read64
- def write64
- def readSized
- def writeSized
- def read128
- def write128
- def readSimdSized
- def writeSimdSized
- def readBytes
- def writeBytes
- def allPerm
- def allNoLazyFree
- def allAnon
- def canRead
- def canWrite
- def canExec
- def canMap
- def countPerm
- def countRw
- def Frozen
- def KeepsFrozen
- def mapZero
-
def SimdMemSize.
bytes
ARM64 Byte-Addressable Little-Endian Virtual Memory (Arm64.)
-
Layer / Role:
Arm64Trusted Computing Base (Role::Spec, definitions only, 0 theorems). -
Purpose & Key Exports: 64-bit byte-addressable virtual memory (
Memory) mapping addresses to per-byte optional permissions and bytes (cells : BitVec 64 → Option (OptionPerm× BitVec 8)), protection flags (Perm,Perm.,protNone Perm.,ro Perm.,rw Perm.,rx canRead,canWrite,canExec,canMap,countPerm,countRw), little-endian accessors (read8,read16,read32,read64,read128,write8,write16,write32,write64,write128,readBytes,writeBytes), and mapping helpers (mapFresh,mapZero,unmapWritable,unmapNonFrozen,setPermNonFrozen,zeroNonFrozen). -
When to read: Use this header or
lean-sig outline Arm64/for API names; useTrusted/ ISA/ Memory.lean ProofLib/andMemory.lean Arm64/for reasoning about memory in proofs.Trusted/ Spec/ SepLogic.lean
Conservative Permissions, Option Perm, & W^X
read*/write* ignore permissions; Arm64. checks canRead/canWrite/canExec before every access and faults otherwise. Writes never alter permissions. A byte's permission in Memory is m.Perm:
-
noneis unmapped (no VMA). Permissions are conservative (at most the real machine's): "unmapped" (none) means "not known to be accessible", not "guaranteed to fault", so proofs must avoid unmapped bytes without deriving facts from unmappedness. -
some pis mapped ((m.) with Linux protection bits p :perm a).mapped = (m. perm a).isSome = true Perm(r,w,x). In particular, somePerm.(protNone some ⟨false, false, false⟩) is a mappedPROT_NONEpage: reserved in the virtual address space (canMap= false, sommapandbrknever overlap it), inaccessible (canRead=canWrite=canExec= false), and not frozen (frozen= false). -
Executable memory is never writable (
canWriterequiresw∧ ¬x, W^X), ensuring loaded instructions remain immutable across execution.
@[expose] public sectionnamespace Arm64structure Perm
Linux protection bits (PROT_READ, PROT_WRITE, PROT_EXEC) and per-byte VM state of a mapped byte of virtual memory: readable (r), writable (w), executable (x), whether the byte is marked lazyfree via madvise(..., MADV_FREE) (lazyFree, asynchronously zeroable until dirtied by a store), and whether the byte belongs to an anonymous mapping (anon, false on file-backed PT_LOAD pages).
structure Perm where
r : Bool
w : Bool
x : Bool
lazyFree : Bool := false
anon : Bool := true
deriving DecidableEq, Repr, Inhabitednamespace Permdef protNone
Mapped with PROT_NONE (---): reserved in the virtual address space, inaccessible to loads, stores, and instruction fetches (r = w = x = false), and not frozen (frozen = false).
def protNone : Perm := ⟨false, false, false, false, true⟩1 declaration not shown (lines 65–66): theorems, which Lean checks, or definitions that no headline statement uses
Arm64.
def rwx
def frozen
The mapped byte is read-only or executable and not writable. Its contents and permission are frozen: nothing during execution may change them (see Memory.).
end Perm
def _root_.
An optional byte permission is mapped iff it is some p (isSome).
@[simp, reducible] def _root_.Option.mapped (p : Option Perm) : Bool := p.isSome
def _root_.
An optional byte permission is readable iff it is some q with q..
def _root_.
An optional byte permission is writable iff it is some q with q..
def _root_.
An optional byte permission is executable iff it is some q with q..
def _root_.
An optional byte permission is frozen iff it is some q with q..
structure Memory
Byte-addressable 64-bit virtual memory: a partial map from addresses to an optional permission (Option Perm) and a byte (BitVec 8). none is an untouched address (unmapped, byte 0).
Representation. Lean core and Std provide no proof-friendly partial map over the full 64-bit address space: Std. / Std. are finite, computational structures whose reasoning principles are about insertion histories, not pointwise lookups. A total function into Option is the simplest faithful model and makes pointwise lemmas (read-over-write, permission-over-write) one-line case splits.
Interface. Clients use the derived accessors read8 (the byte, 0 if absent) and perm (Option Perm: none if unmapped, some p if mapped with protection p) together with the base lemmas of ProofLib. / ProofLib. (read8_write8, perm_write8, perm_mapFresh, ...), never cells directly.
Writes to unmapped addresses. write8 stores some (m., so a raw write to an unmapped address (m.) creates some (none, v) with permission none (unmapped), never some (some ⟨false, false, false⟩, v). This keeps read8_write8 and perm_write8 unconditional while reserving some ⟨false, false, false⟩ (some Perm.) exclusively for mapped PROT_NONE pages.
@[ext]
structure Memory where
cells : BitVec 64 → Option (Option Perm × BitVec 8)namespace Memory2 declarations not shown (lines 135–144): theorems, which Lean checks, or definitions that no headline statement uses
Arm64., Arm64.
def rawPerm
def perm
def isLazyFree
Whether the byte at addr is mapped and marked lazyfree via MADV_FREE (so its contents may be asynchronously reclaimed to zero by Linux 7.3 until overwritten).
def isAnon
Whether the byte at addr is mapped as anonymous memory (vma_is_anonymous on Linux 7.3; false on unmapped bytes and file-backed PT_LOAD pages).
1 declaration not shown (lines 173–178): theorems, which Lean checks, or definitions that no headline statement uses
Arm64.
def read8
Read a single byte (8 bits) at addr (0 if unmapped).
def write8
Write a single byte (8 bits) at addr. Normalized permissions (m.) and m. are unchanged; writing clears lazyFree := false (storing dirties the PTE on Linux 7.3, cancelling lazy reclaim).
def read16
Read a 16-bit halfword in little-endian byte order at addr.
def write16
Write a 16-bit halfword in little-endian byte order at addr.
def read32
Read a 32-bit word in little-endian byte order at addr.
def write32
Write a 32-bit word in little-endian byte order at addr.
def read64
Read a 64-bit doubleword in little-endian byte order at addr.
def read64 (m : Memory) (addr : BitVec 64) : BitVec 64 :=
let b0 := m.read8 (addr + 0#64)
let b1 := m.read8 (addr + 1#64)
let b2 := m.read8 (addr + 2#64)
let b3 := m.read8 (addr + 3#64)
let b4 := m.read8 (addr + 4#64)
let b5 := m.read8 (addr + 5#64)
let b6 := m.read8 (addr + 6#64)
let b7 := m.read8 (addr + 7#64)
b7 ++ b6 ++ b5 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0def write64
Write a 64-bit doubleword in little-endian byte order at addr.
def write64 (m : Memory) (addr : BitVec 64) (val : BitVec 64) : Memory :=
let m := m.write8 (addr + 0#64) (val.extractLsb' 0 8)
let m := m.write8 (addr + 1#64) (val.extractLsb' 8 8)
let m := m.write8 (addr + 2#64) (val.extractLsb' 16 8)
let m := m.write8 (addr + 3#64) (val.extractLsb' 24 8)
let m := m.write8 (addr + 4#64) (val.extractLsb' 32 8)
let m := m.write8 (addr + 5#64) (val.extractLsb' 40 8)
let m := m.write8 (addr + 6#64) (val.extractLsb' 48 8)
let m := m.write8 (addr + 7#64) (val.extractLsb' 56 8)
mdef readSized
Read memory of size bytes (B8, H16, W32, X64), zero-extended or sign-extended to 64 bits.
def readSized (m : Memory) (size : MemWidth) (signed : Bool) (addr : BitVec 64) : BitVec 64 :=
match size with
| .B8 =>
let b := m.read8 addr
if signed then b.signExtend 64 else b.zeroExtend 64
| .H16 =>
let h := m.read16 addr
if signed then h.signExtend 64 else h.zeroExtend 64
| .W32 =>
let w := m.read32 addr
if signed then w.signExtend 64 else w.zeroExtend 64
| .X64 =>
m.read64 addrdef writeSized
Write memory of size bytes (B8, H16, W32, X64) from a 64-bit value.
def read128
Read a 128-bit quadword in little-endian byte order at addr.
def write128
Write a 128-bit quadword in little-endian byte order at addr.
def readSimdSized
Read memory of SimdMemSize (B8, H16, S32, D64, Q128), zero-extended to 128 bits.
def writeSimdSized
Write memory of SimdMemSize (B8, H16, S32, D64, Q128) from a 128-bit SIMD register value.
def writeSimdSized (m : Memory) (size : SimdMemSize) (addr : BitVec 64) (val : BitVec 128) : Memory :=
match size with
| .B8 => m.write8 addr (val.truncate 8)
| .H16 => m.write16 addr (val.truncate 16)
| .S32 => m.write32 addr (val.truncate 32)
| .D64 => m.write64 addr (val.truncate 64)
| .Q128 => m.write128 addr valdef readBytes
Read count contiguous bytes starting at base ([m.).
def writeBytes
Write a contiguous list of bytes starting at base, so that byte i lands at base + i. Used to load statically initialized data (e.g. an exported ELF .rodata image) into memory.
def writeBytes (m : Memory) (base : BitVec 64) : List (BitVec 8) → Memory
| [] => m
| b :: rest => (m.write8 base b).writeBytes (base + 1#64) restPermissions
def allPerm
Every byte of [addr, addr + n) (wrapping modulo 2^64) has an optional permission satisfying p.
def allNoLazyFree
Every byte of [addr, addr + n) (wrapping modulo 2^64) has isLazyFree = false.
def allNoLazyFree (m : Memory) (addr : BitVec 64) : Nat → Bool
| 0 => true
| n + 1 => !m.isLazyFree addr && m.allNoLazyFree (addr + 1#64) ndef allAnon
Every byte of [addr, addr + n) (wrapping modulo 2^64) is mapped anonymous (isAnon = true).
def canRead
[addr, addr + n) is deterministically readable (r = true and not marked lazyFree via MADV_FREE). Reading an unwritten lazyFree byte fails closed because Linux 7.3 may asynchronously reclaim the page to zero between loads (mm/ ttu_anon_lazyfree_folio).
def canRead (m : Memory) (addr : BitVec 64) (n : Nat) : Bool :=
m.allPerm (·.r) addr n && m.allNoLazyFree addr ndef canWrite
def canExec
[addr, addr + n) is executable.
def canMap
[addr, addr + n) is unmapped (!(m. for every byte).
def countPerm
Number of bytes in [addr, addr + n) (wrapping modulo 2^64) whose optional permission satisfies p.
def countRw
def Frozen
Byte a is frozen in m: mapped read-only or executable and not writable (Option.).
def KeepsFrozen
m' keeps every byte that is frozen in m: same contents, same permission, same lazyFree bit, and same anonymous-mapping bit (isAnon). Every Step does this (instructions only store to canWrite bytes; extern hooks must, see Step.), so code and read-only data stay loaded throughout execution.
def KeepsFrozen (m m' : Memory) : Prop :=
∀ a, m.Frozen a →
m'.read8 a = m.read8 a ∧ m'.perm a = m.perm a ∧
m'.isLazyFree a = m.isLazyFree a ∧ m'.isAnon a = m.isAnon a1 declaration not shown (lines 374–382): theorems, which Lean checks, or definitions that no headline statement uses
Arm64.
def mapZero
Map the currently unmapped bytes of [base, base + len) read-write and zero-filled; mapped bytes are unchanged. Used for the fresh pages of brk growth and anonymous mmap, which Linux zero-fills. Never touches frozen bytes.
end Memory
def SimdMemSize.
Number of bytes accessed by a SIMD load or store of size sz.
end Arm64