Skip to content

Commit 1eb9643

Browse files
authored
Add option to enable layout randomization (rust-lang#1623)
1 parent a426181 commit 1eb9643

File tree

5 files changed

+200
-2
lines changed

5 files changed

+200
-2
lines changed

kani-driver/src/args.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,13 @@ pub struct KaniArgs {
191191
/// Disable CBMC's slice formula which prevents values from being assigned to redundant variables in traces.
192192
#[structopt(long, hidden_short_help(true), requires("enable-unstable"))]
193193
pub no_slice_formula: bool,
194+
195+
/// Randomize the layout of structures. This option can help catching code that relies on
196+
/// a specific layout chosen by the compiler that is not guaranteed to be stable in the future.
197+
/// If a value is given, it will be used as the seed for randomization
198+
/// See the `-Z randomize-layout` and `-Z layout-seed` arguments of the rust compiler.
199+
#[structopt(long)]
200+
pub randomize_layout: Option<Option<u64>>,
194201
/*
195202
The below is a "TODO list" of things not yet implemented from the kani_flags.py script.
196203
@@ -351,6 +358,22 @@ impl KaniArgs {
351358
self.cbmc_args.iter().any(|s| s.to_str().unwrap().starts_with("--unwind"));
352359
let natives_unwind = self.default_unwind.is_some() || self.unwind.is_some();
353360

361+
if self.randomize_layout.is_some() && self.concrete_playback.is_some() {
362+
let random_seed = if let Some(seed) = self.randomize_layout.unwrap() {
363+
format!(" -Z layout-seed={}", seed)
364+
} else {
365+
String::new()
366+
};
367+
368+
// `tracing` is not a dependency of `kani-driver`, so we println! here instead.
369+
println!(
370+
"Using concrete playback with --randomize-layout.\n\
371+
The produced tests will have to be played with the same rustc arguments:\n\
372+
-Z randomize-layout{}",
373+
random_seed
374+
);
375+
}
376+
354377
// TODO: these conflicting flags reflect what's necessary to pass current tests unmodified.
355378
// We should consider improving the error messages slightly in a later pull request.
356379
if natives_unwind && extra_unwind {

kani-driver/src/call_single_file.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@ impl KaniSession {
148148
flags.push(abs_type.into());
149149
}
150150

151+
if let Some(seed_opt) = self.args.randomize_layout {
152+
flags.push("-Z".into());
153+
flags.push("randomize-layout".into());
154+
if let Some(seed) = seed_opt {
155+
flags.push("-Z".into());
156+
flags.push(format!("layout-seed={}", seed).into());
157+
}
158+
}
159+
151160
flags.push("-C".into());
152161
flags.push("symbol-mangling-version=v0".into());
153162

kani-driver/src/concrete_playback.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ impl KaniSession {
4343
harness.pretty_name
4444
),
4545
Some(concrete_vals) => {
46-
let concrete_playback = format_unit_test(&harness.mangled_name, &concrete_vals);
46+
let concrete_playback = format_unit_test(
47+
&harness.mangled_name,
48+
&concrete_vals,
49+
self.args.randomize_layout,
50+
);
4751
match playback_mode {
4852
ConcretePlaybackMode::Print => {
4953
println!(
@@ -222,7 +226,14 @@ impl KaniSession {
222226
}
223227

224228
/// Generate a unit test from a list of concrete values.
225-
fn format_unit_test(harness_name: &str, concrete_vals: &[ConcreteVal]) -> UnitTest {
229+
/// `randomize_layout_seed` is `None` when layout is not randomized,
230+
/// `Some(None)` when layout is randomized without seed, and
231+
/// `Some(Some(seed))` when layout is randomized with the seed `seed`.
232+
fn format_unit_test(
233+
harness_name: &str,
234+
concrete_vals: &[ConcreteVal],
235+
randomize_layout_seed: Option<Option<u64>>,
236+
) -> UnitTest {
226237
/*
227238
Given a number of byte vectors, format them as:
228239
// interp_concrete_val_1
@@ -249,10 +260,23 @@ fn format_unit_test(harness_name: &str, concrete_vals: &[ConcreteVal]) -> UnitTe
249260
let hash = hasher.finish();
250261

251262
let concrete_playback_func_name = format!("kani_concrete_playback_{harness_name}_{hash}");
263+
264+
let randomize_layout_message = match randomize_layout_seed {
265+
None => String::new(),
266+
Some(None) => {
267+
"// This test has to be run with rustc option: -Z randomize-layout\n ".to_string()
268+
}
269+
Some(Some(seed)) => format!(
270+
"// This test has to be run with rust options: -Z randomize-layout -Z layout-seed={}\n ",
271+
seed,
272+
),
273+
};
274+
252275
#[rustfmt::skip]
253276
let concrete_playback = format!(
254277
"#[test]
255278
fn {concrete_playback_func_name}() {{
279+
{randomize_layout_message}\
256280
let concrete_vals: Vec<Vec<u8>> = vec![
257281
{vecs_as_str}
258282
];
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// This test only succeeds on the layout of a structure being
5+
// decided in a deterministic way.
6+
// With randomization, there is a very very high probability that it fails (1 - (1/2^100))
7+
8+
// kani-flags: --randomize-layout=0
9+
// kani-verify-fail
10+
11+
macro_rules! make_structs {
12+
($i:ident) => {
13+
struct $i {
14+
a: u32,
15+
b: u16,
16+
}
17+
};
18+
19+
($i:ident, $($rest:ident),+) => {
20+
make_structs!($i);
21+
make_structs!($($rest),+);
22+
};
23+
}
24+
25+
// We only need Foo1 to be clone
26+
impl Clone for Foo1 {
27+
fn clone(&self) -> Self {
28+
Self { a: self.a, b: self.b }
29+
}
30+
}
31+
32+
macro_rules! check_against {
33+
($to_check:ident, $current_against:ty) => {
34+
{
35+
let other: $current_against = unsafe {std::mem::transmute($to_check.clone())};
36+
assert_eq!($to_check.a, other.a);
37+
assert_eq!($to_check.b, other.b);
38+
}
39+
};
40+
($to_check:ident, $current_against:ty, $($rest:ty),+) => {
41+
check_against!($to_check, $current_against);
42+
check_against!($to_check, $($rest),+);
43+
};
44+
}
45+
46+
make_structs!(
47+
Foo1, Foo2, Foo3, Foo4, Foo5, Foo6, Foo7, Foo8, Foo9, Foo10, Foo11, Foo12, Foo13, Foo14, Foo15,
48+
Foo16, Foo17, Foo18, Foo19, Foo20, Foo21, Foo22, Foo23, Foo24, Foo25, Foo26, Foo27, Foo28,
49+
Foo29, Foo30, Foo31, Foo32, Foo33, Foo34, Foo35, Foo36, Foo37, Foo38, Foo39, Foo40, Foo41,
50+
Foo42, Foo43, Foo44, Foo45, Foo46, Foo47, Foo48, Foo49, Foo50, Foo51, Foo52, Foo53, Foo54,
51+
Foo55, Foo56, Foo57, Foo58, Foo59, Foo60, Foo61, Foo62, Foo63, Foo64, Foo65, Foo66, Foo67,
52+
Foo68, Foo69, Foo70, Foo71, Foo72, Foo73, Foo74, Foo75, Foo76, Foo77, Foo78, Foo79, Foo80,
53+
Foo81, Foo82, Foo83, Foo84, Foo85, Foo86, Foo87, Foo88, Foo89, Foo90, Foo91, Foo92, Foo93,
54+
Foo94, Foo95, Foo96, Foo97, Foo98, Foo99, Foo100
55+
);
56+
57+
#[kani::proof]
58+
fn main() {
59+
let a: u32 = kani::any();
60+
let b: u16 = kani::any();
61+
let base = Foo1 { a, b };
62+
check_against!(
63+
base, Foo2, Foo3, Foo4, Foo5, Foo6, Foo7, Foo8, Foo9, Foo10, Foo11, Foo12, Foo13, Foo14,
64+
Foo15, Foo16, Foo17, Foo18, Foo19, Foo20, Foo21, Foo22, Foo23, Foo24, Foo25, Foo26, Foo27,
65+
Foo28, Foo29, Foo30, Foo31, Foo32, Foo33, Foo34, Foo35, Foo36, Foo37, Foo38, Foo39, Foo40,
66+
Foo41, Foo42, Foo43, Foo44, Foo45, Foo46, Foo47, Foo48, Foo49, Foo50, Foo51, Foo52, Foo53,
67+
Foo54, Foo55, Foo56, Foo57, Foo58, Foo59, Foo60, Foo61, Foo62, Foo63, Foo64, Foo65, Foo66,
68+
Foo67, Foo68, Foo69, Foo70, Foo71, Foo72, Foo73, Foo74, Foo75, Foo76, Foo77, Foo78, Foo79,
69+
Foo80, Foo81, Foo82, Foo83, Foo84, Foo85, Foo86, Foo87, Foo88, Foo89, Foo90, Foo91, Foo92,
70+
Foo93, Foo94, Foo95, Foo96, Foo97, Foo98, Foo99, Foo100
71+
);
72+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// This test checks that `--randomize-layout` doesn't affect repr(C) and Kani is still bit-precise
5+
6+
// kani-flags: --randomize-layout=0
7+
8+
macro_rules! make_structs {
9+
($i:ident) => {
10+
#[repr(C)]
11+
struct $i {
12+
a: u32,
13+
b: u16,
14+
}
15+
};
16+
17+
($i:ident, $($rest:ident),+) => {
18+
make_structs!($i);
19+
make_structs!($($rest),+);
20+
};
21+
}
22+
23+
// We only need Foo1 to be clone
24+
impl Clone for Foo1 {
25+
fn clone(&self) -> Self {
26+
Self { a: self.a, b: self.b }
27+
}
28+
}
29+
30+
macro_rules! check_against {
31+
($to_check:ident, $current_against:ty) => {
32+
{
33+
let other: $current_against = unsafe {std::mem::transmute($to_check.clone())};
34+
assert_eq!($to_check.a, other.a);
35+
assert_eq!($to_check.b, other.b);
36+
}
37+
};
38+
($to_check:ident, $current_against:ty, $($rest:ty),+) => {
39+
check_against!($to_check, $current_against);
40+
check_against!($to_check, $($rest),+);
41+
};
42+
}
43+
44+
make_structs!(
45+
Foo1, Foo2, Foo3, Foo4, Foo5, Foo6, Foo7, Foo8, Foo9, Foo10, Foo11, Foo12, Foo13, Foo14, Foo15,
46+
Foo16, Foo17, Foo18, Foo19, Foo20, Foo21, Foo22, Foo23, Foo24, Foo25, Foo26, Foo27, Foo28,
47+
Foo29, Foo30, Foo31, Foo32, Foo33, Foo34, Foo35, Foo36, Foo37, Foo38, Foo39, Foo40, Foo41,
48+
Foo42, Foo43, Foo44, Foo45, Foo46, Foo47, Foo48, Foo49, Foo50, Foo51, Foo52, Foo53, Foo54,
49+
Foo55, Foo56, Foo57, Foo58, Foo59, Foo60, Foo61, Foo62, Foo63, Foo64, Foo65, Foo66, Foo67,
50+
Foo68, Foo69, Foo70, Foo71, Foo72, Foo73, Foo74, Foo75, Foo76, Foo77, Foo78, Foo79, Foo80,
51+
Foo81, Foo82, Foo83, Foo84, Foo85, Foo86, Foo87, Foo88, Foo89, Foo90, Foo91, Foo92, Foo93,
52+
Foo94, Foo95, Foo96, Foo97, Foo98, Foo99, Foo100
53+
);
54+
55+
#[kani::proof]
56+
fn main() {
57+
let a: u32 = kani::any();
58+
let b: u16 = kani::any();
59+
let base = Foo1 { a, b };
60+
check_against!(
61+
base, Foo2, Foo3, Foo4, Foo5, Foo6, Foo7, Foo8, Foo9, Foo10, Foo11, Foo12, Foo13, Foo14,
62+
Foo15, Foo16, Foo17, Foo18, Foo19, Foo20, Foo21, Foo22, Foo23, Foo24, Foo25, Foo26, Foo27,
63+
Foo28, Foo29, Foo30, Foo31, Foo32, Foo33, Foo34, Foo35, Foo36, Foo37, Foo38, Foo39, Foo40,
64+
Foo41, Foo42, Foo43, Foo44, Foo45, Foo46, Foo47, Foo48, Foo49, Foo50, Foo51, Foo52, Foo53,
65+
Foo54, Foo55, Foo56, Foo57, Foo58, Foo59, Foo60, Foo61, Foo62, Foo63, Foo64, Foo65, Foo66,
66+
Foo67, Foo68, Foo69, Foo70, Foo71, Foo72, Foo73, Foo74, Foo75, Foo76, Foo77, Foo78, Foo79,
67+
Foo80, Foo81, Foo82, Foo83, Foo84, Foo85, Foo86, Foo87, Foo88, Foo89, Foo90, Foo91, Foo92,
68+
Foo93, Foo94, Foo95, Foo96, Foo97, Foo98, Foo99, Foo100
69+
);
70+
}

0 commit comments

Comments
 (0)