Skip to content

Commit d926482

Browse files
Towards Proving Memory Initialization (rust-lang#3264)
This PR enables automatic memory initialization proofs for raw pointers in Kani. This is done without any extra instrumentation from the user. Currently, due to high memory consumption and only partial support of pointee types for which memory initialization proofs work, this feature is gated behind `-Z uninit-checks` flag. Note that because it uses shadow memory under the hood, programs using this feature need to pass `-Z ghost-state` flag as well. This PR is a part of working towards rust-lang#3300. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Celina G. Val <[email protected]>
1 parent 2fb97d2 commit d926482

File tree

41 files changed

+2500
-53
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2500
-53
lines changed

kani-compiler/src/args.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,6 @@ pub enum ExtraChecks {
8888
/// Check pointer validity when casting pointers to references.
8989
/// See https://github.com/model-checking/kani/issues/2975.
9090
PtrToRefCast,
91+
/// Check for using uninitialized memory.
92+
Uninit,
9193
}

kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,27 @@ impl<'tcx> GotocCtx<'tcx> {
118118
.typ
119119
.clone();
120120

121+
let shadow_memory_assign = self
122+
.tcx
123+
.all_diagnostic_items(())
124+
.name_to_id
125+
.get(&rustc_span::symbol::Symbol::intern("KaniMemInitShadowMem"))
126+
.map(|attr_id| {
127+
self.tcx
128+
.symbol_name(rustc_middle::ty::Instance::mono(self.tcx, *attr_id))
129+
.name
130+
.to_string()
131+
})
132+
.and_then(|shadow_memory_table| self.symbol_table.lookup(&shadow_memory_table).cloned())
133+
.map(|shadow_memory_symbol| {
134+
vec![Lambda::as_contract_for(
135+
&goto_annotated_fn_typ,
136+
None,
137+
shadow_memory_symbol.to_expr(),
138+
)]
139+
})
140+
.unwrap_or_default();
141+
121142
let assigns = modified_places
122143
.into_iter()
123144
.map(|local| {
@@ -127,6 +148,7 @@ impl<'tcx> GotocCtx<'tcx> {
127148
self.codegen_place_stable(&local.into(), loc).unwrap().goto_expr.dereference(),
128149
)
129150
})
151+
.chain(shadow_memory_assign)
130152
.collect();
131153

132154
FunctionContract::new(assigns)

kani-compiler/src/kani_middle/transform/body.rs

Lines changed: 158 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ pub struct MutableBody {
3737
span: Span,
3838
}
3939

40+
/// Denotes whether instrumentation should be inserted before or after the statement.
41+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42+
pub enum InsertPosition {
43+
Before,
44+
After,
45+
}
46+
4047
impl MutableBody {
4148
/// Get the basic blocks of this builder.
4249
pub fn blocks(&self) -> &[BasicBlock] {
@@ -95,12 +102,13 @@ impl MutableBody {
95102
from: Operand,
96103
pointee_ty: Ty,
97104
mutability: Mutability,
98-
before: &mut SourceInstruction,
105+
source: &mut SourceInstruction,
106+
position: InsertPosition,
99107
) -> Local {
100108
assert!(from.ty(self.locals()).unwrap().kind().is_raw_ptr());
101109
let target_ty = Ty::new_ptr(pointee_ty, mutability);
102110
let rvalue = Rvalue::Cast(CastKind::PtrToPtr, from, target_ty);
103-
self.new_assignment(rvalue, before)
111+
self.new_assignment(rvalue, source, position)
104112
}
105113

106114
/// Add a new assignment for the given binary operation.
@@ -111,21 +119,27 @@ impl MutableBody {
111119
bin_op: BinOp,
112120
lhs: Operand,
113121
rhs: Operand,
114-
before: &mut SourceInstruction,
122+
source: &mut SourceInstruction,
123+
position: InsertPosition,
115124
) -> Local {
116125
let rvalue = Rvalue::BinaryOp(bin_op, lhs, rhs);
117-
self.new_assignment(rvalue, before)
126+
self.new_assignment(rvalue, source, position)
118127
}
119128

120129
/// Add a new assignment.
121130
///
122131
/// Return local where the result is saved.
123-
pub fn new_assignment(&mut self, rvalue: Rvalue, before: &mut SourceInstruction) -> Local {
124-
let span = before.span(&self.blocks);
132+
pub fn new_assignment(
133+
&mut self,
134+
rvalue: Rvalue,
135+
source: &mut SourceInstruction,
136+
position: InsertPosition,
137+
) -> Local {
138+
let span = source.span(&self.blocks);
125139
let ret_ty = rvalue.ty(&self.locals).unwrap();
126140
let result = self.new_local(ret_ty, span, Mutability::Not);
127141
let stmt = Statement { kind: StatementKind::Assign(Place::from(result), rvalue), span };
128-
self.insert_stmt(stmt, before);
142+
self.insert_stmt(stmt, source, position);
129143
result
130144
}
131145

@@ -139,6 +153,7 @@ impl MutableBody {
139153
tcx: TyCtxt,
140154
check_type: &CheckType,
141155
source: &mut SourceInstruction,
156+
position: InsertPosition,
142157
value: Local,
143158
msg: &str,
144159
) {
@@ -168,7 +183,7 @@ impl MutableBody {
168183
unwind: UnwindAction::Terminate,
169184
};
170185
let terminator = Terminator { kind, span };
171-
self.split_bb(source, terminator);
186+
self.split_bb(source, position, terminator);
172187
}
173188
CheckType::Panic | CheckType::NoCore => {
174189
tcx.sess
@@ -182,11 +197,55 @@ impl MutableBody {
182197
}
183198
}
184199

185-
/// Split a basic block right before the source location and use the new terminator
186-
/// in the basic block that was split.
200+
/// Add a new call to the basic block indicated by the given index.
201+
///
202+
/// The new call will have the same span as the source instruction, and the basic block
203+
/// will be split. The source instruction will be adjusted to point to the first instruction in
204+
/// the new basic block.
205+
pub fn add_call(
206+
&mut self,
207+
callee: &Instance,
208+
source: &mut SourceInstruction,
209+
position: InsertPosition,
210+
args: Vec<Operand>,
211+
destination: Place,
212+
) {
213+
let new_bb = self.blocks.len();
214+
let span = source.span(&self.blocks);
215+
let callee_op =
216+
Operand::Copy(Place::from(self.new_local(callee.ty(), span, Mutability::Not)));
217+
let kind = TerminatorKind::Call {
218+
func: callee_op,
219+
args,
220+
destination,
221+
target: Some(new_bb),
222+
unwind: UnwindAction::Terminate,
223+
};
224+
let terminator = Terminator { kind, span };
225+
self.split_bb(source, position, terminator);
226+
}
227+
228+
/// Split a basic block and use the new terminator in the basic block that was split.
187229
///
188230
/// The source is updated to point to the same instruction which is now in the new basic block.
189-
pub fn split_bb(&mut self, source: &mut SourceInstruction, new_term: Terminator) {
231+
pub fn split_bb(
232+
&mut self,
233+
source: &mut SourceInstruction,
234+
position: InsertPosition,
235+
new_term: Terminator,
236+
) {
237+
match position {
238+
InsertPosition::Before => {
239+
self.split_bb_before(source, new_term);
240+
}
241+
InsertPosition::After => {
242+
self.split_bb_after(source, new_term);
243+
}
244+
}
245+
}
246+
247+
/// Split a basic block right before the source location.
248+
fn split_bb_before(&mut self, source: &mut SourceInstruction, new_term: Terminator) {
190249
let new_bb_idx = self.blocks.len();
191250
let (idx, bb) = match source {
192251
SourceInstruction::Statement { idx, bb } => {
@@ -196,9 +255,9 @@ impl MutableBody {
196255
(orig_idx, orig_bb)
197256
}
198257
SourceInstruction::Terminator { bb } => {
199-
let orig_bb = *bb;
258+
let (orig_idx, orig_bb) = (self.blocks[*bb].statements.len(), *bb);
200259
*bb = new_bb_idx;
201-
(self.blocks[orig_bb].statements.len(), orig_bb)
260+
(orig_idx, orig_bb)
202261
}
203262
};
204263
let old_term = mem::replace(&mut self.blocks[bb].terminator, new_term);
@@ -208,16 +267,95 @@ impl MutableBody {
208267
self.blocks.push(new_bb);
209268
}
210269

211-
/// Insert statement before the source instruction and update the source as needed.
212-
pub fn insert_stmt(&mut self, new_stmt: Statement, before: &mut SourceInstruction) {
213-
match before {
270+
/// Split a basic block right after the source location.
271+
fn split_bb_after(&mut self, source: &mut SourceInstruction, mut new_term: Terminator) {
272+
let new_bb_idx = self.blocks.len();
273+
match source {
274+
// Split the current block after the statement located at `source`
275+
// and move the remaining statements into the new one.
214276
SourceInstruction::Statement { idx, bb } => {
215-
self.blocks[*bb].statements.insert(*idx, new_stmt);
216-
*idx += 1;
277+
let (orig_idx, orig_bb) = (*idx, *bb);
278+
*idx = 0;
279+
*bb = new_bb_idx;
280+
let old_term = mem::replace(&mut self.blocks[orig_bb].terminator, new_term);
281+
let bb_stmts = &mut self.blocks[orig_bb].statements;
282+
let remaining = bb_stmts.split_off(orig_idx + 1);
283+
let new_bb = BasicBlock { statements: remaining, terminator: old_term };
284+
self.blocks.push(new_bb);
217285
}
286+
// Make the terminator at `source` point at the new block,
287+
// the terminator of which is a simple Goto instruction.
218288
SourceInstruction::Terminator { bb } => {
219-
// Append statements at the end of the basic block.
220-
self.blocks[*bb].statements.push(new_stmt);
289+
let current_terminator = &mut self.blocks.get_mut(*bb).unwrap().terminator;
290+
// Kani can only instrument function calls like this.
291+
match (&mut current_terminator.kind, &mut new_term.kind) {
292+
(
293+
TerminatorKind::Call { target: Some(target_bb), .. },
294+
TerminatorKind::Call { target: Some(new_target_bb), .. },
295+
) => {
296+
// Set the new terminator to point where previous terminator pointed.
297+
*new_target_bb = *target_bb;
298+
// Point the current terminator to the new terminator's basic block.
299+
*target_bb = new_bb_idx;
300+
// Update the current poisition.
301+
*bb = new_bb_idx;
302+
self.blocks.push(BasicBlock { statements: vec![], terminator: new_term });
303+
}
304+
_ => unimplemented!("Kani can only split blocks after calls."),
305+
};
306+
}
307+
};
308+
}
309+
310+
/// Insert statement before or after the source instruction and update the source as needed.
311+
pub fn insert_stmt(
312+
&mut self,
313+
new_stmt: Statement,
314+
source: &mut SourceInstruction,
315+
position: InsertPosition,
316+
) {
317+
match position {
318+
InsertPosition::Before => {
319+
match source {
320+
SourceInstruction::Statement { idx, bb } => {
321+
self.blocks[*bb].statements.insert(*idx, new_stmt);
322+
*idx += 1;
323+
}
324+
SourceInstruction::Terminator { bb } => {
325+
// Append statements at the end of the basic block.
326+
self.blocks[*bb].statements.push(new_stmt);
327+
}
328+
}
329+
}
330+
InsertPosition::After => {
331+
let new_bb_idx = self.blocks.len();
332+
let span = source.span(&self.blocks);
333+
match source {
334+
SourceInstruction::Statement { idx, bb } => {
335+
self.blocks[*bb].statements.insert(*idx + 1, new_stmt);
336+
*idx += 1;
337+
}
338+
SourceInstruction::Terminator { bb } => {
339+
// Create a new basic block, as we need to append a statement after the terminator.
340+
let current_terminator = &mut self.blocks.get_mut(*bb).unwrap().terminator;
341+
// Kani can only instrument function calls in this way.
342+
match &mut current_terminator.kind {
343+
TerminatorKind::Call { target: Some(target_bb), .. } => {
344+
*source = SourceInstruction::Statement { idx: 0, bb: new_bb_idx };
345+
let new_bb = BasicBlock {
346+
statements: vec![new_stmt],
347+
terminator: Terminator {
348+
kind: TerminatorKind::Goto { target: *target_bb },
349+
span,
350+
},
351+
};
352+
*target_bb = new_bb_idx;
353+
self.blocks.push(new_bb);
354+
}
355+
_ => unimplemented!("Kani can only insert statements after calls."),
356+
};
357+
}
358+
}
221359
}
222360
}
223361
}

0 commit comments

Comments
 (0)