Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FFI stubbing for libc crate only works if libc crate is imported via feature(rustc_private) #2673

Open
roypat opened this issue Aug 11, 2023 · 4 comments
Assignees
Labels
[C] Bug This is a bug. Something isn't working.
Milestone

Comments

@roypat
Copy link

roypat commented Aug 11, 2023

I tried this code:

#[allow(unused)]
mod stubs {
    use libc::{c_int, c_long};

    pub unsafe extern "C" fn sysconf(_input: c_int) -> c_long {
        1
    }
}

#[kani::proof]
#[kani::stub(libc::sysconf, stubs::sysconf)]
fn verify_sysconf_stub() {
    assert_eq!(1, unsafe{ libc::sysconf(libc::_SC_PAGE_SIZE) });
}

using the following command line invocation:

cargo kani --enable-unstable --enable-stubbing

with Kani version: 0.34.0

I expected to see this happen: sysconf to be successfully stubbed and verification to succeed

Instead, this happened: sysconf was not stubbed out, and instead the CMBC model was used. Because of this, verification failed with

$ cargo kani --enable-unstable --enable-stubbing
Kani Rust Verifier 0.34.0 (cargo plugin)
   Compiling kani_test v0.1.0 (/home/ANT.AMAZON.COM/roypat/Development/kani_test)
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
Checking harness verify_sysconf_stub...
CBMC 5.89.0 (cbmc-5.89.0)
CBMC version 5.89.0 (cbmc-5.89.0) 64-bit x86_64 linux
Reading GOTO program from file /home/ANT.AMAZON.COM/roypat/Development/kani_test/target/kani/x86_64-unknown-linux-gnu/debug/deps/kani_test-c8538ab98039f13c__RNvCs1HTYkq7Kxv9_9kani_test19verify_sysconf_stub.out
Generating GOTO Program
Adding CPROVER library (x86_64)
Removal of function pointers and virtual functions
Generic Property Instrumentation
Running with 16 object bits, 48 offset bits (user-specified)
Starting Bounded Model Checking
Runtime Symex: 0.00113693s
size of program expression: 58 steps
slicing removed 29 assignments
Generated 8 VCC(s), 2 remaining after simplification
Runtime Postprocess Equation: 2.1764e-05s
Passing problem to propositional reduction
converting SSA
Runtime Convert SSA: 0.000108755s
Running propositional reduction
Post-processing
Runtime Post-process: 8.234e-06s
Solving with CaDiCaL sc2021
66 variables, 67 clauses
SAT checker: instance is SATISFIABLE
Runtime Solver: 3.7576e-05s
Runtime decision procedure: 0.000179441s

RESULTS:
Check 1: verify_sysconf_stub.assertion.1
	- Status: FAILURE
	- Description: "assertion failed: 1 == unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) }"
	- Location: src/main.rs:23:9 in function verify_sysconf_stub

Check 2: sysconf.pointer_dereference.1
	- Status: SUCCESS
	- Description: "dereference failure: pointer NULL"
	- Location: <builtin-library-sysconf>:22 in function sysconf

Check 3: sysconf.pointer_dereference.2
	- Status: SUCCESS
	- Description: "dereference failure: pointer invalid"
	- Location: <builtin-library-sysconf>:22 in function sysconf

Check 4: sysconf.pointer_dereference.3
	- Status: SUCCESS
	- Description: "dereference failure: deallocated dynamic object"
	- Location: <builtin-library-sysconf>:22 in function sysconf

Check 5: sysconf.pointer_dereference.4
	- Status: SUCCESS
	- Description: "dereference failure: dead object"
	- Location: <builtin-library-sysconf>:22 in function sysconf

Check 6: sysconf.pointer_dereference.5
	- Status: SUCCESS
	- Description: "dereference failure: pointer outside object bounds"
	- Location: <builtin-library-sysconf>:22 in function sysconf

Check 7: sysconf.pointer_dereference.6
	- Status: SUCCESS
	- Description: "dereference failure: invalid integer address"
	- Location: <builtin-library-sysconf>:22 in function sysconf


SUMMARY:
 ** 1 of 7 failed
Failed Checks: assertion failed: 1 == unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) }
 File: "/home/ANT.AMAZON.COM/roypat/Development/kani_test/src/main.rs", line 23, in verify_sysconf_stub

VERIFICATION:- FAILED
Verification Time: 0.016494747s

Summary:
Verification failed for - verify_sysconf_stub
Complete - 0 successfully verified harnesses, 1 failures, 1 total.

If I remove libc from my Cargo.toml and instead put

#![feature(rustc_private)]
extern crate libc;

at the top of my file, verification succeeds as expected. This is why the integration test added for the FFI stubbing feature added in #2658 passes.

@roypat roypat added the [C] Bug This is a bug. Something isn't working. label Aug 11, 2023
@roypat
Copy link
Author

roypat commented Aug 16, 2023

The workaround provided in #2686 for a similar issue (importing the stubbing target by alias and stubbing our that alias) also works in this case:

#[allow(unused)]
mod stubs {
    use libc::{c_int, c_long};

