Skip to content

Commit 414b692

Browse files
committed
add rapx-verify-std CI
1 parent 398ff24 commit 414b692

File tree

10 files changed

+154
-1
lines changed

10 files changed

+154
-1
lines changed

.github/workflows/rapx.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: RAPx
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches: [main,rapx-verify-std]
7+
pull_request:
8+
branches: [main,rapx-verify-std]
9+
10+
env:
11+
RAPx_VERSION: "2f8cefc8a4bbd7c347e02e97c61de1d8224776ba"
12+
SAFETY_TOOL_VERSION: "6b77419da66865bb5a36b6c9baec71b3e833d3c9"
13+
14+
jobs:
15+
check-with-rapx:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
submodules: true
21+
22+
- name: Clone and setup safety_tool
23+
run: |
24+
git clone https://github.com/Artisan-Lab/tag-std.git
25+
cd tag-std
26+
git checkout $SAFETY_TOOL_VERSION
27+
cargo install cargo-expand
28+
cd safety-tool
29+
TOOL_DIR=$(pwd)
30+
echo "TOOL_DIR=$TOOL_DIR" >> $GITHUB_ENV
31+
rm -f rust-toolchain.toml
32+
./gen_rust_toolchain_toml.rs std
33+
34+
- name: Install safety_tool
35+
run: |
36+
export VERIFY_RUST_STD=1
37+
export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
38+
cd tag-std/safety-tool
39+
cargo install --path . --locked -Fstd
40+
safety-tool --version
41+
safety-tool-rfl build-dev
42+
safety-tool-rfl --display-extra-rustc-args
43+
44+
- name: Clone and install RAPx
45+
run: |
46+
rm -rf RAPx
47+
git clone https://github.com/Artisan-Lab/RAPx.git
48+
cd RAPx
49+
git checkout $RAPx_VERSION
50+
./install.sh
51+
52+
- name: Set up RAPx environment
53+
run: |
54+
RUSTUP_TOOLCHAIN=$(rustup show active-toolchain | cut -d ' ' -f 1)
55+
echo "RUSTUP_TOOLCHAIN=$RUSTUP_TOOLCHAIN" >> $GITHUB_ENV
56+
CURRENT_DIR=$(pwd)
57+
RUST_SRC_PATH="$CURRENT_DIR/library"
58+
echo "__CARGO_TESTS_ONLY_SRC_ROOT=$RUST_SRC_PATH" >> $GITHUB_ENV
59+
60+
- name: Run RAPx verification
61+
run: |
62+
cargo new dummy_crate
63+
cd dummy_crate
64+
export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
65+
export RUSTFLAGS="--cfg=rapx -L $TOOL_DIR/target/safety-tool/lib --extern=safety_macro -Zcrate-attr=feature(register_tool) -Zcrate-attr=register_tool(rapx)"
66+
cargo +$RUSTUP_TOOLCHAIN rapx -verify-std -- -Zbuild-std=panic_abort,core,std --target x86_64-unknown-linux-gnu

doc/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- [GOTO Transcoder](./tools/goto-transcoder.md)
1212
- [VeriFast](./tools/verifast.md)
1313
- [Flux](./tools/flux.md)
14+
- [RAPx](./tools/rapx.md)
1415

1516
---
1617

