|
| 1 | +//! This fuzzer attempts to test the functional correctness of the `bstr::utf8::validate` function. |
| 2 | +//! This coverage is desirable, because some `unsafe` blocks in the `bstr` crate depend on the |
| 3 | +//! guarantees made by `utf8::validate` - e.g. the soundness of `bstr::ByteSlice::to_str` depends |
| 4 | +//! on these guarantees. |
| 5 | +//! |
| 6 | +//! The `utf8::validate` function is in a non-public module, which means that we can't test it |
| 7 | +//! directly. Therefore we test via `bstr::ByteSlice::to_str` instead. |
| 8 | +//! |
| 9 | +//! We use the following [test oracle](https://en.wikipedia.org/wiki/Test_oracle) to validate |
| 10 | +//! results returned by `utf8::validate`: |
| 11 | +//! |
| 12 | +//! * A standard library implementation (`std::str::from_utf8` is analogous to |
| 13 | +//! `bstr::ByteSlice::to_str` and `run_utf8_validation` in `core/str/validations.rs` is analogous |
| 14 | +//! to `bstr::utf8::validate`). |
| 15 | +//! https://github.com/BurntSushi/bstr/issues/25#issuecomment-543835601 explains |
| 16 | +//! why `bstr` doesn't reuse the standard library's implementation. |
| 17 | +//! * TODO: Consider also adding a manual, simple (and therefore hopefully "obviously correct") |
| 18 | +//! implementation as another test oracle. |
| 19 | +
|
| 20 | +#![no_main] |
| 21 | + |
| 22 | +use bstr::ByteSlice; |
| 23 | +use libfuzzer_sys::fuzz_target; |
| 24 | + |
| 25 | +fn validate(data: &[u8]) { |
| 26 | + let bstr_result = data.to_str(); |
| 27 | + let std_result = std::str::from_utf8(data); |
| 28 | + |
| 29 | + match bstr_result { |
| 30 | + Ok(bstr_str) => { |
| 31 | + let Ok(std_str) = std_result else { |
| 32 | + panic!("`bstr` succeeded but `std` failed"); |
| 33 | + }; |
| 34 | + assert_eq!(data.as_ptr(), bstr_str.as_ptr()); |
| 35 | + assert_eq!(data.as_ptr(), std_str.as_ptr()); |
| 36 | + assert_eq!(data.len(), bstr_str.len()); |
| 37 | + assert_eq!(data.len(), std_str.len()); |
| 38 | + } |
| 39 | + Err(bstr_err) => { |
| 40 | + let Err(std_err) = std_result else { |
| 41 | + panic!("`bstr` failed but `std` succeeded"); |
| 42 | + }; |
| 43 | + assert_eq!(bstr_err.error_len(), std_err.error_len()); |
| 44 | + assert_eq!(bstr_err.valid_up_to(), std_err.valid_up_to()); |
| 45 | + } |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +fuzz_target!(|data: &[u8]| { |
| 50 | + // Test various alignments, because `utf8::validate` calls into `ascii::first_non_ascii_byte` |
| 51 | + // and the latter is sensitive to the alignment. |
| 52 | + for alignment_offset in 0..=(std::cmp::min(data.len(), 16)) { |
| 53 | + validate(&data[alignment_offset..]); |
| 54 | + } |
| 55 | +}); |
0 commit comments