Skip to content

Commit 5b6e99a

Browse files
authored
Correctly identify extern statics (#2054)
1 parent 40293f9 commit 5b6e99a

2 files changed

Lines changed: 41 additions & 13 deletions

File tree

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

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -497,18 +497,22 @@ impl<'tcx> GotocCtx<'tcx> {
497497
let instance = Instance::mono(self.tcx, def_id);
498498

499499
let sym = self.ensure(&self.symbol_name(instance), |ctx, name| {
500-
// check if this static is extern
501-
let rlinkage = ctx.tcx.codegen_fn_attrs(def_id).linkage;
502-
503-
// we believe rlinkage being `Some` means the static not extern
504-
// based on compiler/rustc_codegen_cranelift/src/linkage.rs#L21
505-
// see https://github.com/model-checking/kani/issues/388
506-
//
507-
// Update: The assertion below may fail in similar environments.
508-
// We are disabling it until we find out the root cause, see
509-
// https://github.com/model-checking/kani/issues/400
510-
//
511-
// assert!(rlinkage.is_none());
500+
// Rust has a notion of "extern static" variables. These are in an "extern" block,
501+
// and so aren't initialized in the current codegen unit. For example (from std):
502+
// extern "C" {
503+
// #[linkage = "extern_weak"]
504+
// static __dso_handle: *mut u8;
505+
// #[linkage = "extern_weak"]
506+
// static __cxa_thread_atexit_impl: *const libc::c_void;
507+
// }
508+
// CBMC shares C's notion of "extern" global variables. However, CBMC mostly does
509+
// not use this information except when doing C typechecking.
510+
// The one exception is handling static variables with no initializer (see
511+
// CBMC's `static_lifetime_init`):
512+
// 1. If they are `is_extern` they are nondet-initialized.
513+
// 2. If they are `!is_extern`, they are zero-initialized.
514+
// So we recognize a Rust "extern" declaration and pass that information along.
515+
let is_extern = ctx.tcx.is_foreign_item(def_id);
512516

513517
let span = ctx.tcx.def_span(def_id);
514518
Symbol::static_variable(
@@ -517,7 +521,7 @@ impl<'tcx> GotocCtx<'tcx> {
517521
ctx.codegen_ty(instance.ty(ctx.tcx, ty::ParamEnv::reveal_all())),
518522
ctx.codegen_span(&span),
519523
)
520-
.with_is_extern(rlinkage.is_none())
524+
.with_is_extern(is_extern)
521525
.with_is_thread_local(is_thread_local)
522526
});
523527
sym.clone().to_expr().address_of()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// kani-verify-fail
5+
6+
//! This test exercises the `is_extern` property that CBMC makes use of during proof initialization.
7+
//! CBMC has two behaviors with an *uninitialized* static variable:
8+
//! 1. If it is declared `extern`, then it is nondet-initialized. (Possible in unsafe Rust)
9+
//! 2. If it is not `extern`, then it is zero-initialized. (Not possible in Rust)
10+
//!
11+
//! Here we test to see that we observe the nondet-initialization.
12+
//! If this extern static were zero-initialized, the assert below would pass.
13+
//! Instead, we expect to see failure, because nondet-initialization could be 1.
14+
15+
extern "C" {
16+
static an_uninitialized_variable: u32;
17+
}
18+
19+
#[kani::proof]
20+
fn check_extern_static_isnt_deterministic() {
21+
// If this is zero-initialized, this assertion will pass, but this
22+
// test is labeled 'kani-verify-fail', and so the test would fail.
23+
assert!(unsafe { an_uninitialized_variable } != 1);
24+
}

0 commit comments

Comments
 (0)