doc/src/tools/rapx.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# RAPx
2+
RAPx is a static analysis platform for Rust programs. It can serve as a companion to the Rust compiler in detecting semantic bugs related to unsafe code.
3+
4+
Using abstract interpretation, RAPx checks for undefined behavior (UB) by pre-tagging unsafe APIs. When a caller calls such a pre-tagged API, RAPx examines all program paths to verify whether the program state meets the tag's conditions—ensuring the caller remains sound under any input and does not trigger UB. Currently, RAPx can detect various types of UB, such as misaligned or dangling pointers, out-of-bounds memory access, and uninitialized memory usage.
5+
6+
## Safety Property Verification
7+
This section will briefly describe several key steps of UB validation by RAPx.
8+
9+
RAPx has a specialized verification module for unsafe Rust code that systematically prevents undefined behavior (UB) through two core steps:
10+
11+
+ Audit Unit Generation: Segment code into verifiable units by analyzing unsafe code propagation patterns.
12+
13+
+ Safety Property Verification: Use contract-based abstract interpretation to validate safety properties.
14+
15+
For comprehensive methodology details and practical examples, see the [module of verification in RAPx-Book](https://artisan-lab.github.io/RAPx-Book/6.4-unsafe.html).
16+
17+
## Installation
18+
See the [quick start section](https://github.com/Artisan-Lab/RAPx?tab=readme-ov-file#quick-start) to learn how to install RAPx. The version of rustc used by RAPx is continuously updated and tries to match the `verify-rust-std` project's version whenever possible.
19+
20+
## Usage
21+
### Usage in verifying third-party crate
22+
After ensuring that RAPx with the correct rustc version has been installed, you can verify the soundness of your code by doing the following steps:
23+
+ Navigate to the root directory of the crate you wish to analyze:
24+
```
25+
cd /to-be-verified-crate/
26+
```
27+
+ Set the required environment variables. Since RAPx by default checks all unsafe calls in the standard library, __CARGO_TESTS_ONLY_SRC_ROOT must point to a pre-annotated version of std. A fully annotated std repository for RAPx linking will be released in the future.
28+
```
29+
export RUSTUP_TOOLCHAIN=$RUSTUP_TOOLCHAIN_OF_RAPX
30+
export __CARGO_TESTS_ONLY_SRC_ROOT=/path-to-pre-annotated-std-lib/library
31+
```
32+
+ Run the verification using the -verify subcommand. RAPx requires linking to the annotated standard library, so the -Zbuild-std argument is necessary:
33+
```
34+
# In Linux
35+
cargo +$RUSTUP_TOOLCHAIN rapx -verify -- -Zbuild-std=panic_abort,core,alloc,std --target x86_64-unknown-linux-gnu
36+
# In Mac(Arm)
37+
cargo +$RUSTUP_TOOLCHAIN rapx -verify -- -Zbuild-std=panic_abort,core,alloc,std --target aarch64-apple-darwin
38+
```
39+
Upon completion, RAPx will output the analysis results regarding the contract compliance of APIs containing unsafe code:
40+
```
41+
|RAP|INFO|: --------In safe function "alloc::vec::into_raw_parts_with_alloc"---------
42+
|RAP|INFO|: Use unsafe api "core::ptr::read".
43+
|RAP|INFO|: Argument 1's passed Sps: {"ValidPtr", "Typed", "Align"}
44+
```
45+
46+
### Usage in verifying the the Rust Standard Library
47+
Apart from the need to set up a `dummy_crate` as the entry point for the verification command, the process is the same as validating third-party libraries. However, RAPx offers a customized command, `-verify-std`, specifically for standard library verification. When using this command, RAPx scans all APIs in the standard library and verifies those annotated with `proof` targets. For example:
48+
```rust
49+
#[cfg_attr(rapx, safety {proof})]
50+
pub fn pop(&mut self) -> Option<T> {
51+
...
52+
}
53+
```
54+
This annotation will be conditionally expanded and verified during RAPx's compilation process.
55+
56+
## Caveats
57+
RAPx provides sound detection of undefined behavior - if any path in the program contains UB, it will be reported. But this guarantee comes with inherent over-approximation of program paths, which may yield false positives where reported UB paths are infeasible in concrete execution.
58+
59+
Besides, RAPx is still under heavy development and can currently validate some SPs mentioned in [tag-std](https://github.com/Artisan-Lab/tag-std/blob/main/primitive-sp.md#21-summary-of-primitive-sps). Continued development and integration are needed to support the verification of the remaining ones.
60+
61+
Finally, RAPx ensures the absence of undefined behavior (UB) on all paths by (i) checking the callee’s preconditions or (ii) tracking subsequent [hazard conditions](https://github.com/Artisan-Lab/tag-std/blob/main/primitive-sp.md#1-overall-idea). RAPx can not provide any proof for the logic correctness of function.
62+

library/alloc/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ check-cfg = [
3434
'cfg(no_global_oom_handling)',
3535
'cfg(no_rc)',
3636
'cfg(no_sync)',
37+
'cfg(rapx)'
3738
]

library/alloc/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ pub mod sync;
234234
pub mod task;
235235
pub mod vec;
236236

237+
#[cfg(rapx)]
238+
mod rapx_macro {
239+
pub use safety_macro::safety;
240+
}
241+
237242
#[doc(hidden)]
238243
#[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")]
239244
pub mod __export {

library/alloc/src/vec/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ use crate::alloc::{Allocator, Global};
9292
use crate::borrow::{Cow, ToOwned};
9393
use crate::boxed::Box;
9494
use crate::collections::TryReserveError;
95+
#[cfg(rapx)]
96+
use crate::rapx_macro::safety;
9597
use crate::raw_vec::RawVec;
9698

9799
mod extract_if;
@@ -1256,6 +1258,7 @@ impl<T, A: Allocator> Vec<T, A> {
12561258
#[must_use = "losing the pointer will leak memory"]
12571259
#[unstable(feature = "allocator_api", issue = "32838")]
12581260
// #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
1261+
#[cfg_attr(rapx, safety {proof})]
12591262
pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) {
12601263
let mut me = ManuallyDrop::new(self);
12611264
let len = me.len();

library/core/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ check-cfg = [
4242
'cfg(target_has_reliable_f128)',
4343
'cfg(target_has_reliable_f128_math)',
4444
'cfg(kani)',
45-
'cfg(flux)'
45+
'cfg(flux)',
46+
'cfg(rapx)'
4647
]
4748

4849
[package.metadata.flux]

library/core/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,3 +434,8 @@ include!("primitive_docs.rs");
434434

435435
#[cfg(flux)]
436436
mod flux_info;
437+
438+
#[cfg(rapx)]
439+
mod rapx_macro {
440+
pub use safety_macro::safety;
441+
}

library/core/src/ptr/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ use crate::kani;
408408
use crate::marker::{FnPtr, PointeeSized};
409409
use crate::mem::{self, MaybeUninit, SizedTypeProperties};
410410
use crate::num::NonZero;
411+
#[cfg(rapx)]
412+
use crate::rapx_macro::safety;
411413
use crate::{fmt, hash, intrinsics, ub_checks};
412414

413415
mod alignment;
@@ -1664,6 +1666,9 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T {
16641666
#[rustc_const_stable(feature = "const_ptr_read", since = "1.71.0")]
16651667
#[track_caller]
16661668
#[rustc_diagnostic_item = "ptr_read"]
1669+
#[cfg_attr(rapx, safety {ValidPtr(src, T, 1)})]
1670+
#[cfg_attr(rapx, safety {Typed(src, T)})]
1671+
#[cfg_attr(rapx, safety {Align(src, T)})]
16671672
pub const unsafe fn read<T>(src: *const T) -> T {
16681673
// It would be semantically correct to implement this via `copy_nonoverlapping`
16691674
// and `MaybeUninit`, as was done before PR #109035. Calling `assume_init`

library/core/src/ptr/mut_ptr.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use crate::intrinsics::const_eval_select;
77
use crate::kani;
88
use crate::marker::PointeeSized;
99
use crate::mem::{self, SizedTypeProperties};
10+
#[cfg(rapx)]
11+
use crate::rapx_macro::safety;
1012
use crate::slice::{self, SliceIndex};
1113

1214
impl<T: PointeeSized> *mut T {
@@ -1041,6 +1043,8 @@ impl<T: PointeeSized> *mut T {
10411043
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
10421044
// verifying that the result remains within the same allocation as `self`.
10431045
#[ensures(|result| (core::mem::size_of::<T>() == 0) || core::ub_checks::same_allocation(self as *const T, *result as *const T))]
1046+
#[cfg_attr(rapx, safety {InBound(self, T, 1)})]
1047+
#[cfg_attr(rapx, safety {ValidNum(count)})]
10441048
pub const unsafe fn add(self, count: usize) -> Self
10451049
where
10461050
T: Sized,

0 commit comments

Comments
 (0)