Merge branch 'master' into v2_lib
This commit is contained in:
@@ -26,6 +26,9 @@ use std::convert::TryInto;
|
|||||||
// NOTE: We should be able to improve coverage by only checking the last operation. Because
|
// NOTE: We should be able to improve coverage by only checking the last operation. Because
|
||||||
// operations before the last could be checked with a shorter entropy.
|
// operations before the last could be checked with a shorter entropy.
|
||||||
|
|
||||||
|
// NOTE: Maybe we should split the fuzz target in smaller parts (like one per init). We should also
|
||||||
|
// name the fuzz targets with action names.
|
||||||
|
|
||||||
/// Checks the store against a sequence of manipulations.
|
/// Checks the store against a sequence of manipulations.
|
||||||
///
|
///
|
||||||
/// The entropy to generate the sequence of manipulation should be provided in `data`. Debugging
|
/// The entropy to generate the sequence of manipulation should be provided in `data`. Debugging
|
||||||
@@ -181,7 +184,7 @@ impl<'a> Fuzzer<'a> {
|
|||||||
println!("Power on the store.");
|
println!("Power on the store.");
|
||||||
}
|
}
|
||||||
self.increment(StatKey::PowerOnCount);
|
self.increment(StatKey::PowerOnCount);
|
||||||
let interruption = self.interruption(driver.delay_map());
|
let interruption = self.interruption(driver.count_operations());
|
||||||
match driver.partial_power_on(interruption) {
|
match driver.partial_power_on(interruption) {
|
||||||
Err((storage, _)) if self.init.is_dirty() => {
|
Err((storage, _)) if self.init.is_dirty() => {
|
||||||
self.entropy.consume_all();
|
self.entropy.consume_all();
|
||||||
@@ -198,7 +201,7 @@ impl<'a> Fuzzer<'a> {
|
|||||||
if self.debug {
|
if self.debug {
|
||||||
println!("{:?}", operation);
|
println!("{:?}", operation);
|
||||||
}
|
}
|
||||||
let interruption = self.interruption(driver.delay_map(&operation));
|
let interruption = self.interruption(driver.count_operations(&operation));
|
||||||
match driver.partial_apply(operation, interruption) {
|
match driver.partial_apply(operation, interruption) {
|
||||||
Err((store, _)) if self.init.is_dirty() => {
|
Err((store, _)) if self.init.is_dirty() => {
|
||||||
self.entropy.consume_all();
|
self.entropy.consume_all();
|
||||||
@@ -334,59 +337,48 @@ impl<'a> Fuzzer<'a> {
|
|||||||
|
|
||||||
/// Generates an interruption.
|
/// Generates an interruption.
|
||||||
///
|
///
|
||||||
/// The `delay_map` describes the number of modified bits by the upcoming sequence of store
|
/// The `max_delay` describes the number of storage operations.
|
||||||
/// operations.
|
fn interruption(&mut self, max_delay: Option<usize>) -> StoreInterruption {
|
||||||
// TODO(ia0): We use too much CPU to compute the delay map. We should be able to just count the
|
|
||||||
// number of storage operations by checking the remaining delay. We can then use the entropy
|
|
||||||
// directly from the corruption function because it's called at most once.
|
|
||||||
fn interruption(
|
|
||||||
&mut self,
|
|
||||||
delay_map: Result<Vec<usize>, (usize, BufferStorage)>,
|
|
||||||
) -> StoreInterruption {
|
|
||||||
if self.init.is_dirty() {
|
if self.init.is_dirty() {
|
||||||
// We only test that the store can power on without crashing. If it would get
|
// We only test that the store can power on without crashing. If it would get
|
||||||
// interrupted then it's like powering up with a different initial state, which would be
|
// interrupted then it's like powering up with a different initial state, which would be
|
||||||
// tested with another fuzzing input.
|
// tested with another fuzzing input.
|
||||||
return StoreInterruption::none();
|
return StoreInterruption::none();
|
||||||
}
|
}
|
||||||
let delay_map = match delay_map {
|
let max_delay = match max_delay {
|
||||||
Ok(x) => x,
|
Some(x) => x,
|
||||||
Err((delay, storage)) => {
|
None => return StoreInterruption::none(),
|
||||||
print!("{}", storage);
|
|
||||||
panic!("delay={}", delay);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let delay = self.entropy.read_range(0, delay_map.len() - 1);
|
let delay = self.entropy.read_range(0, max_delay);
|
||||||
let mut complete_bits = BitStack::default();
|
|
||||||
for _ in 0..delay_map[delay] {
|
|
||||||
complete_bits.push(self.entropy.read_bit());
|
|
||||||
}
|
|
||||||
if self.debug {
|
if self.debug {
|
||||||
if delay == delay_map.len() - 1 {
|
if delay == max_delay {
|
||||||
assert!(complete_bits.is_empty());
|
|
||||||
println!("Do not interrupt.");
|
println!("Do not interrupt.");
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!("Interrupt after {} operations.", delay);
|
||||||
"Interrupt after {} operations with complete mask {}.",
|
|
||||||
delay, complete_bits
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if delay < delay_map.len() - 1 {
|
if delay < max_delay {
|
||||||
self.increment(StatKey::InterruptionCount);
|
self.increment(StatKey::InterruptionCount);
|
||||||
}
|
}
|
||||||
let corrupt = Box::new(move |old: &mut [u8], new: &[u8]| {
|
let corrupt = Box::new(move |old: &mut [u8], new: &[u8]| {
|
||||||
|
let mut count = 0;
|
||||||
|
let mut total = 0;
|
||||||
for (old, new) in old.iter_mut().zip(new.iter()) {
|
for (old, new) in old.iter_mut().zip(new.iter()) {
|
||||||
for bit in 0..8 {
|
for bit in 0..8 {
|
||||||
let mask = 1 << bit;
|
let mask = 1 << bit;
|
||||||
if *old & mask == *new & mask {
|
if *old & mask == *new & mask {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if complete_bits.pop().unwrap() {
|
total += 1;
|
||||||
|
if self.entropy.read_bit() {
|
||||||
|
count += 1;
|
||||||
*old ^= mask;
|
*old ^= mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.debug {
|
||||||
|
println!("Flip {} bits out of {}.", count, total);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
StoreInterruption { delay, corrupt }
|
StoreInterruption { delay, corrupt }
|
||||||
}
|
}
|
||||||
@@ -432,113 +424,3 @@ impl Init {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compact stack of bits.
|
|
||||||
// NOTE: This would probably go away once the delay map is simplified.
|
|
||||||
#[derive(Default, Clone, Debug)]
|
|
||||||
struct BitStack {
|
|
||||||
/// Bits stored in little-endian (for bytes and bits).
|
|
||||||
///
|
|
||||||
/// The last byte only contains `len` bits.
|
|
||||||
data: Vec<u8>,
|
|
||||||
|
|
||||||
/// Number of bits stored in the last byte.
|
|
||||||
///
|
|
||||||
/// It is 0 if the last byte is full, not 8.
|
|
||||||
len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitStack {
|
|
||||||
/// Returns whether the stack is empty.
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.len() == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the length of the stack.
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
if self.len == 0 {
|
|
||||||
8 * self.data.len()
|
|
||||||
} else {
|
|
||||||
8 * (self.data.len() - 1) + self.len
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pushes a bit to the stack.
|
|
||||||
fn push(&mut self, value: bool) {
|
|
||||||
if self.len == 0 {
|
|
||||||
self.data.push(0);
|
|
||||||
}
|
|
||||||
if value {
|
|
||||||
*self.data.last_mut().unwrap() |= 1 << self.len;
|
|
||||||
}
|
|
||||||
self.len += 1;
|
|
||||||
if self.len == 8 {
|
|
||||||
self.len = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pops a bit from the stack.
|
|
||||||
fn pop(&mut self) -> Option<bool> {
|
|
||||||
if self.len == 0 {
|
|
||||||
if self.data.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
self.len = 8;
|
|
||||||
}
|
|
||||||
self.len -= 1;
|
|
||||||
let result = self.data.last().unwrap() & 1 << self.len;
|
|
||||||
if self.len == 0 {
|
|
||||||
self.data.pop().unwrap();
|
|
||||||
}
|
|
||||||
Some(result != 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for BitStack {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
|
||||||
let mut bits = self.clone();
|
|
||||||
while let Some(bit) = bits.pop() {
|
|
||||||
write!(f, "{}", bit as usize)?;
|
|
||||||
}
|
|
||||||
write!(f, " ({} bits)", self.len())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bit_stack_ok() {
|
|
||||||
let mut bits = BitStack::default();
|
|
||||||
|
|
||||||
assert_eq!(bits.pop(), None);
|
|
||||||
|
|
||||||
bits.push(true);
|
|
||||||
assert_eq!(bits.pop(), Some(true));
|
|
||||||
assert_eq!(bits.pop(), None);
|
|
||||||
|
|
||||||
bits.push(false);
|
|
||||||
assert_eq!(bits.pop(), Some(false));
|
|
||||||
assert_eq!(bits.pop(), None);
|
|
||||||
|
|
||||||
bits.push(true);
|
|
||||||
bits.push(false);
|
|
||||||
assert_eq!(bits.pop(), Some(false));
|
|
||||||
assert_eq!(bits.pop(), Some(true));
|
|
||||||
assert_eq!(bits.pop(), None);
|
|
||||||
|
|
||||||
bits.push(false);
|
|
||||||
bits.push(true);
|
|
||||||
assert_eq!(bits.pop(), Some(true));
|
|
||||||
assert_eq!(bits.pop(), Some(false));
|
|
||||||
assert_eq!(bits.pop(), None);
|
|
||||||
|
|
||||||
let n = 27;
|
|
||||||
for i in 0..n {
|
|
||||||
assert_eq!(bits.len(), i);
|
|
||||||
bits.push(true);
|
|
||||||
}
|
|
||||||
for i in (0..n).rev() {
|
|
||||||
assert_eq!(bits.pop(), Some(true));
|
|
||||||
assert_eq!(bits.len(), i);
|
|
||||||
}
|
|
||||||
assert_eq!(bits.pop(), None);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -311,31 +311,15 @@ impl StoreDriverOff {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mapping from delay time to number of modified bits.
|
/// Returns the number of storage operations to power on.
|
||||||
///
|
///
|
||||||
/// For example if the `i`-th value is `n`, it means that the `i`-th operation modifies `n` bits
|
/// Returns `None` if the store cannot power on successfully.
|
||||||
/// in the storage. For convenience, the vector always ends with `0` for one past the last
|
pub fn count_operations(&self) -> Option<usize> {
|
||||||
/// operation. This permits to choose a random index in the vector and then a random set of bit
|
let initial_delay = usize::MAX;
|
||||||
/// positions among the number of modified bits to simulate any possible corruption (including
|
let mut storage = self.storage.clone();
|
||||||
/// no corruption with the last index).
|
storage.arm_interruption(initial_delay);
|
||||||
pub fn delay_map(&self) -> Result<Vec<usize>, (usize, BufferStorage)> {
|
let mut store = Store::new(storage).ok()?;
|
||||||
let mut result = Vec::new();
|
Some(initial_delay - store.storage_mut().disarm_interruption())
|
||||||
loop {
|
|
||||||
let delay = result.len();
|
|
||||||
let mut storage = self.storage.clone();
|
|
||||||
storage.arm_interruption(delay);
|
|
||||||
match Store::new(storage) {
|
|
||||||
Err((StoreError::StorageError, x)) => storage = x,
|
|
||||||
Err((StoreError::InvalidStorage, mut storage)) => {
|
|
||||||
storage.reset_interruption();
|
|
||||||
return Err((delay, storage));
|
|
||||||
}
|
|
||||||
Ok(_) | Err(_) => break,
|
|
||||||
}
|
|
||||||
result.push(count_modified_bits(&mut storage));
|
|
||||||
}
|
|
||||||
result.push(0);
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,29 +406,15 @@ impl StoreDriverOn {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mapping from delay time to number of modified bits.
|
/// Returns the number of storage operations to apply a store operation.
|
||||||
///
|
///
|
||||||
/// See the documentation of [`StoreDriverOff::delay_map`] for details.
|
/// Returns `None` if the store cannot apply the operation successfully.
|
||||||
///
|
pub fn count_operations(&self, operation: &StoreOperation) -> Option<usize> {
|
||||||
/// [`StoreDriverOff::delay_map`]: struct.StoreDriverOff.html#method.delay_map
|
let initial_delay = usize::MAX;
|
||||||
pub fn delay_map(
|
let mut store = self.store.clone();
|
||||||
&self,
|
store.storage_mut().arm_interruption(initial_delay);
|
||||||
operation: &StoreOperation,
|
store.apply(operation).1.ok()?;
|
||||||
) -> Result<Vec<usize>, (usize, BufferStorage)> {
|
Some(initial_delay - store.storage_mut().disarm_interruption())
|
||||||
let mut result = Vec::new();
|
|
||||||
loop {
|
|
||||||
let delay = result.len();
|
|
||||||
let mut store = self.store.clone();
|
|
||||||
store.storage_mut().arm_interruption(delay);
|
|
||||||
match store.apply(operation).1 {
|
|
||||||
Err(StoreError::StorageError) => (),
|
|
||||||
Err(StoreError::InvalidStorage) => return Err((delay, store.extract_storage())),
|
|
||||||
Ok(()) | Err(_) => break,
|
|
||||||
}
|
|
||||||
result.push(count_modified_bits(store.storage_mut()));
|
|
||||||
}
|
|
||||||
result.push(0);
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Powers off the store.
|
/// Powers off the store.
|
||||||
@@ -629,22 +599,3 @@ impl<'a> StoreInterruption<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Counts the number of bits modified by an interrupted operation.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if an interruption did not trigger.
|
|
||||||
fn count_modified_bits(storage: &mut BufferStorage) -> usize {
|
|
||||||
let mut modified_bits = 0;
|
|
||||||
storage.corrupt_operation(Box::new(|before, after| {
|
|
||||||
modified_bits = before
|
|
||||||
.iter()
|
|
||||||
.zip(after.iter())
|
|
||||||
.map(|(x, y)| (x ^ y).count_ones() as usize)
|
|
||||||
.sum();
|
|
||||||
}));
|
|
||||||
// We should never write the same slice or erase an erased page.
|
|
||||||
assert!(modified_bits > 0);
|
|
||||||
modified_bits
|
|
||||||
}
|
|
||||||
|
|||||||
2
third_party/lang-items/Cargo.toml
vendored
2
third_party/lang-items/Cargo.toml
vendored
@@ -11,7 +11,7 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
libtock_core = { path = "../../third_party/libtock-rs/core", default-features = false, features = ["alloc_init", "custom_panic_handler", "custom_alloc_error_handler"] }
|
libtock_core = { path = "../../third_party/libtock-rs/core", default-features = false, features = ["alloc_init", "custom_panic_handler", "custom_alloc_error_handler"] }
|
||||||
libtock_drivers = { path = "../libtock-drivers" }
|
libtock_drivers = { path = "../libtock-drivers" }
|
||||||
linked_list_allocator = { version = "0.8.1", default-features = false }
|
linked_list_allocator = { version = "0.8.7", default-features = false, features = ["const_mut_refs"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
debug_allocations = []
|
debug_allocations = []
|
||||||
|
|||||||
Reference in New Issue
Block a user