use vstd::prelude::*;
use crate::spec::{policy::*, cbpf::*};
mod block;
mod builder;
mod eval;
mod expr;
mod machine;
mod rule;
mod syscall;
mod value;
mod words;
use builder::Builder;
#[allow(unused_imports)]
use machine::Regs;
verus! {
#[verifier::external_derive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum CompileError {
#[error("compiled program exceeds the cBPF jump offset limit")]
JmpIdxOverflow,
#[error("syscall signature exceeds the available argument slots")]
SignatureLayout,
#[error("rule condition needs more scratch memory than cBPF has")]
ScratchOverflow,
}
impl Policy {
#[cfg_attr(not(target_os = "linux"), allow(unused))]
pub(crate) fn to_cbpf(&self) -> (res: Result<Program, CompileError>)
requires self.wf()
ensures res matches Ok(prog) ==> {
&&& prog.wf()
&&& forall |data: &[u8]| #[trigger] Event::parse(data) matches Some(ev) ==>
exists |act: Action| {
&&& #[trigger] self.eval(ev, act)
&&& prog.eval(data) == Outcome::Return(act.to_ret())
}
}
{
let mut b = Builder::new();
b.emit(Instr::Ret(RetVal::K(self.act_bad_arch.exec_to_ret())));
proof { Builder::lemma_ret(b.rev@, self.act_bad_arch.to_ret()); }
let mut i = self.archs.len();
while i > 0
invariant
i <= self.archs@.len(),
self.wf(),
b.wf(),
0 < b.rev@.len(),
b.rev@[0] is Ret,
forall |data: &[u8]| Event::parse(data) is Some ==>
#[trigger] Builder::returns_all(b.rev@, data, b.rev@.len(),
self.blocks(Event::of(data), i as int).to_ret()),
decreases i
{
i -= 1;
let ghost prev = b.rev@;
let ghost i0 = i as int + 1;
self.emit_arch_block(&mut b, self.archs[i])?;
proof {
assert forall |data: &[u8]| Event::parse(data) is Some implies
#[trigger] Builder::returns_all(b.rev@, data, b.rev@.len(),
self.blocks(Event::of(data), i as int).to_ret()) by {
assert(Builder::returns_all(prev, data, prev.len(),
self.blocks(Event::of(data), i0).to_ret()));
}
}
}
let ghost gb = b;
let prog = b.finish();
proof {
gb.lemma_wf(prog);
assert forall |data: &[u8]| #[trigger] Event::parse(data) is Some implies
exists |act: Action| {
&&& #[trigger] self.eval(Event::of(data), act)
&&& prog.eval(data) == Outcome::Return(act.to_ret())
} by {
let act = self.blocks(Event::of(data), 0);
prog.lemma_run(data);
assert(prog.instrs@.len() == gb.rev@.len());
assert(Builder::returns_all(gb.rev@, data, gb.rev@.len(), act.to_ret()));
assert(Builder::extends(gb.rev@, gb.rev@));
assert(Builder::returns(gb.rev@, data, gb.rev@.len(), Regs::of(MachineState::init()),
act.to_ret()));
assert(Builder::run(gb.rev@, data, gb.rev@.len(), Regs::of(MachineState::init()))
== Outcome::Return(act.to_ret()));
self.lemma_blocks(Event::of(data), 0);
assert(prog.eval(data) == Outcome::Return(act.to_ret()));
}
}
Ok(prog)
}
}
}