1mod consec_blocks;
23mod dram_addr;
24mod flippy_page;
25mod keyed_cache;
26mod mem_configuration;
27mod memblock;
28mod pagemap_info;
29mod pfn_offset;
30mod pfn_offset_resolver;
31mod pfn_resolver;
32mod timer;
33mod virt_to_phys;
34
35pub use self::consec_blocks::ConsecBlocks;
36pub use self::dram_addr::DRAMAddr;
37pub use self::flippy_page::{FlippyPage, find_flippy_page};
38pub use self::mem_configuration::{MTX_SIZE, MemConfiguration};
39pub use self::memblock::{Error as ConsecPfnsError, FormatPfns, GetConsecPfns, Memory};
40pub use self::pfn_offset::PfnOffset;
41pub use self::pfn_offset_resolver::PfnOffsetResolver;
42pub use self::pfn_resolver::PfnResolver;
43pub use self::timer::{MemoryTupleTimer, TimerError, construct_memory_tuple_timer};
44pub use self::virt_to_phys::PhysAddr;
45pub use self::virt_to_phys::{LinuxPageMap, LinuxPageMapError, VirtToPhysResolver};
46use rand::Rng as _;
47use serde::Serialize;
48use std::arch::x86_64::_mm_clflush;
49use std::fmt::Debug;
50use std::io::BufWriter;
51
52use crate::util::{CL_SIZE, PAGE_MASK, PAGE_SIZE, ROW_MASK, ROW_SIZE, Rng};
53
54use libc::{c_void, memcmp};
55use log::{debug, info, trace};
56use std::{arch::x86_64::_mm_mfence, fmt};
57
58pub type AggressorPtr = *const u8;
63
64#[derive(Debug)]
66pub enum MemoryError {
67 AllocFailed,
69 ZeroSizeLayout,
71}
72
73impl std::error::Error for MemoryError {}
74
75impl fmt::Display for MemoryError {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 match self {
78 MemoryError::AllocFailed => write!(f, "Allocation failed"),
79 MemoryError::ZeroSizeLayout => write!(f, "Zero size layout"),
80 }
81 }
82}
83
84pub trait VictimMemory: BytePointer + Initializable + Checkable {}
89
90#[allow(clippy::len_without_is_empty)]
94pub trait BytePointer {
95 fn addr(&self, offset: usize) -> *mut u8;
102
103 fn ptr(&self) -> *mut u8;
105
106 fn len(&self) -> usize;
108
109 fn dump(&self, file: &str) -> std::io::Result<()> {
117 use std::fs::File;
118 use std::io::Write;
119 let file = File::create(file)?;
120 let mut writer = BufWriter::new(file);
121 for offset in (0..self.len()).step_by(ROW_SIZE) {
122 for byte_offset in 0..ROW_SIZE {
123 write!(writer, "{:02x}", unsafe {
124 *self.addr(offset + byte_offset)
125 })?;
126 }
127 writer.write_all(b"\n")?;
128 }
129 writer.flush()?;
130 Ok(())
131 }
132}
133
134#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
140pub enum DataPattern {
141 Random(Box<Rng>),
143 StripeZero {
145 #[serde(skip_serializing)]
147 zeroes: Vec<AggressorPtr>,
148 },
149 Zero,
151 StripeOne {
153 #[serde(skip_serializing)]
155 ones: Vec<AggressorPtr>,
156 },
157 One,
159}
160
161impl DataPattern {
162 fn get(&mut self, addr: *const u8) -> [u8; PAGE_SIZE] {
163 match self {
164 DataPattern::Random(rng) => {
165 let mut arr = [0u8; PAGE_SIZE];
166 for byte in arr.iter_mut() {
167 *byte = rng.random();
168 }
169 arr
170 }
171 DataPattern::StripeZero { zeroes } => {
172 for &row in zeroes.iter() {
173 if (row as usize) == addr as usize & !ROW_MASK {
174 trace!("setting aggressor page to 0x00 at addr {:p}", addr);
175 return [0x00; PAGE_SIZE];
176 }
177 }
178 [0xFF; PAGE_SIZE]
179 }
180 DataPattern::Zero => [0x00; PAGE_SIZE],
181 DataPattern::StripeOne { ones } => {
182 for &row in ones.iter() {
183 if (row as usize) == addr as usize & !ROW_MASK {
184 trace!("setting aggressor page to 0xFF at addr {:p}", addr);
185 return [0xFF; PAGE_SIZE];
186 }
187 }
188 [0x00; PAGE_SIZE]
189 }
190 DataPattern::One => [0xFF; PAGE_SIZE],
191 }
192 }
193}
194
195pub trait Initializable {
200 fn initialize(&self, pattern: DataPattern);
202
203 fn initialize_excluding(&self, pattern: DataPattern, pages: &[*const u8]);
205
206 fn initialize_cb(&self, f: &mut dyn FnMut(usize) -> Option<[u8; PAGE_SIZE]>);
210}
211
212#[derive(Clone, Copy, Serialize, PartialEq, Eq, Hash)]
217pub struct BitFlip {
218 pub addr: usize,
220 pub bitmask: u8,
222 pub data: u8,
224}
225
226#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
230pub enum FlipDirection {
231 ZeroToOne,
233 OneToZero,
235 Multiple(Vec<FlipDirection>),
237 None,
239 Any,
241}
242
243impl core::fmt::Debug for BitFlip {
244 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
245 f.debug_struct("BitFlip")
246 .field("addr", &format_args!("{:#x}", self.addr))
247 .field("bitmask", &format_args!("{:#x}", self.bitmask))
248 .field("data", &format_args!("{:#x}", self.data))
249 .finish()
250 }
251}
252
253impl BitFlip {
254 pub fn new(addr: *const u8, bitmask: u8, data: u8) -> Self {
256 BitFlip {
257 addr: addr as usize,
258 bitmask,
259 data,
260 }
261 }
262}
263
264impl BitFlip {
265 pub fn flip_direction(&self) -> FlipDirection {
267 match self.bitmask.count_ones() {
268 0 => FlipDirection::None,
269 1 => {
270 let flipped = self.bitmask & self.data;
271 match flipped {
272 0 => FlipDirection::ZeroToOne,
273 _ => FlipDirection::OneToZero,
274 }
275 }
276 2.. => FlipDirection::Multiple(
277 (0..8)
278 .filter_map(|i| {
279 if self.bitmask & (1 << i) != 0 {
280 Some(if self.data & (1 << i) != 0 {
281 FlipDirection::OneToZero
282 } else {
283 FlipDirection::ZeroToOne
284 })
285 } else {
286 None
287 }
288 })
289 .collect(),
290 ),
291 }
292 }
293}
294
295pub trait Checkable {
300 fn check(&self, pattern: DataPattern) -> Vec<BitFlip>;
302
303 fn check_excluding(&self, pattern: DataPattern, pages: &[*const u8]) -> Vec<BitFlip>;
305
306 fn check_cb(&self, f: &mut dyn FnMut(usize) -> Option<[u8; PAGE_SIZE]>) -> Vec<BitFlip>;
308}
309
310impl<T> Initializable for T
312where
313 T: VictimMemory,
314{
315 fn initialize(&self, pattern: DataPattern) {
316 self.initialize_excluding(pattern, &[]);
317 }
318
319 fn initialize_excluding(&self, mut pattern: DataPattern, pages: &[*const u8]) {
320 info!(
321 "initialize buffer with pattern {}",
322 match &pattern {
323 DataPattern::Random(rng) => format!("random ({:?})", rng),
324 DataPattern::StripeZero { .. } => "stripe zero".into(),
325 DataPattern::Zero => "zero".into(),
326 DataPattern::StripeOne { .. } => "stripe one".into(),
327 DataPattern::One => "one".into(),
328 }
329 );
330 self.initialize_cb(&mut |offset: usize| {
331 let addr = self.addr(offset);
332 let val = pattern.get(addr); if pages
334 .iter()
335 .any(|&page| page as usize & !PAGE_MASK == addr as usize & !PAGE_MASK)
336 {
337 return None;
338 }
339 Some(val)
340 });
341 }
342
343 fn initialize_cb(&self, f: &mut dyn FnMut(usize) -> Option<[u8; PAGE_SIZE]>) {
344 let len = self.len();
345 if !len.is_multiple_of(8) {
346 panic!("memory len must be divisible by 8");
347 }
348 if !len.is_multiple_of(PAGE_SIZE) {
349 panic!(
350 "memory len ({}) must be divisible by PAGE_SIZE ({})",
351 len, PAGE_SIZE
352 );
353 }
354
355 debug!("initialize {} bytes", len);
356
357 for offset in (0..len).step_by(PAGE_SIZE) {
358 if let Some(value) = f(offset) {
359 unsafe {
360 std::ptr::write_volatile(self.addr(offset) as *mut [u8; PAGE_SIZE], value);
361 }
362 }
363 }
364 debug!("memory init done");
365 }
366}
367
368impl<T: BytePointer> PfnResolver for T {
370 fn pfn(&self) -> Result<PhysAddr, LinuxPageMapError> {
371 let mut resolver = LinuxPageMap::new()?;
372 resolver.get_phys(self.ptr() as u64)
373 }
374}
375
376impl<T> Checkable for T
378where
379 T: VictimMemory,
380{
381 fn check(&self, pattern: DataPattern) -> Vec<BitFlip> {
382 self.check_excluding(pattern, &[])
383 }
384
385 fn check_excluding(&self, mut pattern: DataPattern, pages: &[*const u8]) -> Vec<BitFlip> {
386 self.check_cb(&mut |offset: usize| {
387 let addr = self.addr(offset);
388 let val = pattern.get(addr); if pages
390 .iter()
391 .any(|&page| page as usize & !PAGE_MASK == addr as usize & !PAGE_MASK)
392 {
393 return None;
394 }
395 Some(val)
396 })
397 }
398
399 fn check_cb(&self, f: &mut dyn FnMut(usize) -> Option<[u8; PAGE_SIZE]>) -> Vec<BitFlip> {
400 let len = self.len();
401 if !len.is_multiple_of(PAGE_SIZE) {
402 panic!(
403 "memory len ({}) must be divisible by PAGE_SIZE ({})",
404 len, PAGE_SIZE
405 );
406 }
407
408 let mut ret = vec![];
409 for offset in (0..len).step_by(PAGE_SIZE) {
410 if let Some(expected) = f(offset) {
411 unsafe {
412 for byte_offset in (0..PAGE_SIZE).step_by(CL_SIZE) {
413 _mm_clflush(self.addr(offset + byte_offset));
414 }
415 _mm_mfence();
416 let cmp = memcmp(
417 self.addr(offset) as *const c_void,
418 expected.as_ptr() as *const c_void,
419 PAGE_SIZE,
420 );
421 if cmp == 0 {
422 continue;
423 }
424 debug!(
425 "Found bitflip in page {}. Determining exact flip position",
426 offset
427 );
428 for (i, &expected) in expected.iter().enumerate() {
429 let addr = self.addr(offset + i);
430 _mm_clflush(addr);
431 _mm_mfence();
432 if *addr != expected {
433 ret.push(BitFlip::new(addr, *addr ^ expected, expected));
434 }
435 }
436 }
437 } else {
438 debug!("skipping page {} due to exclusion", offset);
439 }
440 }
441 ret
442 }
443}
444
445#[test]
446fn test_pattern_random_clone() {
447 let pattern = DataPattern::Random(Box::new(Rng::from_seed(rand::random())));
448 let a = pattern.clone().get(std::ptr::null());
449 let b = pattern.clone().get(std::ptr::null());
450 assert_eq!(a, b);
451}
452
453#[test]
454fn test_bitflip_direction() {
455 let flip = BitFlip::new(std::ptr::null(), 0b0000_0000, 0xFF);
456 assert_eq!(flip.flip_direction(), FlipDirection::None);
457 let flip = BitFlip::new(std::ptr::null(), 0b0000_0001, 0b0000_0001);
458 assert_eq!(flip.flip_direction(), FlipDirection::OneToZero);
459
460 let flip = BitFlip::new(std::ptr::null(), 0b0000_0001, 0b1111_1110);
461 assert_eq!(flip.flip_direction(), FlipDirection::ZeroToOne);
462
463 let flip = BitFlip::new(std::ptr::null(), 0b0000_0011, 0b0000_0010);
464 assert_eq!(
465 flip.flip_direction(),
466 FlipDirection::Multiple(vec![FlipDirection::ZeroToOne, FlipDirection::OneToZero])
467 );
468
469 let flip = BitFlip::new(std::ptr::null(), 0b0000_0011, 0b0000_0000);
470 assert_eq!(
471 flip.flip_direction(),
472 FlipDirection::Multiple(vec![FlipDirection::ZeroToOne, FlipDirection::ZeroToOne])
473 );
474
475 let flip = BitFlip::new(std::ptr::null(), 0b0000_0011, 0b0000_0011);
476 assert_eq!(
477 flip.flip_direction(),
478 FlipDirection::Multiple(vec![FlipDirection::OneToZero, FlipDirection::OneToZero])
479 );
480}