#![feature(unsafe_block_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)] use turn_lock::TurnLock; const PAGE_SIZE: usize = 1 << 12; // FIXME Magic // design docs // Build a channel using x pages + one synchronisation primitive. // F+R only use one line per page // F+F should use several line per page // Each page has 1<<12 bytes / 1<<6 bytes per line, hence 64 lines (or 6 bits of info). // General structure : two threads, a transmitter and a reciever. Transmitter generates bytes, Reciever reads bytes, then on join compare results for accuracy. // Alos time in order to determine duration, in rdtsc and seconds. use bit_field::BitField; use cache_utils::mmap::MMappedMemory; use cache_utils::rdtsc_fence; use std::any::Any; use std::collections::VecDeque; use std::sync::Arc; use std::thread; /** * Safety considerations : Not ensure thread safety, need proper locking as needed. */ pub trait CovertChannel: Send + Sync { const BIT_PER_PAGE: usize; unsafe fn transmit(&self, page: *const u8, bits: &mut BitIterator); unsafe fn receive(&self, page: *const u8) -> Vec; } pub struct CovertChannelBenchmarkResult { pub num_bytes_transmitted: usize, pub num_bit_errors: usize, pub error_rate: f64, pub time_rdtsc: u64, //pub time_seconds: todo } pub struct BitIterator<'a> { bytes: &'a Vec, byte_index: usize, bit_index: u8, } impl<'a> BitIterator<'a> { pub fn new(bytes: &'a Vec) -> BitIterator<'a> { BitIterator { bytes, byte_index: 0, bit_index: 0, } } pub fn atEnd(&self) -> bool { self.byte_index >= self.bytes.len() } } impl Iterator for BitIterator<'_> { type Item = bool; fn next(&mut self) -> Option { if let Some(b) = self.bytes.get(self.byte_index) { let r = (b >> (u8::BIT_LENGTH - 1 - self.bit_index as usize)) & 1 != 0; self.bit_index += 1; self.byte_index += self.bit_index as usize / u8::BIT_LENGTH; self.bit_index = self.bit_index % u8::BIT_LENGTH as u8; Some(r) } else { None } } } struct CovertChannelPage { pub turn: TurnLock, pub addr: *const u8, } struct CovertChannelParams { pages: Vec, covert_channel: Arc, transmit_core: usize, } unsafe impl Send for CovertChannelParams {} fn transmit_thread( num_bytes: usize, mut params: CovertChannelParams, ) -> (u64, Vec) { let mut result = Vec::new(); result.reserve(num_bytes); for _ in 0..num_bytes { let byte = rand::random(); result.push(byte); } let mut bit_iter = BitIterator::new(&result); let start = unsafe { rdtsc_fence() }; while !bit_iter.atEnd() { for page in params.pages.iter_mut() { page.turn.wait(); unsafe { params.covert_channel.transmit(page.addr, &mut bit_iter) }; page.turn.next(); } } (start, result) } pub fn benchmark_channel( channel: T, num_pages: usize, num_bytes: usize, transmit_core: usize, receive_core: usize, ) -> CovertChannelBenchmarkResult { // Allocate pages let size = num_pages * PAGE_SIZE; let m = MMappedMemory::new(size); let mut pages_transmit = Vec::new(); let mut pages_receive = Vec::new(); let array: &[u8] = m.slice(); for i in 0..num_pages { let addr = &array[i * PAGE_SIZE] as *const u8; let mut turns = TurnLock::new(2); let mut t_iter = turns.drain(0..); let receive_lock = t_iter.next().unwrap(); let transmit_lock = t_iter.next().unwrap(); assert!(t_iter.next().is_none()); pages_transmit.push(CovertChannelPage { turn: transmit_lock, addr, }); pages_receive.push(CovertChannelPage { turn: receive_lock, addr, }); } let covert_channel_arc = Arc::new(channel); let params = CovertChannelParams { pages: pages_transmit, covert_channel: covert_channel_arc.clone(), transmit_core, }; if transmit_core == receive_core { unimplemented!() } let helper = thread::spawn(move || transmit_thread(num_bytes, params)); // Create the thread parameters let mut received_bytes: Vec = Vec::new(); let mut received_bits = VecDeque::::new(); while received_bytes.len() < num_bytes { for page in pages_receive.iter_mut() { page.turn.wait(); let mut bits = unsafe { covert_channel_arc.receive(page.addr) }; page.turn.next(); received_bits.extend(&mut bits.iter()); while received_bits.len() >= u8::BIT_LENGTH { let mut byte = 0; for i in 0..u8::BIT_LENGTH { byte <<= 1; let bit = received_bits.pop_front().unwrap(); byte |= bit as u8; } received_bytes.push(byte); } } // TODO // receiver thread } let stop = unsafe { rdtsc_fence() }; let r = helper.join(); let (start, sent_bytes) = match r { Ok(r) => r, Err(e) => panic!("Join Error: {:?#}"), }; assert_eq!(sent_bytes.len(), received_bytes.len()); assert_eq!(num_bytes, received_bytes.len()); let mut num_bit_error = 0; for i in 0..num_bytes { num_bit_error += (sent_bytes[i] ^ received_bytes[i]).count_ones() as usize; } let error_rate = (num_bit_error as f64) / ((num_bytes * u8::BIT_LENGTH) as f64); // Create transmit thread CovertChannelBenchmarkResult { num_bytes_transmitted: num_bytes, num_bit_errors: num_bit_error, error_rate, time_rdtsc: stop - start, } } #[cfg(test)] mod tests { use crate::BitIterator; #[test] fn it_works() { assert_eq!(2 + 2, 4); } #[test] fn test_bit_vec() { let bit_iter = BitIterator::new(vec![0x55, 0xf]); let results = vec![ false, true, false, true, false, true, false, true, false, false, false, false, true, true, true, true, ]; for (i, bit) in bit_iter.enumerate() { assert_eq!(results[i], bit); } } }