Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5e6bbb9
Created make target and pytest harness to prove verify-rust-std
dkcumming Mar 11, 2026
bdb7462
Updated README with verification requirements
dkcumming Mar 11, 2026
10f3dbe
`wrapping_sub` passing
dkcumming Mar 11, 2026
70654ee
`unchecked_mul` passing
dkcumming Mar 11, 2026
6d61381
`unchecked_shl` passing
dkcumming Mar 11, 2026
a1c6579
Restricted `make test-verify-std-rust` to tests in the verify-std-rust/
dkcumming Mar 11, 2026
4182d26
`unchecked_shr` passing
dkcumming Mar 11, 2026
d29c959
`unchecked_neg` passing
dkcumming Mar 11, 2026
efccba7
`wrapping_shl` added but fails with non-det branching
dkcumming Mar 11, 2026
e568644
Added lemmas for `wrapping_shl` which is now passing
dkcumming Mar 11, 2026
5cf008c
`wrapping_shr` passing
dkcumming Mar 11, 2026
3de3a67
`widening_mul` passing
dkcumming Mar 11, 2026
04c8165
`carrying_mul` passing
dkcumming Mar 11, 2026
be11fc5
Added float harnesses under `.txt` as they have many thunks
dkcumming Mar 13, 2026
29c9a0d
Added `show` for expected output on `-fail` tests
dkcumming Mar 13, 2026
c7259e9
Added README.md for verify-rust-std
dkcumming Mar 13, 2026
24681b1
All proofs are run with `--terminate-on-thunk`
dkcumming Mar 13, 2026
a737317
Added show output for failing tests and moved float test to runable
dkcumming Mar 13, 2026
f54ce95
Use parsed `.smir.json` to avoid repeated LLVM compilation
dkcumming Mar 15, 2026
5b1001c
Merge remote-tracking branch 'origin/master' into verify-rust-std/cha…
dkcumming Mar 20, 2026
0481016
Merge branch 'master' into verify-rust-std/challenge-0011
dkcumming Mar 29, 2026
5e52bbf
Added verify-std-rust as a matrix entry for integration-tests job
dkcumming Mar 29, 2026
42955d8
Added status section to the README of the challenges
dkcumming Mar 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@ jobs:
test-args: '-k "test_prove and not test_prove_termination"'
parallel: 6
timeout: 60
- name: 'Verify Rust Std'
test-args: '-k test_verify_rust_std'
parallel: 6
timeout: 60
- name: 'Remainder'
test-args: '-k "not llvm and not test_run_smir_random and not test_exec_smir and not test_prove_termination and not test_prove"'
test-args: '-k "not llvm and not test_run_smir_random and not test_exec_smir and not test_prove_termination and not test_prove and not test_verify_rust_std"'
parallel: 6
timeout: 20
steps:
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ test-integration: stable-mir-json build
$(UV_RUN) pytest $(TOP_DIR)/kmir/src/tests/integration --maxfail=1 --verbose \
--durations=0 --numprocesses=$(PARALLEL) --dist=worksteal $(TEST_ARGS)

test-verify-rust-std: stable-mir-json build
$(UV_RUN) pytest $(TOP_DIR)/kmir/src/tests/integration/test_integration.py::test_verify_rust_std --maxfail=1 --verbose \
--durations=0 --numprocesses=$(PARALLEL) --dist=worksteal $(TEST_ARGS)

.PHONY: test-stable-mir-ui
test-stable-mir-ui: stable-mir-json build
@test -n "$(RUST_DIR_ROOT)" || (echo "RUST_DIR_ROOT is required. Example: RUST_DIR_ROOT=/path/to/rust make test-stable-mir-ui"; exit 2)
Expand Down
11 changes: 11 additions & 0 deletions kmir/src/kmir/kdist/mir-semantics/lemmas/kmir-lemmas.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ power of two but the semantics will always operate with these particular ones.
rule VAL &Int bitmask128 => VAL requires 0 <=Int VAL andBool VAL <=Int bitmask128 [simplification, preserves-definedness, smt-lemma]
```

Shift operations like `wrapping_shl` mask the shift amount with `BITS - 1` (e.g., `rhs & 7` for `u8`).
When the shift amount is already known to be less than `BITS`, the mask is a no-op.

```k
rule VAL &Int 7 => VAL requires 0 <=Int VAL andBool VAL <Int 8 [simplification, preserves-definedness, smt-lemma]
rule VAL &Int 15 => VAL requires 0 <=Int VAL andBool VAL <Int 16 [simplification, preserves-definedness, smt-lemma]
rule VAL &Int 31 => VAL requires 0 <=Int VAL andBool VAL <Int 32 [simplification, preserves-definedness, smt-lemma]
rule VAL &Int 63 => VAL requires 0 <=Int VAL andBool VAL <Int 64 [simplification, preserves-definedness, smt-lemma]
rule VAL &Int 127 => VAL requires 0 <=Int VAL andBool VAL <Int 128 [simplification, preserves-definedness, smt-lemma]
```

Repeated bit-masking can be simplified in an even more general way:

```k
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Challenge 0011: Safety of Methods for Numeric Primitive Types

