From 8165fd7cfc3b52514feb616bca30deca94d9f688 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 12 Sep 2024 13:19:28 +0200 Subject: [PATCH] refactor interrupt mutation --- fuzzers/FRET/src/systemstate/mutational.rs | 454 ++++++++++----------- 1 file changed, 218 insertions(+), 236 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 28bd379497..837c5c8b2d 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -27,15 +27,15 @@ use super::stg::{STGEdge, STGNode}; pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec { let len = buf.len(); - let mut start_tick : u32 = 0; - let mut ret = Vec::with_capacity(DO_NUM_INTERRUPT); + let mut start_tick; + let mut ret = Vec::with_capacity(min(DO_NUM_INTERRUPT, len/4)); for i in 0..DO_NUM_INTERRUPT { - let mut t : [u8; 4] = [0,0,0,0]; - if len > (i+1)*4 { + let mut buf4b : [u8; 4] = [0,0,0,0]; + if len >= (i+1)*4 { for j in 0usize..4usize { - t[j]=buf[i*4+j]; + buf4b[j]=buf[i*4+j]; } - start_tick = u32::from_le_bytes(t); + start_tick = u32::from_le_bytes(buf4b); if start_tick < FIRST_INT {start_tick=0;} ret.push(start_tick); } else {break;} @@ -53,6 +53,14 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec Vec { + let mut ret = Vec::with_capacity(interrupt_times.len()*4); + for i in interrupt_times { + ret.extend(u32::to_le_bytes(*i)); + } + ret +} + //======================= Custom mutator @@ -136,260 +144,234 @@ where myrand.set_seed(state.rand_mut().next()); - let mut loopcount = 0; - let mut loopbound = 50; - loop { + let mut rerun_count = 0; // count how many times we rerun the executor + // Try many times to find a mutation that is not already in the corpus + let loopbound = 50; + for _ in 0..loopbound { + // Choose which isr to mutate let interrup_config = match myrand.choose(&self.interrup_config) { Some(s) => s, Option::None => return Ok(()) }; let name = format!("isr_{}_times", interrup_config.0); // manager.log(state, LogSeverity::Info, format!("Mutation {}/{}", loopbound, loopcount))?; - loopbound-=1; - let current_case = state.current_testcase()?; - let old_input = current_case.input().as_ref().unwrap(); - let old_interrupt_times = old_input.parts_by_name(&name).next(); - let mut new_input = old_input.clone(); - let mut new_interrupt_times : &mut I = if new_input.parts_by_name(&name).next().is_some() { - new_input.parts_by_name_mut(&name).next().unwrap() - } else { - new_input.add_part(String::from(&name), I::default()); new_input.parts_by_name_mut(&name).next().unwrap() - }.1; - let mut do_rerun = false; - // if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time - { + let curr_case = state.current_testcase()?; + let curr_input = curr_case.input().as_ref().unwrap(); - // produce a slice of absolute interrupt times - let mut interrupt_offsets : [u32; MAX_NUM_INTERRUPT] = [u32::MAX; MAX_NUM_INTERRUPT]; - let mut num_interrupts : usize = 0; + let mut new_input = curr_input.clone(); + let new_interrupt_part : &mut I = if new_input.parts_by_name(&name).next().is_some() { + new_input.parts_by_name_mut(&name).next().unwrap() + } else { + new_input.add_part(String::from(&name), I::default()); new_input.parts_by_name_mut(&name).next().unwrap() + }.1; + let old_interrupt_times = input_bytes_to_interrupt_times(new_interrupt_part.bytes(), *interrup_config); + let mut new_interrupt_times = Vec::with_capacity(MAX_NUM_INTERRUPT); + let mut do_rerun = false; + // if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time { - let t = input_bytes_to_interrupt_times(new_interrupt_times.bytes(), *interrup_config); - for i in 0..t.len() {interrupt_offsets[i]=t[i];} - num_interrupts=t.len(); - } - interrupt_offsets.sort_unstable(); - - // println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); - let mut prefix : Vec<[u8; 4]> = vec![]; - // let mut suffix : Vec = vec![]; - #[cfg(feature = "mutate_stg")] - { - let metadata = state.metadata_map(); - let hist = metadata.get::().unwrap(); - let maxtick : u64 = hist.1.0; - drop(hist); + #[cfg(feature = "mutate_stg")] { - let choice = myrand.between(1,100); - if choice <= 25 || *interrupt_offsets.get(0).unwrap_or(&u32::MAX) as u64 > maxtick { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts - do_rerun = true; - // let metadata = state.metadata_map(); - let hist = metadata.get::().unwrap(); - let maxtick : u64 = hist.1.0; - // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); - let mut numbers : Vec = vec![]; - for i in 0..myrand.between(0,2*min(MAX_NUM_INTERRUPT, maxtick as usize / (interrup_config.1 as usize * QEMU_ISNS_PER_USEC as usize))) { - prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64) as usize).try_into().expect("ticks > u32"))); + let metadata = state.metadata_map(); + let maxtick = {metadata.get::().unwrap().1.0}; + drop(new_interrupt_part.drain(..).collect::>()); + { + let choice = myrand.between(1,100); + if choice <= 25 || *old_interrupt_times.get(0).unwrap_or(&u32::MAX) as u64 > maxtick { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts + do_rerun = true; + let hist = metadata.get::().unwrap(); + let maxtick : u64 = hist.1.0; + // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); + for _ in 0..myrand.between(0,min(MAX_NUM_INTERRUPT, (maxtick as usize * 3) / (interrup_config.1 as usize * QEMU_ISNS_PER_USEC as usize * 2))) { + new_interrupt_times.push(myrand.between(0, min(maxtick, u32::MAX as u64) as usize).try_into().expect("ticks > u32")); + } } - } - else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases - let feedbackstate = match state - .named_metadata_map() - .get::("stgfeedbackstate") { - Some(s) => s, - Option::None => { - panic!("STGfeedbackstate not visible") + else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases + let feedbackstate = match state + .named_metadata_map() + .get::("stgfeedbackstate") { + Some(s) => s, + Option::None => { + panic!("STGfeedbackstate not visible") + } + }; + if let Some(meta) = curr_case.metadata_map().get::() { + if let Some(t) = try_force_new_branches(&old_interrupt_times, feedbackstate, meta, *interrup_config) { + do_rerun = true; + new_interrupt_times=t; } - }; - if let Some(meta) = current_case.metadata_map().get::() { - if let Some(t) = try_force_new_branches(&interrupt_offsets, feedbackstate, meta, *interrup_config) { - do_rerun = true; - for i in 0..t.len() { - if i(); + // if tmp.is_some() { + // let trace = tmp.expect("STGNodeMetadata not found"); + // let mut node_indices = vec![]; + // for i in (0..trace.intervals.len()).into_iter() { + // if let Some(abb) = &trace.intervals[i].abb { + // if let Some(idx) = feedbackstate.state_abb_hash_index.get(&(trace.intervals[i].start_state,abb.get_hash())) { + // node_indices.push(Some(idx)); + // continue; + // } + // } + // node_indices.push(None); + // } + // // let mut marks : HashMap= HashMap::new(); // interrupt -> block hit + // // for i in 0..trace.intervals.len() { + // // let curr = &trace.intervals[i]; + // // let m = interrupt_offsets[0..num_interrupts].iter().filter(|x| (curr.start_tick..curr.end_tick).contains(&((**x) as u64))); + // // for k in m { + // // marks.insert(*k,i); + // // } + // // } + // // walk backwards trough the trace and try moving the interrupt to a block that does not have an outgoing interrupt edge or ist already hit by a predecessor + // for i in (0..num_interrupts).rev() { + // let mut lb = FIRST_INT; + // let mut ub : u32 = trace.intervals[trace.intervals.len()-1].end_tick.try_into().expect("ticks > u32"); + // if i > 0 { + // lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + // } + // if i < num_interrupts-1 { + // ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + // } + // let alternatives : Vec<_> = (0..trace.intervals.len()).filter(|x| + // node_indices[*x].is_some() && + // (trace.intervals[*x].start_tick < (lb as u64) && (lb as u64) < trace.intervals[*x].end_tick + // || trace.intervals[*x].start_tick > (lb as u64) && trace.intervals[*x].start_tick < (ub as u64)) + // ).collect(); + // let not_yet_hit : Vec<_> = alternatives.iter().filter( + // |x| feedbackstate.graph.edges_directed(*node_indices[**x].unwrap(), petgraph::Direction::Outgoing).any(|y| y.weight().event != CaptureEvent::ISRStart)).collect(); + // if not_yet_hit.len() > 0 { + // let replacement = &trace.intervals[*myrand.choose(not_yet_hit).unwrap()]; + // interrupt_offsets[i] = (myrand.between(replacement.start_tick as usize, + // replacement.end_tick as usize)).try_into().expect("ticks > u32"); + // // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + // do_rerun = true; + // break; + // } + // } + // } + } + else { // old version of the alternative search + new_interrupt_times = old_interrupt_times.clone(); + let tmp = curr_case.metadata_map().get::(); + if tmp.is_some() { + let trace = tmp.expect("STGNodeMetadata not found"); + + // calculate hits and identify snippets + let mut last_m = false; + let mut marks : Vec<(&ExecInterval, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler + for i in 0..trace.intervals.len() { + let curr = &trace.intervals[i]; + let m = old_interrupt_times.iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); + if m { + marks.push((curr, i, 1)); + // println!("1: {}",curr.current_task.0.task_name); + } else if last_m { + marks.push((curr, i, 2)); + // println!("2: {}",curr.current_task.0.task_name); + } else { + marks.push((curr, i, 0)); + } + last_m = m; } - - } - } - // let tmp = current_case.metadata_map().get::(); - // if tmp.is_some() { - // let trace = tmp.expect("STGNodeMetadata not found"); - // let mut node_indices = vec![]; - // for i in (0..trace.intervals.len()).into_iter() { - // if let Some(abb) = &trace.intervals[i].abb { - // if let Some(idx) = feedbackstate.state_abb_hash_index.get(&(trace.intervals[i].start_state,abb.get_hash())) { - // node_indices.push(Some(idx)); - // continue; - // } - // } - // node_indices.push(None); - // } - // // let mut marks : HashMap= HashMap::new(); // interrupt -> block hit - // // for i in 0..trace.intervals.len() { - // // let curr = &trace.intervals[i]; - // // let m = interrupt_offsets[0..num_interrupts].iter().filter(|x| (curr.start_tick..curr.end_tick).contains(&((**x) as u64))); - // // for k in m { - // // marks.insert(*k,i); - // // } - // // } - // // walk backwards trough the trace and try moving the interrupt to a block that does not have an outgoing interrupt edge or ist already hit by a predecessor - // for i in (0..num_interrupts).rev() { - // let mut lb = FIRST_INT; - // let mut ub : u32 = trace.intervals[trace.intervals.len()-1].end_tick.try_into().expect("ticks > u32"); - // if i > 0 { - // lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); - // } - // if i < num_interrupts-1 { - // ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); - // } - // let alternatives : Vec<_> = (0..trace.intervals.len()).filter(|x| - // node_indices[*x].is_some() && - // (trace.intervals[*x].start_tick < (lb as u64) && (lb as u64) < trace.intervals[*x].end_tick - // || trace.intervals[*x].start_tick > (lb as u64) && trace.intervals[*x].start_tick < (ub as u64)) - // ).collect(); - // let not_yet_hit : Vec<_> = alternatives.iter().filter( - // |x| feedbackstate.graph.edges_directed(*node_indices[**x].unwrap(), petgraph::Direction::Outgoing).any(|y| y.weight().event != CaptureEvent::ISRStart)).collect(); - // if not_yet_hit.len() > 0 { - // let replacement = &trace.intervals[*myrand.choose(not_yet_hit).unwrap()]; - // interrupt_offsets[i] = (myrand.between(replacement.start_tick as usize, - // replacement.end_tick as usize)).try_into().expect("ticks > u32"); - // // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); - // do_rerun = true; - // break; - // } - // } - // } - } - else { // old version of the alternative search - let tmp = current_case.metadata_map().get::(); - if tmp.is_some() { - let trace = tmp.expect("STGNodeMetadata not found"); - - // calculate hits and identify snippets - let mut last_m = false; - let mut marks : Vec<(&ExecInterval, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler - for i in 0..trace.intervals.len() { - let curr = &trace.intervals[i]; - let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); - if m { - marks.push((curr, i, 1)); - // println!("1: {}",curr.current_task.0.task_name); - } else if last_m { - marks.push((curr, i, 2)); - // println!("2: {}",curr.current_task.0.task_name); - } else { - marks.push((curr, i, 0)); - } - last_m = m; - } - for i in 0..num_interrupts { - // bounds based on minimum inter-arrival time - let mut lb = FIRST_INT; - let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); - if i > 0 { - lb = u32::saturating_add(interrupt_offsets[i-1], interrup_config.1 * QEMU_ISNS_PER_USEC); - } - if i < num_interrupts-1 { - ub = u32::saturating_sub(interrupt_offsets[i+1], interrup_config.1 * QEMU_ISNS_PER_USEC); - } - // get old hit and handler - let old_hit = marks.iter().filter( - |x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick - ).next(); - let old_handler = match old_hit { - Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 { - Some(marks[s.1+1]) - } else {None}, - None => None - }; - // find reachable alternatives - let alternatives : Vec<_> = marks.iter().filter(|x| - x.2 != 2 && - ( - x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick - || x.0.start_tick > (lb as u64) && x.0.start_tick < (ub as u64)) - ).collect(); - // in cases there are no alternatives - if alternatives.len() == 0 { - if old_hit.is_none() { - // choose something random - let untouched : Vec<_> = marks.iter().filter( - |x| x.2 == 0 + for i in 0..old_interrupt_times.len() { + // bounds based on minimum inter-arrival time + let mut lb = FIRST_INT; + let mut ub : u32 = trace.intervals[trace.intervals.len()-1].end_tick.try_into().expect("ticks > u32"); + if i > 0 { + // use the new times, because changes to preceding timings are not accounted for yet + lb = u32::saturating_add(new_interrupt_times[i-1], interrup_config.1 * QEMU_ISNS_PER_USEC); + } + if i < old_interrupt_times.len()-1 { + ub = u32::saturating_sub(new_interrupt_times[i+1], interrup_config.1 * QEMU_ISNS_PER_USEC); + } + // get old hit and handler + let old_hit = marks.iter().filter( + |x| x.0.start_tick < (old_interrupt_times[i] as u64) && (old_interrupt_times[i] as u64) < x.0.end_tick + ).next(); + let old_handler = match old_hit { + Some(s) => if s.1 < old_interrupt_times.len()-1 && s.1 < marks.len()-1 { + Some(marks[s.1+1]) + } else {None}, + None => None + }; + // find reachable alternatives + let alternatives : Vec<_> = marks.iter().filter(|x| + x.2 != 2 && + ( + x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick + || x.0.start_tick > (lb as u64) && x.0.start_tick < (ub as u64)) ).collect(); - if untouched.len() > 0 { - let tmp = interrupt_offsets[i]; - let choice = myrand.choose(untouched).unwrap(); - interrupt_offsets[i] = myrand.between(choice.0.start_tick as usize, choice.0.end_tick as usize) - .try_into().expect("tick > u32"); + // in cases there are no alternatives + if alternatives.len() == 0 { + if old_hit.is_none() { + // choose something random + let untouched : Vec<_> = marks.iter().filter( + |x| x.2 == 0 + ).collect(); + if untouched.len() > 0 { + let tmp = old_interrupt_times[i]; + let choice = myrand.choose(untouched).unwrap(); + new_interrupt_times[i] = myrand.between(choice.0.start_tick as usize, choice.0.end_tick as usize) + .try_into().expect("tick > u32"); + do_rerun = true; + } + // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); + continue; + } else { + // do nothing + // println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } + } + let replacement = myrand.choose(alternatives).unwrap(); + if (old_hit.map_or(false, |x| x == replacement)) { + // use the old value + // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } else { + let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { + // move futher back, respect old_handler + old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) + } else { 0 }; + // let tmp = new_interrupt_times[i]; + new_interrupt_times[i] = (myrand.between(replacement.0.start_tick as usize, + replacement.0.end_tick as usize) + extra as usize).try_into().expect("ticks > u32"); + // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); do_rerun = true; } - // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); - continue; - } else { - // do nothing - // println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); - continue; } + // println!("Mutator: {:?}", numbers); + // let mut start : u32 = 0; + // for i in 0..numbers.len() { + // let tmp = numbers[i]; + // numbers[i] = numbers[i]-start; + // start = tmp; + // } + new_interrupt_part.extend(&interrupt_times_to_input_bytes(&new_interrupt_times)); } - let replacement = myrand.choose(alternatives).unwrap(); - if (old_hit.map_or(false, |x| x == replacement)) { - // use the old value - // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); - continue; - } else { - let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { - // move futher back, respect old_handler - old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) - } else { 0 }; - let tmp = interrupt_offsets[i]; - interrupt_offsets[i] = (myrand.between(replacement.0.start_tick as usize, - replacement.0.end_tick as usize) + extra as usize).try_into().expect("ticks > u32"); - // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); - do_rerun = true; - } - } - let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); - numbers.sort(); - // println!("Mutator: {:?}", numbers); - // let mut start : u32 = 0; - // for i in 0..numbers.len() { - // let tmp = numbers[i]; - // numbers[i] = numbers[i]-start; - // start = tmp; - // } - for i in 0..numbers.len() { - prefix.push(u32::to_le_bytes(numbers[i])); - } } } } - } - #[cfg(not(feature = "trace_stg"))] - { - if myrand.between(1,100) <= 25 { // we have no hint if interrupt times will change anything - do_rerun = true; - let metadata = state.metadata_map(); - let hist = metadata.get::().unwrap(); - let maxtick : u64 = hist.1.0; - // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); - let mut numbers : Vec = vec![]; - for i in 0..num_interrupts { - prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick as usize, u32::MAX as usize)).try_into().expect("ticks > u32"))); + #[cfg(not(feature = "trace_stg"))] + { + if myrand.between(1,100) <= 25 { // we have no hint if interrupt times will change anything + do_rerun = true; + let metadata = state.metadata_map(); + let maxtick = {metadata.get::().unwrap().1.0}; + new_interrupt_times = Vec::with_capacity(MAX_NUM_INTERRUPT); + for i in 0..myrand.between(0,min(MAX_NUM_INTERRUPT, (maxtick as usize * 3) / (interrup_config.1 as usize * QEMU_ISNS_PER_USEC as usize * 2))) { + new_interrupt_times.push(myrand.between(0, min(maxtick, u32::MAX as u64) as usize).try_into().expect("ticks > u32")); + } } } + new_interrupt_part.extend(&interrupt_times_to_input_bytes(&new_interrupt_times)); } - let _t= new_interrupt_times.drain(..).collect::>(); - drop(_t); - new_interrupt_times.extend(&prefix.concat()); - } - drop(current_case); - // InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?; - if do_rerun { - loopcount+=1; - let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; - if corpus_idx.is_none() && loopbound<=0 { break;} - } else {if loopbound<=0 {break;}} + drop(curr_case); + if do_rerun { + rerun_count+=1; + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; + if corpus_idx.is_none() && loopbound<=0 { break;} + } else {if loopbound<=0 {break;}} } Ok(()) }