    pub unsafe extern "C" fn sysconf(_input: c_int) -> c_long {
        1
    }
}

use libc::sysconf as sysconf_alias;

#[kani::proof]
#[kani::stub(sysconf_alias, stubs::sysconf)]
fn verify_sysconf_stub() {
    assert_eq!(1, unsafe{ libc::sysconf(libc::_SC_PAGE_SIZE) });
}

leads to


RESULTS:
Check 1: verify_sysconf_stub.assertion.1
	- Status: SUCCESS
	- Description: "assertion failed: 1 == unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) }"
	- Location: src/main.rs:38:5 in function verify_sysconf_stub


SUMMARY:
 ** 0 of 1 failed

VERIFICATION:- SUCCESSFUL
Verification Time: 0.02846472s

@roypat
Copy link
Author

roypat commented Aug 16, 2023

Mhh, instead trying to stub out libc::clock_gettime (which is something we'd like to do over at firecracker to make some of our ratelimiter proofs nicer), actually leads to a kani crash:

I tried

mod stubs {
    pub unsafe extern "C" fn clock_gettime(_clock_id: libc::clockid_t, tp: *mut libc::timespec) -> libc::c_int {
        unsafe {
            (*tp).tv_sec = kani::any();
            (*tp).tv_nsec = kani::any();
        }

        0
    }
}


#[kani::proof]
#[kani::stub(libc::clock_gettime, stubs::clock_gettime)]
fn verify_clock_gettime_stub() {
    let i = std::time::Instant::now();
}

and running RUST_BACKTRACE=1 cargo kani --enable-unstable --enable-stubbing leads to

2023-08-16T11:04:50.845806Z ERROR cprover_bindings::goto_program::expr: Argument doesn't check param=Pointer { typ: StructTag("tag-libc::timespec") } arg=Pointer { typ: StructTag("tag-libc::unix::timespec") }
error: internal compiler error: Kani unexpectedly panicked at panicked at cprover_bindings/src/goto_program/expr.rs:661:9:
                                Function call does not type check:
                                func: Expr { value: Symbol { identifier: "_RNvNtCsgn0tUiWO6QV_10playground5stubs13clock_gettime" }, typ: Code { parameters: [Parameter { typ: Signedbv { width: 32 }, identifier: None, base_name: None }, Parameter { typ: Pointer { typ: StructTag("tag-libc::timespec") }, identifier: None, base_name: None }], return_type: Signedbv { width: 32 } }, location: None, size_of_annotation: None }
                                args: [Expr { value: Symbol { identifier: "_ZN3std3sys4unix4time5inner48_$LT$impl$u20$std..sys..unix..time..Timespec$GT$3now17hbf6b40abeb30b878E::1::var_1::clock" }, typ: Signedbv { width: 32 }, location: None, size_of_annotation: None }, Expr { value: Symbol { identifier: "_ZN3std3sys4unix4time5inner48_$LT$impl$u20$std..sys..unix..time..Timespec$GT$3now17hbf6b40abeb30b878E::1::var_6" }, typ: Pointer { typ: StructTag("tag-libc::unix::timespec") }, location: None, size_of_annotation: None }].

thread 'rustc' panicked at cprover_bindings/src/goto_program/expr.rs:661:9:
Function call does not type check:
func: Expr { value: Symbol { identifier: "_RNvNtCsgn0tUiWO6QV_10playground5stubs13clock_gettime" }, typ: Code { parameters: [Parameter { typ: Signedbv { width: 32 }, identifier: None, base_name: None }, Parameter { typ: Pointer { typ: StructTag("tag-libc::timespec") }, identifier: None, base_name: None }], return_type: Signedbv { width: 32 } }, location: None, size_of_annotation: None }
args: [Expr { value: Symbol { identifier: "_ZN3std3sys4unix4time5inner48_$LT$impl$u20$std..sys..unix..time..Timespec$GT$3now17hbf6b40abeb30b878E::1::var_1::clock" }, typ: Signedbv { width: 32 }, location: None, size_of_annotation: None }, Expr { value: Symbol { identifier: "_ZN3std3sys4unix4time5inner48_$LT$impl$u20$std..sys..unix..time..Timespec$GT$3now17hbf6b40abeb30b878E::1::var_6" }, typ: Pointer { typ: StructTag("tag-libc::unix::timespec") }, location: None, size_of_annotation: None }]
stack backtrace:
   0: rust_begin_unwind
             at /rustc/474709a9a2a74a8bcf0055fadb335d0ca0d2d939/library/std/src/panicking.rs:617:5
   1: core::panicking::panic_fmt
             at /rustc/474709a9a2a74a8bcf0055fadb335d0ca0d2d939/library/core/src/panicking.rs:67:14
   2: cprover_bindings::goto_program::expr::Expr::call
   3: kani_compiler::codegen_cprover_gotoc::codegen::statement::<impl kani_compiler::codegen_cprover_gotoc::context::goto_ctx::GotocCtx>::codegen_terminator
   4: kani_compiler::codegen_cprover_gotoc::codegen::block::<impl kani_compiler::codegen_cprover_gotoc::context::goto_ctx::GotocCtx>::codegen_block
   5: kani_compiler::codegen_cprover_gotoc::utils::debug::<impl kani_compiler::codegen_cprover_gotoc::context::goto_ctx::GotocCtx>::call_with_panic_debug_info
   6: kani_compiler::codegen_cprover_gotoc::compiler_interface::GotocCodegenBackend::codegen_items
   7: <kani_compiler::codegen_cprover_gotoc::compiler_interface::GotocCodegenBackend as rustc_codegen_ssa::traits::backend::CodegenBackend>::codegen_crate
   8: <rustc_session::session::Session>::time::<alloc::boxed::Box<dyn core::any::Any>, rustc_interface::passes::start_codegen::{closure#0}>
   9: rustc_interface::passes::start_codegen
  10: <rustc_middle::ty::context::GlobalCtxt>::enter::<<rustc_interface::queries::Queries>::ongoing_codegen::{closure#0}, core::result::Result<alloc::boxed::Box<dyn core::any::Any>, rustc_span::ErrorGuaranteed>>
  11: <rustc_interface::interface::Compiler>::enter::<rustc_driver_impl::run_compiler::{closure#1}::{closure#2}, core::result::Result<core::option::Option<rustc_interface::queries::Linker>, rustc_span::ErrorGuaranteed>>
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Kani unexpectedly panicked during compilation.
Please file an issue here: https://github.com/model-checking/kani/issues/new?labels=bug&template=bug_report.md

[Kani] current codegen item: codegen_function: std::sys::unix::time::inner::<impl std::sys::unix::time::Timespec>::now
_ZN3std3sys4unix4time5inner48_$LT$impl$u20$std..sys..unix..time..Timespec$GT$3now17hbf6b40abeb30b878E
[Kani] current codegen location: Loc { file: "/github/home/.rustup/toolchains/nightly-2023-08-04-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/unix/time.rs", function: None, start_line: 397, start_col: Some(9), end_line: 397, end_col: Some(55) }
error: could not compile `playground` (bin "playground"); 10 warnings emitted
error: Failed to compile `playground` due to an internal compiler error.: error: internal compiler error: Kani unexpectedly panicked at panicked at cprover_bindings/src/goto_program/expr.rs:661:9:
                                Function call does not type check:
                                func: Expr { value: Symbol { identifier: "_RNvNtCsgn0tUiWO6QV_10playground5stubs13clock_gettime" }, typ: Code { parameters: [Parameter { typ: Signedbv { width: 32 }, identifier: None, base_name: None }, Parameter { typ: Pointer { typ: StructTag("tag-libc::timespec") }, identifier: None, base_name: None }], return_type: Signedbv { width: 32 } }, location: None, size_of_annotation: None }
                                args: [Expr { value: Symbol { identifier: "_ZN3std3sys4unix4time5inner48_$LT$impl$u20$std..sys..unix..time..Timespec$GT$3now17hbf6b40abeb30b878E::1::var_1::clock" }, typ: Signedbv { width: 32 }, location: None, size_of_annotation: None }, Expr { value: Symbol { identifier: "_ZN3std3sys4unix4time5inner48_$LT$impl$u20$std..sys..unix..time..Timespec$GT$3now17hbf6b40abeb30b878E::1::var_6" }, typ: Pointer { typ: StructTag("tag-libc::unix::timespec") }, location: None, size_of_annotation: None }].

I think this also is a result of the standard library using a "special" version of libc. I tried to match my libc crate version to the version the standard library was compiled against (and also tried all libc versions since 0.2.141), to no avail :(.

Using the rustc_private feature avoids this

@roypat
Copy link
Author

roypat commented Aug 16, 2023

Right, so I found a workaround... ish

For some reason, rustc's libc is not a drop-in-replacement for crates.io libc, so just doing #![feature(rustc_private)] will not work for arbitrary crates (e.g. firecracker). It also means that a nightly compiler would be required for normal builds, which is not ideal. I managed to get things to work by adding #![cfg_attr(kani, feature(rustc_private))] and in my Cargo.toml libc_public = { package = "libc", version = "..." } (and then put use libc_public as libc at the top of each module that does not contain a kani libc stub). In modules for kani stubs, I instead do extern crate libc as rustc_libc and write the stubs in terms of rustc_libc (and this is what requires the "normal" libc crate to be renamed in Cargo.toml, as otherwise extern crate libc will refer to the version from crates.io instead of the rustc internal one.

@celinval
Copy link
Contributor

I think the problem here might be related to different versions of the same crate. The libc you are pulling from crates.io might not have the same version as the one that rustc_private points to, hence the mismatched type.

Your workaround makes sense to me, so you can clearly pinpoint which one you are trying to stub. That said, I think we are mishandling this case, and we should fix that. Let us know if you are blocked though.

@feliperodri feliperodri self-assigned this Aug 31, 2023
@carolynzech carolynzech added this to the Stubbing milestone Sep 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[C] Bug This is a bug. Something isn't working.
Projects
No open projects
Status: In Progress
Development

No branches or pull requests

4 participants