Tests for [verify-rust-std challenge 0011](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html), which verifies the safety of public unsafe methods for floats and integers in `core::num`.

## Part 1: Unsafe Integer Methods

All types: i8, i16, i32, i64, i128, u8, u16, u32, u64, u128

- [x] `unchecked_add`
- [x] `unchecked_sub`
- [x] `unchecked_mul`
- [x] `unchecked_shl`
- [x] `unchecked_shr`

Signed only: i8, i16, i32, i64, i128

- [x] `unchecked_neg`

## Part 2: Safe API Verification

All types: i8, i16, i32, i64, i128, u8, u16, u32, u64, u128

- [x] `wrapping_shl`
- [x] `wrapping_shr`

Unsigned only: u8, u16, u32, u64

- [x] `widening_mul`
- [x] `carrying_mul`

## Part 3: Float to Integer Conversion

TODO: Currently floats are unsupported. However there the required harnesses are
added to `to_int_unchecked-fail.rs`, once they are passing this file should be
renamed to `to_int_unchecked.rs` and tests that demonstrate KMIR catching `UB`
should be added to `to_int_unchecked-fail.rs`.

Types: f16, f32, f64, f128

- [ ] `to_int_unchecked`
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#![feature(bigint_helper_methods)]

fn main() {
carrying_mul_u8(0, 0, 0);
carrying_mul_u16(0, 0, 0);
carrying_mul_u32(0, 0, 0);
carrying_mul_u64(0, 0, 0);
}

fn carrying_mul_u8(a: u8, b: u8, c: u8) {
let (lo, hi) = a.carrying_mul(b, c);
let expected = (a as u16) * (b as u16) + (c as u16);
assert!(lo == (expected as u8));
assert!(hi == ((expected >> u8::BITS) as u8));
}

fn carrying_mul_u16(a: u16, b: u16, c: u16) {
let (lo, hi) = a.carrying_mul(b, c);
let expected = (a as u32) * (b as u32) + (c as u32);
assert!(lo == (expected as u16));
assert!(hi == ((expected >> u16::BITS) as u16));
}

fn carrying_mul_u32(a: u32, b: u32, c: u32) {
let (lo, hi) = a.carrying_mul(b, c);
let expected = (a as u64) * (b as u64) + (c as u64);
assert!(lo == (expected as u32));
assert!(hi == ((expected >> u32::BITS) as u32));
}

