clock_bound_vmclock/
shm_reader.rs

1use std::ffi::c_void;
2use std::fs::File;
3use std::io::Read;
4use std::mem::size_of;
5use std::os::fd::AsRawFd;
6use std::ptr;
7use std::sync::atomic;
8use tracing::{debug, error};
9
10use crate::shm::{VMClockShmBody, VMClockShmHeader};
11use clock_bound_shm::{syserror, ShmError};
12
13const VMCLOCK_SUPPORTED_VERSION: u16 = 1;
14
15/// A guard tracking an memory mapped file.
16///
17/// Creating the MmapGuard maps an open file descriptor.
18/// The file is unmap'ed when the guard is dropped.
19struct MmapGuard {
20    /// A pointer to the head of the segment
21    segment: *mut c_void,
22
23    /// The size of the segment mapped into memory
24    segsize: usize,
25
26    /// Memory mapped file.
27    _file: File,
28}
29
30impl MmapGuard {
31    /// Create a new MmapGuard.
32    ///
33    /// Memory map the provided open File.
34    fn new(mut file: File) -> Result<Self, ShmError> {
35        let mut buffer = vec![];
36
37        let bytes_read = match file.read_to_end(&mut buffer) {
38            Ok(bytes_read) => bytes_read,
39            Err(_) => return syserror!("Failed to read SHM segment"),
40        };
41
42        if bytes_read == 0_usize {
43            error!("MmapGuard: Read zero bytes.");
44            return Err(ShmError::SegmentNotInitialized);
45        } else if bytes_read < size_of::<VMClockShmHeader>() {
46            error!("MmapGuard: Number of bytes read ({:?}) is less than the size of VMClockShmHeader ({:?}).", bytes_read, size_of::<VMClockShmHeader>());
47            return Err(ShmError::SegmentMalformed);
48        }
49
50        debug!("MMapGuard: Reading the VMClockShmHeader ...");
51
52        // Read the header so we know how much to map in memory.
53        let header = VMClockShmHeader::read(&buffer)?;
54
55        // This consumes the segsize, but we only needed the header for validation and extracting
56        // the segment size. So the move is fine here.
57        let segsize = header.size.into_inner() as usize;
58
59        debug!("MMapGuard: Read a segment size of: {:?}", segsize);
60
61        // SAFETY: We're calling into a C function, but this particular call is always safe.
62        let segment: *mut c_void = unsafe {
63            libc::mmap(
64                ptr::null_mut(),
65                segsize,
66                libc::PROT_READ,
67                libc::MAP_SHARED,
68                file.as_raw_fd(),
69                0,
70            )
71        };
72
73        if segment == libc::MAP_FAILED {
74            return syserror!("mmap SHM segment");
75        }
76
77        Ok(MmapGuard {
78            segment,
79            segsize,
80            _file: file,
81        })
82    }
83}
84
85impl Drop for MmapGuard {
86    /// Drop the MmapGuard and unmap the file it tracks.
87    fn drop(&mut self) {
88        // SAFETY: `segment` was previously returned from `mmap`, and therefore
89        // when this destructor runs there are no more live references into
90        // it.
91        unsafe {
92            let ret = libc::munmap(self.segment, self.segsize);
93            assert!(ret == 0);
94        }
95    }
96}
97
98/// Reader for VMClock shared memory segment.
99///
100/// The VMClock shared memory segment consists of a VMClockShmHeader followed by a
101/// VMClockShmBody struct. The segment is updated by a single producer (the Hypervisor),
102/// but may be read by many clients.  The shared memory segment does not implement a semaphore or
103/// equivalent to synchronize the single-producer / many-consumers processes. Instead, the
104/// mechanism is lock-free and relies on a `seq_count` number to ensure consistent reads (over
105/// retries).
106///
107/// The writer increments the seq_count field from even to odd before each update. It also
108/// increment it again, from odd to even, after finishing the update. Readers must check the
109/// `seq_count` field before and after each read, and verify that they obtain the same, even,
110/// value. Otherwise, the read was dirty and must be retried.
111pub struct VMClockShmReader {
112    // Explicitly make the VMClockShmReader be !Send and !Sync, since it is not thread safe. A bit ugly to
113    // use a phantom raw pointer, but effective and free at runtime.
114    _marker: std::marker::PhantomData<*const ()>,
115
116    // Drop guard to unmap the shared memory segment
117    _guard: MmapGuard,
118
119    // A raw pointer into the shared memory segment, pointing to the version member of the VMClockShmHeader
120    // section. The version number defines the shared memory segment content and layout. This is a
121    // bit less flexible than a series of TLV but simpler (and not mutually exclusive).
122    version_ptr: *const atomic::AtomicU16,
123
124    // A raw pointer into the shared memory segment, pointing to the seq_count member of the
125    // VMClockShmHeader section. The seq_count is used to read consistent snapshots of the shared
126    // memory segment (that is outside of an update event by the writer). This is expected to roll
127    // over as a function of the rate of update from the writer (eg. every ~9 hours if updating
128    // once a second).
129    seq_count_ptr: *const atomic::AtomicU32,
130
131    // A raw pointer into the shared memory segment, pointing to the VMClockShmBody section. Note
132    // that the structured reference by this pointer may not be consistent, and reading it requires
133    // to assert the seq_count value.
134    vmclock_shm_body_ptr: *const VMClockShmBody,
135
136    // The last snapshot of VMClockShmBody taken. This acts as a cache to avoid waiting for the
137    // writer to complete an update and allow to share a reference to this memory location
138    // (avoiding some memory copy). Keeping a state here and sharing it with the caller makes the
139    // VMClockShmReader not thread safe.
140    vmclock_shm_body_snapshot: VMClockShmBody,
141
142    // The value of seq_count when the VMClockShmBody snapshot was taken.
143    seq_count_snapshot: u32,
144}
145
146impl VMClockShmReader {
147    /// Open a VMClock shared memory segment for reading.
148    ///
149    /// On error, returns an appropriate `Errno`. If the content of the segment
150    /// is uninitialized, unparseable, or otherwise malformed, EPROTO will be
151    /// returned.
152    pub fn new(path: &str) -> Result<VMClockShmReader, ShmError> {
153        debug!("VMClockShmReader::new(): path is: {:?}", path);
154        let file = match File::open(path) {
155            Ok(f) => f,
156            Err(e) => {
157                error!("VMClockShmReader::new(): {:?}", e);
158                return Err(ShmError::SegmentNotInitialized);
159            }
160        };
161
162        debug!("VMClockShmReader::new(): Creating a MmapGuard ...");
163        let mmap_guard = MmapGuard::new(file)?;
164
165        // Create a cursor to pick the addresses of the various elements of interest in the shared
166        // memory segment.
167        let mut cursor: *const u8 = mmap_guard.segment.cast();
168        debug!("VMClockShmReader::new(): Created the cursor.");
169
170        // Pick fields from the VMClockShmHeader
171        // SAFETY: `cursor` is aligned to the start of the memory segment and the MmapGuard has
172        // validated the memory segment is large enough to contain the header.
173
174        let version_ptr = unsafe { ptr::addr_of!((*cursor.cast::<VMClockShmHeader>()).version) };
175        let counter_id_ptr =
176            unsafe { ptr::addr_of!((*cursor.cast::<VMClockShmHeader>()).counter_id) };
177        let time_type_ptr =
178            unsafe { ptr::addr_of!((*cursor.cast::<VMClockShmHeader>()).time_type) };
179        let seq_count_ptr =
180            unsafe { ptr::addr_of!((*cursor.cast::<VMClockShmHeader>()).seq_count) };
181
182        // Validate that the VMClock shared memory segment has a version number that is supported
183        // by this reader.
184        // SAFETY: MmapGuard has validated the memory segment of the header. `version_ptr` points
185        // to a part of the memory segment in this header.
186        let version = unsafe { &*version_ptr };
187        let version_number = version.load(atomic::Ordering::Acquire);
188        if version_number != VMCLOCK_SUPPORTED_VERSION {
189            error!("VMClock shared memory segment has version {:?} which is not supported by this version of the VMClockShmReader.", version_number);
190            return Err(ShmError::SegmentVersionNotSupported);
191        }
192
193        // Log the counter_id in the shared memory segment.
194        // SAFETY: MmapGuard has validated the memory segment of the header. `counter_id_ptr` points
195        // to a part of the memory segment in this header.
196        let counter_id = unsafe { &*counter_id_ptr };
197        let counter_id_value = counter_id.load(atomic::Ordering::Acquire);
198        debug!("VMClockShmReader::(): counter_id: {:?}", counter_id_value);
199
200        // Log the time_type in the shared memory segment.
201        // SAFETY: MmapGuard has validated the memory segment of the header. `time_type_ptr` points
202        // to a part of the memory segment in this header.
203        let time_type = unsafe { &*time_type_ptr };
204        let time_type_value = time_type.load(atomic::Ordering::Acquire);
205        debug!("VMClockShmReader::(): time_type: {:?}", time_type_value);
206
207        // Move to the end of the header and map the VMClockShmBody data, but only if the segment
208        // size allows it and matches our expectation.
209        if mmap_guard.segsize < (size_of::<VMClockShmHeader>() + size_of::<VMClockShmBody>()) {
210            error!("VMClockShmReader::new(): Segment size is smaller than expected.");
211            return Err(ShmError::SegmentMalformed);
212        }
213        // SAFETY: segment size has been checked to ensure `cursor` move leads to a valid cast
214        cursor = unsafe { cursor.add(size_of::<VMClockShmHeader>()) };
215        let vmclock_shm_body_ptr = unsafe { ptr::addr_of!(*cursor.cast::<VMClockShmBody>()) };
216
217        Ok(VMClockShmReader {
218            _marker: std::marker::PhantomData,
219            _guard: mmap_guard,
220            version_ptr,
221            seq_count_ptr,
222            vmclock_shm_body_ptr,
223            vmclock_shm_body_snapshot: VMClockShmBody::default(),
224            seq_count_snapshot: 0,
225        })
226    }
227
228    /// Return a consistent snapshot of the shared memory segment.
229    ///
230    /// Taking a snapshot consists in reading the memory segment while confirming the seq_count
231    /// number in the header has not changed (which would indicate an update from the writer
232    /// occurred while reading). If an update is detected, the read is retried.
233    ///
234    /// This function returns a reference to the VMClockShmBody snapshot stored by the reader, and
235    /// not an owned value. This make the VMClockShmReader NOT thread-safe: the data pointed to could be
236    /// updated without one of the thread knowing, leading to a incorrect clock error bond. The
237    /// advantage are in terms of performance: less data copied, but also no locking, yielding or
238    /// excessive retries.
239    pub fn snapshot(&mut self) -> Result<&VMClockShmBody, ShmError> {
240        // Atomically read the current version in the shared memory segment
241        // SAFETY: `self.version` has been validated when creating the reader
242        let version = unsafe { &*self.version_ptr };
243        let version = version.load(atomic::Ordering::Acquire);
244
245        // Validate version number.
246        //
247        // We are validating the version prior to each snapshot to protect
248        // against a Hypervisor which has implemented an unsupported VMClock version.
249        if version != VMCLOCK_SUPPORTED_VERSION {
250            error!("VMClock shared memory segment has version {:?} which is not supported by this version of the VMClockShmReader.", version);
251            return Err(ShmError::SegmentVersionNotSupported);
252        }
253
254        // Atomically read the current seq_count in the shared memory segment
255        // SAFETY: `self.seq_count_ptr` has been validated when creating the reader
256        let seq_count = unsafe { &*self.seq_count_ptr };
257        let mut seq_count_first = seq_count.load(atomic::Ordering::Acquire);
258
259        // Quick optimization, if the seq_count number matches the last one recorded, the shared
260        // memory segment has not been updated since last read. No need to read more of the memory
261        // segment, instead return the reference to the snapshot. This is useful in cases where the
262        // rate of clockbound read is much higher than the rate of write to the shared memory
263        // segment.
264        //
265        // Although the seq_count number could theoretically roll over, it is unlikely to do so
266        // over the lifespan of the instance running.
267        // Assuming an update on every jiffy with the largest Linux HZ value of 1000,
268        // updates would occur every 1 millisecond.  Therefore a roll-over would occur
269        // approximately after: 1 millisecond * ((2 ^ 64) - 1) = 584542046.0906265 years.
270        //
271        if seq_count_first == self.seq_count_snapshot {
272            return Ok(&self.vmclock_shm_body_snapshot);
273        }
274
275        // The seq_count number has changed since the last snapshot. Loop
276        // until we obtain a consistent read of the clock error bound data. This relies on reading
277        // the seq_count value twice, making sure they are identical and an even number.
278        //
279        // The writer of the VMClock in the production environment is expected to be the
280        // Hypervisor.  It is not expected to die, but we do cap the number of retries in case
281        // there is an unexpected bug in the Hypervisor.
282        let mut retries = u32::MAX;
283        while retries > 0 {
284            // Read the VMClockShmBody data from the shared memory
285            // SAFETY: `VMClockShmBody` has been checked to be valid while creating the VMClockShmReader
286            let snapshot = unsafe { self.vmclock_shm_body_ptr.read_volatile() };
287
288            // Confirm no update occurred during the read
289            let seq_count_second = seq_count.load(atomic::Ordering::Acquire);
290
291            // Only track complete updates indicated by an even seq_count number.
292            if seq_count_second & 0x0001 == 0 {
293                if seq_count_first == seq_count_second {
294                    self.seq_count_snapshot = seq_count_first;
295                    self.vmclock_shm_body_snapshot = snapshot;
296                    return Ok(&self.vmclock_shm_body_snapshot);
297                } else {
298                    seq_count_first = seq_count_second;
299                }
300            }
301            retries -= 1;
302        }
303
304        // Attempts to read the snapshot have failed.
305        Err(ShmError::SegmentNotInitialized)
306    }
307}
308
309#[cfg(test)]
310mod t_reader {
311    use super::*;
312    use crate::shm::VMClockClockStatus;
313    use std::fs::{File, OpenOptions};
314    use std::io::Write;
315    use std::path::Path;
316    /// We make use of tempfile::NamedTempFile to ensure that
317    /// local files that are created during a test get removed
318    /// afterwards.
319    use tempfile::NamedTempFile;
320
321    /// Test struct used to hold the expected fields in the VMClock shared memory segment.
322    #[repr(C)]
323    #[derive(Debug, Copy, Clone, PartialEq)]
324    struct VMClockContent {
325        magic: u32,
326        size: u32,
327        version: u16,
328        counter_id: u8,
329        time_type: u8,
330        seq_count: u32,
331        disruption_marker: u64,
332        flags: u64,
333        _padding: [u8; 2],
334        clock_status: VMClockClockStatus,
335        leap_second_smearing_hint: u8,
336        tai_offset_sec: i16,
337        leap_indicator: u8,
338        counter_period_shift: u8,
339        counter_value: u64,
340        counter_period_frac_sec: u64,
341        counter_period_esterror_rate_frac_sec: u64,
342        counter_period_maxerror_rate_frac_sec: u64,
343        time_sec: u64,
344        time_frac_sec: u64,
345        time_esterror_nanosec: u64,
346        time_maxerror_nanosec: u64,
347    }
348
349    fn write_vmclock_content(file: &mut File, vmclock_content: &VMClockContent) {
350        // Convert the VMClockShmBody struct into a slice so we can write it all out, fairly magic.
351        // Definitely needs the #[repr(C)] layout.
352        let slice = unsafe {
353            ::core::slice::from_raw_parts(
354                (vmclock_content as *const VMClockContent) as *const u8,
355                ::core::mem::size_of::<VMClockContent>(),
356            )
357        };
358
359        file.write_all(slice).expect("Write failed VMClockContent");
360        file.sync_all().expect("Sync to disk failed");
361    }
362
363    fn remove_path_if_exists(path_shm: &str) {
364        let path = Path::new(path_shm);
365        if path.exists() {
366            if path.is_dir() {
367                std::fs::remove_dir_all(path_shm).expect("failed to remove file");
368            } else {
369                std::fs::remove_file(path_shm).expect("failed to remove file");
370            }
371        }
372    }
373
374    /// Assert that the reader can map a file.
375    #[test]
376    fn test_reader_new() {
377        let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
378        let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
379        let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
380        let mut vmclock_shm_file = OpenOptions::new()
381            .write(true)
382            .open(vmclock_shm_path)
383            .expect("open vmclock file failed");
384        let vmclock_content = VMClockContent {
385            magic: 0x4B4C4356,
386            size: 104_u32,
387            version: 1_u16,
388            counter_id: 1_u8,
389            time_type: 0_u8,
390            seq_count: 10_u32,
391            disruption_marker: 888888_u64,
392            flags: 0_u64,
393            _padding: [0x00, 0x00],
394            clock_status: VMClockClockStatus::Synchronized,
395            leap_second_smearing_hint: 0_u8,
396            tai_offset_sec: 0_i16,
397            leap_indicator: 0_u8,
398            counter_period_shift: 0_u8,
399            counter_value: 123456_u64,
400            counter_period_frac_sec: 0_u64,
401            counter_period_esterror_rate_frac_sec: 0_u64,
402            counter_period_maxerror_rate_frac_sec: 0_u64,
403            time_sec: 0_u64,
404            time_frac_sec: 0_u64,
405            time_esterror_nanosec: 0_u64,
406            time_maxerror_nanosec: 0_u64,
407        };
408        write_vmclock_content(&mut vmclock_shm_file, &vmclock_content);
409
410        let reader =
411            VMClockShmReader::new(&vmclock_shm_path).expect("Failed to create VMClockShmReader");
412
413        let version = unsafe { &*reader.version_ptr };
414        let seq_count = unsafe { &*reader.seq_count_ptr };
415        let vmclock_shm_body = unsafe { *reader.vmclock_shm_body_ptr };
416
417        assert_eq!(version.load(atomic::Ordering::Relaxed), 1_u16);
418        assert_eq!(seq_count.load(atomic::Ordering::Relaxed), 10_u32);
419        assert_eq!(vmclock_shm_body.counter_value, 123456_u64);
420        assert_eq!(
421            vmclock_shm_body.clock_status,
422            VMClockClockStatus::Synchronized
423        );
424        assert_eq!(vmclock_shm_body.disruption_marker, 888888_u64);
425    }
426
427    /// Assert that the reader will return an error when it tries to open a file that does not exist.
428    #[test]
429    fn test_reader_file_does_not_exist() {
430        let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
431        let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
432        let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
433        remove_path_if_exists(vmclock_shm_path);
434
435        let expected = ShmError::SegmentNotInitialized;
436        match VMClockShmReader::new(&vmclock_shm_path) {
437            Err(actual) => assert_eq!(expected, actual),
438            _ => assert!(false),
439        }
440    }
441
442    /// Assert that the reader will return an error when it tries to open a file that is empty.
443    #[test]
444    fn test_reader_file_is_empty() {
445        let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
446        let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
447        let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
448
449        let expected = ShmError::SegmentNotInitialized;
450        match VMClockShmReader::new(&vmclock_shm_path) {
451            Err(actual) => assert_eq!(expected, actual),
452            _ => assert!(false),
453        }
454    }
455
456    /// Assert that the reader will return an error when it tries to read a file
457    /// that has an unsupported VMClock version.
458    #[test]
459    fn test_reader_version_not_supported() {
460        let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
461        let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
462        let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
463        let mut vmclock_shm_file = OpenOptions::new()
464            .write(true)
465            .open(vmclock_shm_path)
466            .expect("open vmclock file failed");
467        let vmclock_content = VMClockContent {
468            magic: 0x4B4C4356,
469            size: 104_u32,
470            version: 999_u16,
471            counter_id: 1_u8,
472            time_type: 0_u8,
473            seq_count: 10_u32,
474            disruption_marker: 888888_u64,
475            flags: 0_u64,
476            _padding: [0x00, 0x00],
477            clock_status: VMClockClockStatus::Synchronized,
478            leap_second_smearing_hint: 0_u8,
479            tai_offset_sec: 0_i16,
480            leap_indicator: 0_u8,
481            counter_period_shift: 0_u8,
482            counter_value: 123456_u64,
483            counter_period_frac_sec: 0_u64,
484            counter_period_esterror_rate_frac_sec: 0_u64,
485            counter_period_maxerror_rate_frac_sec: 0_u64,
486            time_sec: 0_u64,
487            time_frac_sec: 0_u64,
488            time_esterror_nanosec: 0_u64,
489            time_maxerror_nanosec: 0_u64,
490        };
491        write_vmclock_content(&mut vmclock_shm_file, &vmclock_content);
492
493        let expected = ShmError::SegmentVersionNotSupported;
494        match VMClockShmReader::new(&vmclock_shm_path) {
495            Err(actual) => assert_eq!(expected, actual),
496            _ => assert!(false),
497        }
498    }
499
500    /// Assert that the reader will return an error when it tries to read a file
501    /// that has a segement size that is too small to fit the VMClockShmHeader.
502    #[test]
503    fn test_reader_segment_size_smaller_than_header() {
504        let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
505        let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
506        let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
507        let mut vmclock_shm_file = OpenOptions::new()
508            .write(true)
509            .open(vmclock_shm_path)
510            .expect("open vmclock file failed");
511        let vmclock_content = VMClockContent {
512            magic: 0x4B4C4356,
513            size: 8_u32, // Writing a size smaller than the header size of 16.
514            version: 1_u16,
515            counter_id: 1_u8,
516            time_type: 0_u8,
517            seq_count: 10_u32,
518            disruption_marker: 888888_u64,
519            flags: 0_u64,
520            _padding: [0x00, 0x00],
521            clock_status: VMClockClockStatus::Synchronized,
522            leap_second_smearing_hint: 0_u8,
523            tai_offset_sec: 0_i16,
524            leap_indicator: 0_u8,
525            counter_period_shift: 0_u8,
526            counter_value: 123456_u64,
527            counter_period_frac_sec: 0_u64,
528            counter_period_esterror_rate_frac_sec: 0_u64,
529            counter_period_maxerror_rate_frac_sec: 0_u64,
530            time_sec: 0_u64,
531            time_frac_sec: 0_u64,
532            time_esterror_nanosec: 0_u64,
533            time_maxerror_nanosec: 0_u64,
534        };
535        write_vmclock_content(&mut vmclock_shm_file, &vmclock_content);
536
537        let expected = ShmError::SegmentMalformed;
538        match VMClockShmReader::new(&vmclock_shm_path) {
539            Err(actual) => assert_eq!(expected, actual),
540            _ => assert!(false),
541        }
542    }
543
544    /// Assert that the reader will return an error when it tries to read a file
545    /// that has a segement size that is large enough for the VMClockShmHeader,
546    /// but not large enough for both the VMClockShmHeader and VMClockShmBody.
547    #[test]
548    fn test_reader_segment_size_smaller_than_header_and_body() {
549        let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
550        let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
551        let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
552        let mut vmclock_shm_file = OpenOptions::new()
553            .write(true)
554            .open(vmclock_shm_path)
555            .expect("open vmclock file failed");
556        let vmclock_content = VMClockContent {
557            magic: 0x4B4C4356,
558            size: 26_u32, // Writing a size slightly larger than the header size of 16.
559            version: 1_u16,
560            counter_id: 1_u8,
561            time_type: 0_u8,
562            seq_count: 10_u32,
563            disruption_marker: 888888_u64,
564            flags: 0_u64,
565            _padding: [0x00, 0x00],
566            clock_status: VMClockClockStatus::Synchronized,
567            leap_second_smearing_hint: 0_u8,
568            tai_offset_sec: 0_i16,
569            leap_indicator: 0_u8,
570            counter_period_shift: 0_u8,
571            counter_value: 123456_u64,
572            counter_period_frac_sec: 0_u64,
573            counter_period_esterror_rate_frac_sec: 0_u64,
574            counter_period_maxerror_rate_frac_sec: 0_u64,
575            time_sec: 0_u64,
576            time_frac_sec: 0_u64,
577            time_esterror_nanosec: 0_u64,
578            time_maxerror_nanosec: 0_u64,
579        };
580        write_vmclock_content(&mut vmclock_shm_file, &vmclock_content);
581
582        let expected = ShmError::SegmentMalformed;
583        match VMClockShmReader::new(&vmclock_shm_path) {
584            Err(actual) => assert_eq!(expected, actual),
585            _ => assert!(false),
586        }
587    }
588}