diff --git a/cache_utils/src/bin/two_thread_cal.rs b/cache_utils/src/bin/two_thread_cal.rs index e6c2015..24de0b6 100644 --- a/cache_utils/src/bin/two_thread_cal.rs +++ b/cache_utils/src/bin/two_thread_cal.rs @@ -1,7 +1,7 @@ use cache_utils::calibration::{ - calibrate_fixed_freq_2_thread, flush_and_reload, load_and_flush, only_flush, only_reload, - reload_and_flush, CalibrateOperation2T, CalibrationOptions, HistParams, Verbosity, - CFLUSH_BUCKET_NUMBER, CFLUSH_BUCKET_SIZE, CFLUSH_NUM_ITER, + calibrate_fixed_freq_2_thread, flush_and_reload, get_cache_slicing, load_and_flush, only_flush, + only_reload, reload_and_flush, CalibrateOperation2T, CalibrateResult2T, CalibrationOptions, + HistParams, Verbosity, CFLUSH_BUCKET_NUMBER, CFLUSH_BUCKET_SIZE, CFLUSH_NUM_ITER, }; use cache_utils::mmap::MMappedMemory; use cache_utils::{flush, maccess, noop}; @@ -9,6 +9,8 @@ use nix::sched::{sched_getaffinity, CpuSet}; use nix::unistd::Pid; use core::arch::x86_64 as arch_x86; + +use std::collections::HashMap; use std::process::Command; use std::str::from_utf8; @@ -26,6 +28,39 @@ unsafe fn multiple_access(p: *const u8) { const SIZE: usize = 2 << 20; +#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] +struct ASV { + pub attacker: u8, + pub slice: u8, + pub victim: u8, +} + +struct ResultAnalysis { + // indexed by bucket size + pub miss: Vec, + pub miss_cum_sum: Vec, + pub miss_total: u32, + pub hit: Vec, + pub hit_cum_sum: Vec, + pub hit_total: u32, + pub error_miss_less_than_hit: Vec, + pub error_hit_less_than_miss: Vec, + pub min_error_hlm: u32, + pub min_error_mlh: u32, +} + +#[derive(Debug, Clone, Copy)] +struct Threshold { + pub error_rate: f32, + pub threshold: usize, + // extend with other possible algorithm ? + pub is_hlm: bool, + pub num_true_hit: u32, + pub num_false_hit: u32, + pub num_true_miss: u32, + pub num_false_miss: u32, +} + fn main() { // Grab a slice of memory @@ -75,73 +110,76 @@ fn main() { if pointer as usize & (cache_line_size - 1) != 0 { panic!("not aligned nicely"); } - calibrate_fixed_freq_2_thread( + + let operations = [ + CalibrateOperation2T { + prepare: maccess::, + op: only_flush, + name: "clflush_remote_hit", + display_name: "clflush remote hit", + }, + CalibrateOperation2T { + prepare: maccess::, + op: load_and_flush, + name: "clflush_shared_hit", + display_name: "clflush shared hit", + }, + CalibrateOperation2T { + prepare: flush, + op: only_flush, + name: "clflush_miss_f", + display_name: "clflush miss - f", + }, + CalibrateOperation2T { + prepare: flush, + op: load_and_flush, + name: "clflush_local_hit_f", + display_name: "clflush local hit - f", + }, + CalibrateOperation2T { + prepare: noop::, + op: only_flush, + name: "clflush_miss_n", + display_name: "clflush miss - n", + }, + CalibrateOperation2T { + prepare: noop::, + op: load_and_flush, + name: "clflush_local_hit_n", + display_name: "clflush local hit - n", + }, + CalibrateOperation2T { + prepare: noop::, + op: flush_and_reload, + name: "reload_miss", + display_name: "reload miss", + }, + CalibrateOperation2T { + prepare: maccess::, + op: reload_and_flush, + name: "reload_remote_hit", + display_name: "reload remote hit", + }, + CalibrateOperation2T { + prepare: maccess::, + op: only_reload, + name: "reload_shared_hit", + display_name: "reload shared hit", + }, + CalibrateOperation2T { + prepare: noop::, + op: only_reload, + name: "reload_local_hit", + display_name: "reload local hit", + }, + ]; + + let r = calibrate_fixed_freq_2_thread( pointer, 64, array.len() as isize >> 3, &mut core_pairs.into_iter(), - &[ - CalibrateOperation2T { - prepare: multiple_access, - op: only_flush, - name: "clflush_remote_hit", - display_name: "clflush remote hit", - }, - CalibrateOperation2T { - prepare: multiple_access, - op: load_and_flush, - name: "clflush_shared_hit", - display_name: "clflush shared hit", - }, - CalibrateOperation2T { - prepare: flush, - op: only_flush, - name: "clflush_miss_f", - display_name: "clflush miss - f", - }, - CalibrateOperation2T { - prepare: flush, - op: load_and_flush, - name: "clflush_local_hit_f", - display_name: "clflush local hit - f", - }, - CalibrateOperation2T { - prepare: noop::, - op: only_flush, - name: "clflush_miss_n", - display_name: "clflush miss - n", - }, - CalibrateOperation2T { - prepare: noop::, - op: load_and_flush, - name: "clflush_local_hit_n", - display_name: "clflush local hit - n", - }, - CalibrateOperation2T { - prepare: noop::, - op: flush_and_reload, - name: "reload_miss", - display_name: "reload miss", - }, - CalibrateOperation2T { - prepare: multiple_access, - op: reload_and_flush, - name: "reload_remote_hit", - display_name: "reload remote hit", - }, - CalibrateOperation2T { - prepare: multiple_access, - op: only_reload, - name: "reload_shared_hit", - display_name: "reload shared hit", - }, - CalibrateOperation2T { - prepare: noop::, - op: only_reload, - name: "reload_local_hit", - display_name: "reload local hit", - }, - ], + &operations, CalibrationOptions { hist_params: HistParams { bucket_number: CFLUSH_BUCKET_NUMBER, @@ -153,5 +191,151 @@ fn main() { }, core_per_socket, ); + + let mut analysis = HashMap::::new(); + + let miss_name = "clflush_miss_n"; + let hit_name = "clflush_remote_hit"; + + let miss_index = operations + .iter() + .position(|op| op.name == miss_name) + .unwrap(); + let hit_index = operations + .iter() + .position(|op| op.name == hit_name) + .unwrap(); + + let slicing = get_cache_slicing(core_per_socket); + + let h = if let Some(s) = slicing { + if s.can_hash() { + |addr: usize| -> u8 { slicing.unwrap().hash(addr).unwrap() } + } else { + panic!("No slicing function known"); + } + } else { + panic!("No slicing function known"); + }; + + for result in r { + match result.res { + Err(e) => { + eprintln!("Ooops : {:#?}", e); + panic!() + } + Ok(results) => { + for r in results { + let offset = r.offset; + let miss_hist = r.histogram[miss_index].clone(); + let hit_hist = r.histogram[hit_index].clone(); + + if miss_hist.len() != hit_hist.len() { + panic!("Maformed results"); + } + let len = miss_hist.len(); + let mut miss_cum_sum = vec![0; len]; + let mut hit_cum_sum = vec![0; len]; + miss_cum_sum[0] = miss_hist[0]; + hit_cum_sum[0] = hit_hist[0]; + for i in 1..len { + miss_cum_sum[i] = miss_hist[i] + miss_cum_sum[i - 1]; + hit_cum_sum[i] = hit_hist[i] + hit_cum_sum[i - 1]; + } + let miss_total = miss_cum_sum[len - 1]; + let hit_total = hit_cum_sum[len - 1]; + + let mut error_miss_less_than_hit = vec![0; len - 1]; + let mut error_hit_less_than_miss = vec![0; len - 1]; + + let mut min_error_hlm = u32::max_value(); + let mut min_error_mlh = u32::max_value(); + + for i in 0..(len - 1) { + error_hit_less_than_miss[i] = + miss_cum_sum[i] + (hit_total - hit_cum_sum[i]); + error_miss_less_than_hit[i] = + hit_cum_sum[i] + (miss_total - miss_cum_sum[i]); + + if error_hit_less_than_miss[i] < min_error_hlm { + min_error_hlm = error_hit_less_than_miss[i]; + } + if error_miss_less_than_hit[i] < min_error_mlh { + min_error_mlh = error_miss_less_than_hit[i]; + } + } + + analysis.insert( + ASV { + attacker: result.main_core as u8, + slice: h(offset as usize), + victim: result.helper_core as u8, + }, + ResultAnalysis { + miss: miss_hist, + miss_cum_sum, + miss_total, + hit: hit_hist, + hit_cum_sum, + hit_total, + error_miss_less_than_hit, + error_hit_less_than_miss, + min_error_hlm, + min_error_mlh, + }, + ); + } + } + } + } + let mut thresholds = HashMap::new(); + for (asv, results) in analysis { + let hlm = results.min_error_hlm < results.min_error_mlh; + let (errors, min_error) = if hlm { + (&results.error_hit_less_than_miss, results.min_error_hlm) + } else { + (&results.error_miss_less_than_hit, results.min_error_mlh) + }; + + let mut threshold_vec = Vec::new(); + + // refactor some of this logic into methods of analysis ? + + for i in 0..errors.len() { + if errors[i] == min_error { + let num_true_hit; + let num_false_hit; + let num_true_miss; + let num_false_miss; + if hlm { + num_true_hit = results.hit_cum_sum[i]; + num_false_hit = results.miss_cum_sum[i]; + num_true_miss = results.miss_total - num_false_hit; + num_false_miss = results.hit_total - num_true_hit; + } else { + num_true_miss = results.miss_cum_sum[i]; + num_false_miss = results.hit_cum_sum[i]; + num_true_hit = results.hit_total - num_false_miss; + num_false_hit = results.miss_total - num_true_miss; + } + threshold_vec.push(Threshold { + threshold: i, + is_hlm: hlm, + num_true_hit, + num_false_hit, + num_true_miss, + num_false_miss, + error_rate: min_error as f32 + / (results.hit_total + results.miss_total) as f32, + }) + } + /* + + */ + } + thresholds.insert(asv, threshold_vec); + } + eprintln!("Thresholds :\n{:#?}", thresholds); + println!("Thresholds :\n{:#?}", thresholds); } } diff --git a/cache_utils/src/calibration.rs b/cache_utils/src/calibration.rs index 90504c4..f9b4079 100644 --- a/cache_utils/src/calibration.rs +++ b/cache_utils/src/calibration.rs @@ -1,6 +1,6 @@ #![allow(clippy::missing_safety_doc)] -use crate::complex_addressing::cache_slicing; +use crate::complex_addressing::{cache_slicing, CacheSlicing}; use crate::{flush, maccess, rdtsc_fence}; use cpuid::{CPUVendor, MicroArchitecture}; @@ -234,11 +234,11 @@ pub fn calibrate_flush( #[derive(Debug)] pub struct CalibrateResult { - offset: isize, - histogram: Vec>, - median: Vec, - min: Vec, - max: Vec, + pub offset: isize, + pub histogram: Vec>, + pub median: Vec, + pub min: Vec, + pub max: Vec, } pub struct CalibrateOperation<'a> { @@ -293,6 +293,7 @@ fn calibrate_impl_fixed_freq( let to_bucket = |time: u64| -> usize { time as usize / hist_params.bucket_size }; let from_bucket = |bucket: usize| -> u64 { (bucket * hist_params.bucket_size) as u64 }; + // FIXME : Core per socket let slicing = if let Some(uarch) = MicroArchitecture::get_micro_architecture() { if let Some(vendor_family_model_stepping) = MicroArchitecture::get_family_model_stepping() { Some(cache_slicing( @@ -510,7 +511,25 @@ pub unsafe fn calibrate_fixed_freq_2_thread>( ) } -const OPTIMISED_ADDR_ITER_FACTOR: u32 = 64; +pub fn get_cache_slicing(core_per_socket: u8) -> Option { + if let Some(uarch) = MicroArchitecture::get_micro_architecture() { + if let Some(vendor_family_model_stepping) = MicroArchitecture::get_family_model_stepping() { + Some(cache_slicing( + uarch, + core_per_socket, + vendor_family_model_stepping.0, + vendor_family_model_stepping.1, + vendor_family_model_stepping.2, + )) + } else { + None + } + } else { + None + } +} + +const OPTIMISED_ADDR_ITER_FACTOR: u32 = 16; // TODO : Add the optimised address support // TODO : Modularisation / factorisation of some of the common code with the single threaded no_std version ? @@ -540,21 +559,7 @@ fn calibrate_fixed_freq_2_thread_impl>( let to_bucket = |time: u64| -> usize { time as usize / bucket_size }; let from_bucket = |bucket: usize| -> u64 { (bucket * bucket_size) as u64 }; - let slicing = if let Some(uarch) = MicroArchitecture::get_micro_architecture() { - if let Some(vendor_family_model_stepping) = MicroArchitecture::get_family_model_stepping() { - Some(cache_slicing( - uarch, - core_per_socket, - vendor_family_model_stepping.0, - vendor_family_model_stepping.1, - vendor_family_model_stepping.2, - )) - } else { - None - } - } else { - None - }; + let slicing = get_cache_slicing(core_per_socket); let h = if let Some(s) = slicing { if s.can_hash() {