Skip to content

Commit e928d47

Browse files
committed
add kani harnesses for PhysAddr's Step implementation
The Step implementation uses unsafe. The kani harnesses should give us some confidence that the unsafe code is correct.
1 parent 347d459 commit e928d47

1 file changed

Lines changed: 155 additions & 0 deletions

File tree

src/addr.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,4 +1164,159 @@ mod proofs {
11641164
assert!(Step::forward(start, count) == end);
11651165
}
11661166
}
1167+
1168+
mod phys_addr {
1169+
use super::*;
1170+
1171+
// The next two proof harnesses prove the correctness of the `forward`
1172+
// implementation of PhysAddr.
1173+
1174+
// This harness proves that our implementation can correctly take 0 or 1
1175+
// step starting from any address.
1176+
#[kani::proof]
1177+
fn forward_base_case() {
1178+
let start_raw: u64 = kani::any();
1179+
let Ok(start) = PhysAddr::try_new(start_raw) else {
1180+
return;
1181+
};
1182+
1183+
// Adding 0 to any address should always yield the same address.
1184+
let same = Step::forward(start, 0);
1185+
assert!(start == same);
1186+
1187+
// Manually calculate the expected address after stepping once.
1188+
let expected = match start_raw {
1189+
// Adding 1 to addresses in this range will result in a valid
1190+
// address.
1191+
0x0000_0000_0000_0000..=0xf_ffff_ffff_fffe => Some(start_raw + 1),
1192+
// Adding 1 to this address causes an overflow.
1193+
0xf_ffff_ffff_ffff => None,
1194+
// Bigger addresses do not exist.
1195+
_ => unreachable!(),
1196+
};
1197+
if let Some(expected) = expected {
1198+
// Verify that `expected` is a valid address.
1199+
assert!(PhysAddr::try_new(expected).is_ok());
1200+
}
1201+
// Verify `forward_checked`.
1202+
let next = Step::forward_checked(start, 1);
1203+
assert!(next.map(PhysAddr::as_u64) == expected);
1204+
}
1205+
1206+
// This harness proves that the result of taking two small steps is the
1207+
// same as taking one combined large step.
1208+
#[kani::proof]
1209+
fn forward_induction_step() {
1210+
let start_raw: u64 = kani::any();
1211+
let Ok(start) = PhysAddr::try_new(start_raw) else {
1212+
return;
1213+
};
1214+
1215+
let count1: usize = kani::any();
1216+
let count2: usize = kani::any();
1217+
// If we can take two small steps...
1218+
let Some(next1) = Step::forward_checked(start, count1) else {
1219+
return;
1220+
};
1221+
let Some(next2) = Step::forward_checked(next1, count2) else {
1222+
return;
1223+
};
1224+
1225+
// ...then we can also take one combined large step.
1226+
let count_both = count1 + count2;
1227+
let next_both = Step::forward(start, count_both);
1228+
assert!(next2 == next_both);
1229+
}
1230+
1231+
// The next two proof harnesses prove the correctness of the `backward`
1232+
// implementation of PhysAddr using the `forward` implementation which
1233+
// we've already proven to be correct.
1234+
// They do this by proving the symmetry between those two functions.
1235+
1236+
// This harness proves the correctness of the implementation of `backward`
1237+
// for all inputs for which `forward_checked` succeeds.
1238+
#[kani::proof]
1239+
fn forward_implies_backward() {
1240+
let start_raw: u64 = kani::any();
1241+
let Ok(start) = PhysAddr::try_new(start_raw) else {
1242+
return;
1243+
};
1244+
let count: usize = kani::any();
1245+
1246+
// If `forward_checked` succeeds...
1247+
let Some(end) = Step::forward_checked(start, count) else {
1248+
return;
1249+
};
1250+
1251+
// ...then `backward` succeeds as well.
1252+
let start2 = Step::backward(end, count);
1253+
assert!(start == start2);
1254+
}
1255+
1256+
// This harness proves that for all inputs for which `backward_checked`
1257+
// succeeds, `forward` succeeds as well.
1258+
#[kani::proof]
1259+
fn backward_implies_forward() {
1260+
let end_raw: u64 = kani::any();
1261+
let Ok(end) = PhysAddr::try_new(end_raw) else {
1262+
return;
1263+
};
1264+
let count: usize = kani::any();
1265+
1266+
// If `backward_checked` succeeds...
1267+
let Some(start) = Step::backward_checked(end, count) else {
1268+
return;
1269+
};
1270+
1271+
// ...then `forward` succeeds as well.
1272+
let end2 = Step::forward(start, count);
1273+
assert!(end == end2);
1274+
}
1275+
1276+
// The next two proof harnesses prove the correctness of the
1277+
// `steps_between` implementation of PhysAddr using the `forward`
1278+
// implementation which we've already proven to be correct.
1279+
// They do this by proving the symmetry between those two functions.
1280+
1281+
// This harness proves the correctness of the implementation of
1282+
// `steps_between` for all inputs for which `forward_checked` succeeds.
1283+
#[kani::proof]
1284+
fn forward_implies_steps_between() {
1285+
let start: u64 = kani::any();
1286+
let Ok(start) = PhysAddr::try_new(start) else {
1287+
return;
1288+
};
1289+
let count: usize = kani::any();
1290+
1291+
// If `forward_checked` succeeds...
1292+
let Some(end) = Step::forward_checked(start, count) else {
1293+
return;
1294+
};
1295+
1296+
// ...then `steps_between` succeeds as well.
1297+
assert!(Step::steps_between(&start, &end) == (count, Some(count)));
1298+
}
1299+
1300+
// This harness proves that for all inputs for which `steps_between`
1301+
// succeeds, `forward` succeeds as well.
1302+
#[kani::proof]
1303+
fn steps_between_implies_forward() {
1304+
let start: u64 = kani::any();
1305+
let Ok(start) = PhysAddr::try_new(start) else {
1306+
return;
1307+
};
1308+
let end: u64 = kani::any();
1309+
let Ok(end) = PhysAddr::try_new(end) else {
1310+
return;
1311+
};
1312+
1313+
// If `steps_between` succeeds...
1314+
let Some(count) = Step::steps_between(&start, &end).1 else {
1315+
return;
1316+
};
1317+
1318+
// ...then `forward` succeeds as well.
1319+
assert!(Step::forward(start, count) == end);
1320+
}
1321+
}
11671322
}

0 commit comments

Comments
 (0)