fn carrying_mul_u64(a: u64, b: u64, c: u64) {
let (lo, hi) = a.carrying_mul(b, c);
let expected = (a as u128) * (b as u128) + (c as u128);
assert!(lo == (expected as u64));
assert!(hi == ((expected >> u64::BITS) as u64));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

┌─ 1 (root, init)
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
│ span: 0
│ (31 steps)
├─ 3
│ #cast ( #adjustRef ( ARG1:Value , 2 ) , castKindTransmute , ty ( 38 ) , ty ( 39
│ span: 32
┃ (1 step)
┣━━┓
┃ │
┃ ├─ 4
┃ │ Integer ( #littleEndianFromBytes ( ELEMS:List ) &Int 340282366920938463463374607
┃ │ span: 32
┃ │
┃ │ (29 steps)
┃ ├─ 6
┃ │ #cast ( Integer ( #littleEndianFromBytes ( ELEMS:List ) &Int 3402823669209384634
┃ │ span: 115
┃ │
┃ │ (1 step)
┃ └─ 7 (leaf, terminal)
┃ thunk ( #cast ( Integer ( #littleEndianFromBytes ( ELEMS:List ) &Int 34028236692
┃ span: 115
┗━━┓
└─ 5 (leaf, terminal)
thunk ( #cast ( #adjustRef ( ARG1:Value , 2 ) , castKindTransmute , ty ( 38 ) ,
span: 32


┌─ 2 (root, leaf, target, terminal)
│ #EndProgram ~> .K


Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

┌─ 1 (root, init)
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
│ span: 0
│ (31 steps)
├─ 3
│ #cast ( #adjustRef ( ARG1:Value , 2 ) , castKindTransmute , ty ( 22 ) , ty ( 23
│ span: 32
┃ (1 step)
┣━━┓
┃ │
┃ ├─ 4
┃ │ Integer ( #littleEndianFromBytes ( ELEMS:List ) &Int 65535 , 16 , false )
~> #fr
┃ │ span: 32
┃ │
┃ │ (29 steps)
┃ ├─ 6
┃ │ #cast ( Integer ( #littleEndianFromBytes ( ELEMS:List ) &Int 65535 &Int 32767 ,
┃ │ span: 56
┃ │
┃ │ (1 step)
┃ └─ 7 (leaf, terminal)
┃ thunk ( #cast ( Integer ( #littleEndianFromBytes ( ELEMS:List ) &Int 65535 &Int
┃ span: 56
┗━━┓
└─ 5 (leaf, terminal)
thunk ( #cast ( #adjustRef ( ARG1:Value , 2 ) , castKindTransmute , ty ( 22 ) ,
span: 32


┌─ 2 (root, leaf, target, terminal)
│ #EndProgram ~> .K


Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

┌─ 1 (root, init)
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
│ span: 0
│ (22 steps)
└─ 3 (stuck, leaf)
#execIntrinsic ( IntrinsicFunction ( symbol ( "fabsf32" ) ) , operandMove ( plac
span: 73


┌─ 2 (root, leaf, target, terminal)
│ #EndProgram ~> .K


Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

┌─ 1 (root, init)
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
│ span: 0
│ (22 steps)
└─ 3 (stuck, leaf)
#execIntrinsic ( IntrinsicFunction ( symbol ( "fabsf64" ) ) , operandMove ( plac
span: 90


┌─ 2 (root, leaf, target, terminal)
│ #EndProgram ~> .K


Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

┌─ 1 (root, init)
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
│ span: 0
│ (56 steps)
├─ 3
│ #applyBinOp ( binOpAddUnchecked , Integer ( ARG_INT1:Int , 128 , true ) , Intege
│ span: 301
┃ (1 step)
┣━━┓
┃ │
┃ ├─ 4
┃ │ Integer ( truncate ( ARG_INT1:Int +Int ARG_INT2:Int , 128 , Signed ) , 128 , tru
┃ │ span: 301
┃ │
┃ │ (70 steps)
┃ ├─ 6 (terminal)
┃ │ #EndProgram ~> .K
┃ │
┃ ┊ constraint: true
┃ ┊ subst: ...
┃ └─ 2 (leaf, target, terminal)
┃ #EndProgram ~> .K
┗━━┓
└─ 5 (leaf, terminal)
thunk ( #applyBinOp ( binOpAddUnchecked , Integer ( ARG_INT1:Int , 128 , true )
span: 301



Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

┌─ 1 (root, init)
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
│ span: 0
│ (56 steps)
├─ 3
│ #applyBinOp ( binOpAddUnchecked , Integer ( ARG_INT1:Int , 16 , true ) , Integer
│ span: 115
┃ (1 step)
┣━━┓
┃ │
┃ ├─ 4
┃ │ Integer ( truncate ( ARG_INT1:Int +Int ARG_INT2:Int , 16 , Signed ) , 16 , true
┃ │ span: 115
┃ │
┃ │ (70 steps)
┃ ├─ 6 (terminal)
┃ │ #EndProgram ~> .K
┃ │
┃ ┊ constraint: true
┃ ┊ subst: ...
┃ └─ 2 (leaf, target, terminal)
┃ #EndProgram ~> .K
┗━━┓
└─ 5 (leaf, terminal)
thunk ( #applyBinOp ( binOpAddUnchecked , Integer ( ARG_INT1:Int , 16 , true ) ,
span: 115



Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

┌─ 1 (root, init)
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
│ span: 0
│ (56 steps)
├─ 3
│ #applyBinOp ( binOpAddUnchecked , Integer ( ARG_INT1:Int , 32 , true ) , Integer
│ span: 146
┃ (1 step)
┣━━┓
┃ │
┃ ├─ 4
┃ │ Integer ( truncate ( ARG_INT1:Int +Int ARG_INT2:Int , 32 , Signed ) , 32 , true
┃ │ span: 146
┃ │
┃ │ (70 steps)
┃ ├─ 6 (terminal)
┃ │ #EndProgram ~> .K
┃ │
┃ ┊ constraint: true
┃ ┊ subst: ...
┃ └─ 2 (leaf, target, terminal)
┃ #EndProgram ~> .K
┗━━┓
└─ 5 (leaf, terminal)
thunk ( #applyBinOp ( binOpAddUnchecked , Integer ( ARG_INT1:Int , 32 , true ) ,
span: 146



Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

┌─ 1 (root, init)
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
│ span: 0
│ (56 steps)
├─ 3
│ #applyBinOp ( binOpAddUnchecked , Integer ( ARG_INT1:Int , 64 , true ) , Integer
│ span: 177
┃ (1 step)
┣━━┓
┃ │
┃ ├─ 4
┃ │ Integer ( truncate ( ARG_INT1:Int +Int ARG_INT2:Int , 64 , Signed ) , 64 , true
┃ │ span: 177
┃ │
┃ │ (70 steps)
┃ ├─ 6 (terminal)
┃ │ #EndProgram ~> .K
┃ │
┃ ┊ constraint: true
┃ ┊ subst: ...
┃ └─ 2 (leaf, target, terminal)
┃ #EndProgram ~> .K
┗━━┓
└─ 5 (leaf, terminal)
thunk ( #applyBinOp ( binOpAddUnchecked , Integer ( ARG_INT1:Int , 64 , true ) ,
span: 177



Loading
Loading