Skip to content

Commit 4d1a924

Browse files
committed
Add loop contracts and harness for run_utf8_validation
1 parent 25ad12b commit 4d1a924

File tree

4 files changed

+34
-2
lines changed

4 files changed

+34
-2
lines changed

library/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@
230230
#![feature(unboxed_closures)]
231231
#![feature(unsized_fn_params)]
232232
#![feature(with_negative_coherence)]
233+
#![feature(proc_macro_hygiene)]
233234
// tidy-alphabetical-end
234235
//
235236
// Target features:

library/core/src/str/validations.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
use super::Utf8Error;
44
use crate::mem;
55

6+
#[cfg(kani)]
7+
use crate::kani;
8+
69
/// Returns the initial codepoint accumulator for the first byte.
710
/// The first byte is special, only want bottom 5 bits for width 2, 4 bits
811
/// for width 3, and 3 bits for width 4.
@@ -132,6 +135,7 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
132135
let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 };
133136
let align = v.as_ptr().align_offset(usize_bytes);
134137

138+
#[safety::loop_invariant(index <= len + ascii_block_size)]
135139
while index < len {
136140
let old_offset = index;
137141
macro_rules! err {
@@ -211,6 +215,7 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
211215
// until we find a word containing a non-ascii byte.
212216
if align != usize::MAX && align.wrapping_sub(index) % usize_bytes == 0 {
213217
let ptr = v.as_ptr();
218+
#[safety::loop_invariant(index <= blocks_end + ascii_block_size && align.wrapping_sub(index) % usize_bytes == 0)]
214219
while index < blocks_end {
215220
// SAFETY: since `align - index` and `ascii_block_size` are
216221
// multiples of `usize_bytes`, `block = ptr.add(index)` is
@@ -228,6 +233,7 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
228233
index += ascii_block_size;
229234
}
230235
// step from the point where the wordwise loop stopped
236+
#[safety::loop_invariant(index <= len)]
231237
while index < len && v[index] < 128 {
232238
index += 1;
233239
}
@@ -271,3 +277,28 @@ pub const fn utf8_char_width(b: u8) -> usize {
271277

272278
/// Mask of the value bits of a continuation byte.
273279
const CONT_MASK: u8 = 0b0011_1111;
280+
281+
#[cfg(kani)]
282+
#[unstable(feature = "kani", issue = "none")]
283+
pub mod verify {
284+
use super::*;
285+
286+
#[kani::proof]
287+
#[kani::unwind(8)]
288+
pub fn check_run_utf8_validation() {
289+
if kani::any() {
290+
// TODO: ARR_SIZE can be `std::usize::MAX` with cbmc argument
291+
// `--arrays-uf-always`
292+
const ARR_SIZE: usize = 1000;
293+
let mut x: [u8; ARR_SIZE] = kani::any();
294+
let mut xs = kani::slice::any_slice_of_array_mut(&mut x);
295+
run_utf8_validation(xs);
296+
} else {
297+
let ptr = kani::any_where::<usize, _>(|val| *val != 0) as *const u8;
298+
kani::assume(ptr.is_aligned());
299+
unsafe{
300+
run_utf8_validation(crate::slice::from_raw_parts(ptr, 0));
301+
}
302+
}
303+
}
304+
}

scripts/run-kani.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ main() {
183183

184184
echo "Running Kani verify-std command..."
185185

186-
"$kani_path" verify-std -Z unstable-options ./library --target-dir "$temp_dir_target" -Z function-contracts -Z mem-predicates --output-format=terse $command_args
186+
"$kani_path" verify-std -Z unstable-options ./library --target-dir "$temp_dir_target" -Z function-contracts -Z mem-predicates -Z loop-contracts --output-format=terse $command_args --enable-unstable --cbmc-args --object-bits 12
187187
}
188188

189189
main

tool_config/kani-version.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
# incompatible with the verify-std repo.
33

44
[kani]
5-
commit = "2565ef65767a696f1d519b42621b4e502e8970d0"
5+
commit = "8400296f5280be4f99820129bc66447e8dff63f4"

0 commit comments

Comments
 (0)