1use crate::util::Address;
2use std::collections::HashMap;
3use std::io::{Error, ErrorKind, Result};
4use std::sync::{Mutex, RwLock};
5
6use super::layout::{
7 LOG_GLOBAL_SIDE_METADATA_WORST_CASE_RATIO, LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO,
8};
9use super::{SideMetadataContext, SideMetadataSpec};
10#[cfg(target_pointer_width = "64")]
11use crate::util::heap::layout::vm_layout::vm_layout;
12use crate::util::heap::layout::vm_layout::VMLayout;
13#[cfg(target_pointer_width = "32")]
14use crate::util::heap::layout::vm_layout::LOG_BYTES_IN_CHUNK;
15
16#[cfg(feature = "extreme_assertions")]
18enum MathOp {
19 Add,
20 Sub,
21}
22
23static GLOBAL_META_NAME: &str = "Global";
26
27pub struct SideMetadataSanity {
36 specs_sanity_map: HashMap<&'static str, Vec<SideMetadataSpec>>,
37}
38
39lazy_static! {
40 static ref CONTENT_SANITY_MAP: RwLock<HashMap<SideMetadataSpec, HashMap<Address, u64>>> =
45 RwLock::new(HashMap::new());
46 pub(crate) static ref SANITY_LOCK: Mutex<()> = Mutex::new(());
47}
48
49#[cfg(test)]
51pub(crate) fn reset() {
52 CONTENT_SANITY_MAP.write().unwrap().clear()
53}
54
55fn verify_global_specs_total_size(g_specs: &[SideMetadataSpec]) -> Result<()> {
63 let mut total_size = 0usize;
64 for spec in g_specs {
65 total_size += super::metadata_address_range_size(spec);
66 }
67
68 if total_size
69 <= 1usize << (VMLayout::LOG_ARCH_ADDRESS_SPACE - LOG_GLOBAL_SIDE_METADATA_WORST_CASE_RATIO)
70 {
71 Ok(())
72 } else {
73 Err(Error::new(
74 ErrorKind::InvalidInput,
75 format!("Not enough global metadata space for: \n{:?}", g_specs),
76 ))
77 }
78}
79
80#[cfg(target_pointer_width = "64")]
88fn verify_local_specs_size(l_specs: &[SideMetadataSpec]) -> Result<()> {
89 for spec in l_specs {
90 if super::metadata_address_range_size(spec)
91 > 1usize
92 << (VMLayout::LOG_ARCH_ADDRESS_SPACE - LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO)
93 {
94 return Err(Error::new(
95 ErrorKind::InvalidInput,
96 format!("Local metadata is too big: \n{:?}", spec),
97 ));
98 }
99 }
100
101 Ok(())
102}
103
104#[cfg(target_pointer_width = "32")]
112fn verify_local_specs_size(l_specs: &[SideMetadataSpec]) -> Result<()> {
113 let mut total_size = 0usize;
114 for spec in l_specs {
115 total_size +=
116 super::metadata_bytes_per_chunk(spec.log_bytes_in_region, spec.log_num_of_bits);
117 }
118
119 if total_size > 1usize << (LOG_BYTES_IN_CHUNK - LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO) {
120 return Err(Error::new(
121 ErrorKind::InvalidInput,
122 format!(
123 "Not enough local metadata space per chunk for: \n{:?}",
124 l_specs
125 ),
126 ));
127 }
128
129 Ok(())
130}
131
132fn verify_no_overlap_contiguous(
141 spec_1: &SideMetadataSpec,
142 spec_2: &SideMetadataSpec,
143) -> Result<()> {
144 let base = crate::util::metadata::side_metadata::layout::global_side_metadata_base_address();
145 let end_1 = base + super::metadata_address_range_size(spec_1);
146 let end_2 = base + super::metadata_address_range_size(spec_2);
147
148 if !(spec_1.get_starting_address() >= end_2 || spec_2.get_starting_address() >= end_1) {
149 return Err(Error::new(
150 ErrorKind::InvalidInput,
151 format!(
152 "Overlapping metadata specs detected:\nTHIS:\n{:#?}\nAND:\n{:#?}",
153 spec_1, spec_2
154 ),
155 ));
156 }
157 Ok(())
158}
159
160#[cfg(target_pointer_width = "32")]
169fn verify_no_overlap_chunked(spec_1: &SideMetadataSpec, spec_2: &SideMetadataSpec) -> Result<()> {
170 let end_1 = spec_1.get_offset_for_chunked()
171 + super::metadata_bytes_per_chunk(spec_1.log_bytes_in_region, spec_1.log_num_of_bits);
172 let end_2 = spec_2.get_offset_for_chunked()
173 + super::metadata_bytes_per_chunk(spec_2.log_bytes_in_region, spec_2.log_num_of_bits);
174
175 if !(spec_1.get_offset_for_chunked() >= end_2 || spec_2.get_offset_for_chunked() >= end_1) {
176 return Err(Error::new(
177 ErrorKind::InvalidInput,
178 format!(
179 "Overlapping metadata specs detected:\nTHIS:\n{:#?}\nAND:\n{:#?}",
180 spec_1, spec_2
181 ),
182 ));
183 }
184 Ok(())
185}
186
187fn verify_global_specs(g_specs: &[SideMetadataSpec]) -> Result<()> {
195 verify_global_specs_total_size(g_specs)?;
196
197 for spec_1 in g_specs {
198 for spec_2 in g_specs {
199 if spec_1 != spec_2 {
200 verify_no_overlap_contiguous(spec_1, spec_2)?;
201 }
202 }
203 }
204
205 Ok(())
206}
207
208impl Default for SideMetadataSanity {
210 fn default() -> Self {
211 Self::new()
212 }
213}
214
215impl SideMetadataSanity {
216 pub fn new() -> SideMetadataSanity {
218 SideMetadataSanity {
219 specs_sanity_map: HashMap::new(),
220 }
221 }
222 fn get_all_specs(&self, global: bool) -> Vec<SideMetadataSpec> {
230 let mut specs = vec![];
231 for (k, v) in self.specs_sanity_map.iter() {
232 if !(global ^ (*k == GLOBAL_META_NAME)) {
233 specs.append(&mut (*v).clone());
234 }
235 }
236 std::collections::HashSet::<SideMetadataSpec>::from_iter(specs)
238 .into_iter()
239 .collect()
240 }
241
242 fn verify_local_specs(&self) -> Result<()> {
249 let local_specs = self.get_all_specs(false);
250
251 verify_local_specs_size(&local_specs)?;
252
253 for spec_1 in &local_specs {
254 for spec_2 in &local_specs {
255 if spec_1 != spec_2 {
256 #[cfg(target_pointer_width = "64")]
257 verify_no_overlap_contiguous(spec_1, spec_2)?;
258 #[cfg(target_pointer_width = "32")]
259 verify_no_overlap_chunked(spec_1, spec_2)?;
260 }
261 }
262 }
263 Ok(())
264 }
265
266 pub(crate) fn verify_metadata_context(
279 &mut self,
280 policy_name: &'static str,
281 metadata_context: &SideMetadataContext,
282 ) {
283 let mut content_sanity_map = CONTENT_SANITY_MAP.write().unwrap();
284
285 let first_call = !self.specs_sanity_map.contains_key(&GLOBAL_META_NAME);
287
288 if first_call {
289 verify_global_specs(&metadata_context.global).unwrap();
291 self.specs_sanity_map
292 .insert(GLOBAL_META_NAME, metadata_context.global.clone());
293 } else {
294 let g_specs = self.specs_sanity_map.get(&GLOBAL_META_NAME).unwrap();
296 assert!(
297 g_specs.len() == metadata_context.global.len(),
298 "Global metadata must not change between policies! NEW SPECS: {:#?} OLD SPECS: {:#?}",
299 metadata_context.global,
300 g_specs
301 );
302 }
303
304 for spec in &metadata_context.global {
305 assert!(
307 spec.is_global,
308 "Policy-specific spec {:#?} detected in the global specs: {:#?}",
309 spec, metadata_context.global
310 );
311 if first_call {
314 content_sanity_map.insert(*spec, HashMap::new());
316 } else if !self
317 .specs_sanity_map
318 .get(&GLOBAL_META_NAME)
319 .unwrap()
320 .contains(spec)
321 {
322 panic!("Global metadata must not change between policies! NEW SPEC: {:#?} OLD SPECS: {:#?}", spec, self.get_all_specs(true));
323 }
324 }
325
326 let first_call = !self.specs_sanity_map.contains_key(&policy_name);
328
329 if first_call {
330 self.specs_sanity_map
331 .insert(policy_name, metadata_context.local.clone());
332 }
333
334 for spec in &metadata_context.local {
335 assert!(
337 !spec.is_global,
338 "Global spec {:#?} detected in the policy-specific specs: {:#?}",
339 spec, metadata_context.local
340 );
341 if first_call {
345 content_sanity_map.insert(*spec, HashMap::new());
347 } else if !self
348 .specs_sanity_map
349 .get(policy_name)
350 .unwrap()
351 .contains(spec)
352 {
353 panic!(
354 "Policy-specific metadata for -{}- changed from {:#?} to {:#?}",
355 policy_name,
356 self.specs_sanity_map.get(policy_name).unwrap(),
357 metadata_context.local
358 )
359 }
360 }
361
362 self.verify_local_specs().unwrap();
363 }
364
365 #[cfg(test)]
366 pub fn reset(&mut self) {
367 let mut content_sanity_map = CONTENT_SANITY_MAP.write().unwrap();
368 self.specs_sanity_map.clear();
369 content_sanity_map.clear();
370 }
371}
372
373fn verify_metadata_address_bound(spec: &SideMetadataSpec, data_addr: Address) {
377 #[cfg(target_pointer_width = "32")]
378 assert_eq!(VMLayout::LOG_ARCH_ADDRESS_SPACE, 32, "We assume we use all address space in 32 bits. This seems not true any more, we need a proper check here.");
379 #[cfg(target_pointer_width = "32")]
380 let data_addr_in_address_space = true;
381 #[cfg(target_pointer_width = "64")]
382 let data_addr_in_address_space =
383 data_addr <= unsafe { Address::from_usize(1usize << vm_layout().log_address_space) };
384
385 if !data_addr_in_address_space {
386 warn!(
387 "We try get metadata {} for {}, which is not within the address space we should use",
388 data_addr, spec.name
389 );
390 }
391
392 let metadata_addr =
393 crate::util::metadata::side_metadata::address_to_meta_address(spec, data_addr);
394 let metadata_addr_bound = if spec.uses_contiguous_side_metadata() {
395 spec.upper_bound_address_for_contiguous()
396 } else {
397 #[cfg(target_pointer_width = "32")]
398 {
399 spec.upper_bound_address_for_chunked(data_addr)
400 }
401 #[cfg(target_pointer_width = "64")]
402 {
403 unreachable!()
404 }
405 };
406 assert!(
407 metadata_addr < metadata_addr_bound,
408 "We try access metadata address for address {} of spec {} that is not within the bound {}.",
409 data_addr,
410 spec.name,
411 metadata_addr_bound
412 );
413}
414
415#[cfg(feature = "extreme_assertions")]
424pub fn verify_bzero(metadata_spec: &SideMetadataSpec, start: Address, size: usize) {
425 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
426 let start = align_to_region_start(metadata_spec, start);
427 let end = align_to_region_start(metadata_spec, start + size);
428 match sanity_map.get_mut(metadata_spec) {
429 Some(spec_sanity_map) => {
430 for (k, v) in spec_sanity_map.iter_mut() {
432 if *k >= start && *k < end {
434 *v = 0;
435 }
436 }
437 }
438 None => {
439 panic!("Invalid Metadata Spec: {}", metadata_spec.name);
440 }
441 }
442}
443
444#[cfg(feature = "extreme_assertions")]
452pub fn verify_bset(metadata_spec: &SideMetadataSpec, start: Address, size: usize) {
453 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
454 let start = align_to_region_start(metadata_spec, start);
455 let end = align_to_region_start(metadata_spec, start + size);
456 let max_value = (1 << (1 << metadata_spec.log_num_of_bits)) - 1;
457 match sanity_map.get_mut(metadata_spec) {
458 Some(spec_sanity_map) => {
459 let mut cursor = start;
460 let step: usize = 1 << metadata_spec.log_bytes_in_region;
461 while cursor < end {
462 spec_sanity_map.insert(cursor, max_value);
463 cursor += step;
464 }
465 }
466 None => {
467 panic!("Invalid Metadata Spec!");
468 }
469 }
470}
471
472#[cfg(feature = "extreme_assertions")]
484pub fn verify_bcopy(
485 dst_spec: &SideMetadataSpec,
486 start: Address,
487 size: usize,
488 src_spec: &SideMetadataSpec,
489) {
490 assert_eq!(src_spec.log_num_of_bits, dst_spec.log_num_of_bits);
491 assert_eq!(src_spec.log_bytes_in_region, dst_spec.log_bytes_in_region);
492
493 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
494 let start = align_to_region_start(dst_spec, start);
495 let end = align_to_region_start(dst_spec, start + size);
496
497 let mut tmp_map = HashMap::new();
502
503 {
504 let src_map = sanity_map
505 .get_mut(src_spec)
506 .expect("Invalid source Metadata Spec!");
507
508 let mut cursor = start;
509 let step: usize = 1 << src_spec.log_bytes_in_region;
510 while cursor < end {
511 let src_value = src_map.get(&cursor).copied().unwrap_or(0u64);
512 tmp_map.insert(cursor, src_value);
513 cursor += step;
514 }
515 }
516 {
517 let dst_map = sanity_map
518 .get_mut(dst_spec)
519 .expect("Invalid destination Metadata Spec!");
520
521 let mut cursor = start;
522 let step: usize = 1 << dst_spec.log_bytes_in_region;
523 while cursor < end {
524 let src_value = tmp_map.get(&cursor).copied().unwrap();
525 dst_map.insert(cursor, src_value);
526 cursor += step;
527 }
528 }
529}
530
531#[cfg(feature = "extreme_assertions")]
532use crate::util::metadata::metadata_val_traits::*;
533
534#[cfg(feature = "extreme_assertions")]
535fn truncate_value<T: MetadataValue>(log_num_of_bits: usize, val: u64) -> u64 {
536 if log_num_of_bits < T::LOG2 as usize {
538 val & ((1 << (1 << log_num_of_bits)) - 1)
539 } else {
540 val
541 }
542}
543
544#[cfg(feature = "extreme_assertions")]
545#[cfg(test)]
546mod truncate_tests {
547 use super::*;
548
549 #[test]
550 fn test_truncate() {
551 assert_eq!(truncate_value::<u8>(2, 0), 0);
552 assert_eq!(truncate_value::<u8>(2, 15), 15);
553 assert_eq!(truncate_value::<u8>(2, 16), 0);
554 assert_eq!(truncate_value::<u8>(2, 17), 1);
555 }
556}
557
558fn align_to_region_start(spec: &SideMetadataSpec, data_addr: Address) -> Address {
561 data_addr.align_down(1 << spec.log_bytes_in_region)
562}
563
564#[cfg(feature = "extreme_assertions")]
575pub fn verify_load<T: MetadataValue>(
576 metadata_spec: &SideMetadataSpec,
577 data_addr: Address,
578 actual_val: T,
579) {
580 let data_addr = align_to_region_start(metadata_spec, data_addr);
581 let actual_val: u64 = actual_val.to_u64().unwrap();
582 verify_metadata_address_bound(metadata_spec, data_addr);
583 let sanity_map = &mut CONTENT_SANITY_MAP.read().unwrap();
584 match sanity_map.get(metadata_spec) {
585 Some(spec_sanity_map) => {
586 let expected_val = if let Some(expected_val) = spec_sanity_map.get(&data_addr) {
588 *expected_val
589 } else {
590 0u64
591 };
592 assert!(
593 expected_val == actual_val,
594 "verify_load({:#?}, {}) -> Expected (0x{:x}) but found (0x{:x})",
595 metadata_spec,
596 data_addr,
597 expected_val,
598 actual_val
599 );
600 }
601 None => panic!("Invalid Metadata Spec: {:#?}", metadata_spec),
602 }
603}
604
605#[cfg(feature = "extreme_assertions")]
614pub fn verify_store<T: MetadataValue>(
615 metadata_spec: &SideMetadataSpec,
616 data_addr: Address,
617 metadata: T,
618) {
619 let data_addr = align_to_region_start(metadata_spec, data_addr);
620 let metadata: u64 = metadata.to_u64().unwrap();
621 verify_metadata_address_bound(metadata_spec, data_addr);
622 let new_val_wrapped = truncate_value::<T>(metadata_spec.log_num_of_bits, metadata);
623 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
624 match sanity_map.get_mut(metadata_spec) {
625 Some(spec_sanity_map) => {
626 let content = spec_sanity_map.entry(data_addr).or_insert(0);
628 *content = new_val_wrapped;
629 }
630 None => panic!("Invalid Metadata Spec: {:#?}", metadata_spec),
631 }
632}
633
634#[cfg(feature = "extreme_assertions")]
645pub fn verify_update<T: MetadataValue>(
646 metadata_spec: &SideMetadataSpec,
647 data_addr: Address,
648 old_val: T,
649 new_val: T,
650) {
651 let data_addr = align_to_region_start(metadata_spec, data_addr);
652 verify_metadata_address_bound(metadata_spec, data_addr);
653
654 let new_val_wrapped =
656 truncate_value::<T>(metadata_spec.log_num_of_bits, new_val.to_u64().unwrap());
657
658 let sanity_map = &mut CONTENT_SANITY_MAP.write().unwrap();
659 match sanity_map.get_mut(metadata_spec) {
660 Some(spec_sanity_map) => {
661 let cur_val = spec_sanity_map.entry(data_addr).or_insert(0);
662 assert_eq!(
663 old_val.to_u64().unwrap(),
664 *cur_val,
665 "Expected old value: {} but found {}",
666 old_val,
667 cur_val
668 );
669 *cur_val = new_val_wrapped;
670 }
671 None => panic!("Invalid metadata spec: {:#?}", metadata_spec),
672 }
673}
674
675#[cfg(test)]
676mod tests {
677 use super::super::*;
678 use super::*;
679
680 #[test]
681 fn test_side_metadata_sanity_verify_global_specs_total_size() {
682 let spec_1 = SideMetadataSpec {
683 name: "spec_1",
684 is_global: true,
685 offset: 0,
686 log_num_of_bits: 0,
687 log_bytes_in_region: 0,
688 };
689 let spec_2 = SideMetadataSpec {
690 name: "spec_2",
691 is_global: true,
692 offset: side_metadata_offset_after(&spec_1),
693 log_num_of_bits: 0,
694 log_bytes_in_region: 0,
695 };
696
697 assert!(verify_global_specs_total_size(&[spec_1]).is_ok());
698 #[cfg(target_pointer_width = "64")]
699 assert!(verify_global_specs_total_size(&[spec_1, spec_2]).is_ok());
700 #[cfg(target_pointer_width = "32")]
701 assert!(verify_global_specs_total_size(&[spec_1, spec_2]).is_err());
702
703 let spec_2 = SideMetadataSpec {
704 name: "spec_2",
705 is_global: true,
706 offset: side_metadata_offset_after(&spec_1),
707 log_num_of_bits: 3,
708 log_bytes_in_region: 1,
709 };
710
711 assert!(verify_global_specs_total_size(&[spec_1, spec_2]).is_err());
712
713 let spec_1 = SideMetadataSpec {
714 name: "spec_1",
715 is_global: true,
716 offset: 0,
717 log_num_of_bits: 1,
718 #[cfg(target_pointer_width = "64")]
719 log_bytes_in_region: 0,
720 #[cfg(target_pointer_width = "32")]
721 log_bytes_in_region: 2,
722 };
723 let spec_2 = SideMetadataSpec {
724 name: "spec_2",
725 is_global: true,
726 offset: side_metadata_offset_after(&spec_1),
727 log_num_of_bits: 3,
728 #[cfg(target_pointer_width = "64")]
729 log_bytes_in_region: 2,
730 #[cfg(target_pointer_width = "32")]
731 log_bytes_in_region: 4,
732 };
733
734 assert!(verify_global_specs_total_size(&[spec_1, spec_2]).is_ok());
735 assert!(verify_global_specs_total_size(&[spec_1, spec_2, spec_1]).is_err());
736 }
737
738 #[test]
739 fn test_side_metadata_sanity_verify_no_overlap_contiguous() {
740 let spec_1 = SideMetadataSpec {
741 name: "spec_1",
742 is_global: true,
743 offset: 0,
744 log_num_of_bits: 0,
745 log_bytes_in_region: 0,
746 };
747 let spec_2 = SideMetadataSpec {
748 name: "spec_2",
749 is_global: true,
750 offset: side_metadata_offset_after(&spec_1),
751 log_num_of_bits: 0,
752 log_bytes_in_region: 0,
753 };
754
755 assert!(verify_no_overlap_contiguous(&spec_1, &spec_1).is_err());
756 assert!(verify_no_overlap_contiguous(&spec_1, &spec_2).is_ok());
757
758 let spec_1 = SideMetadataSpec {
759 name: "spec_1",
760 is_global: true,
761 offset: 1,
762 log_num_of_bits: 0,
763 log_bytes_in_region: 0,
764 };
765
766 assert!(verify_no_overlap_contiguous(&spec_1, &spec_2).is_ok());
769
770 let spec_1 = SideMetadataSpec {
771 name: "spec_1",
772 is_global: true,
773 offset: 0,
774 log_num_of_bits: 0,
775 log_bytes_in_region: 0,
776 };
777 let spec_2 = SideMetadataSpec {
778 name: "spec_2",
779 is_global: true,
780 offset: (Address::ZERO + metadata_address_range_size(&spec_1) - 1).as_usize(),
782 log_num_of_bits: 0,
783 log_bytes_in_region: 0,
784 };
785
786 assert!(verify_no_overlap_contiguous(&spec_1, &spec_2).is_err());
787 }
788
789 #[cfg(target_pointer_width = "32")]
790 #[test]
791 fn test_side_metadata_sanity_verify_no_overlap_chunked() {
792 let spec_1 = SideMetadataSpec {
793 name: "spec_1",
794 is_global: false,
795 offset: 0,
796 log_num_of_bits: 0,
797 log_bytes_in_region: 0,
798 };
799 let spec_2 = SideMetadataSpec {
800 name: "spec_2",
801 is_global: false,
802 offset: side_metadata_offset_after(&spec_1),
803 log_num_of_bits: 0,
804 log_bytes_in_region: 0,
805 };
806
807 assert!(verify_no_overlap_chunked(&spec_1, &spec_1).is_err());
808 assert!(verify_no_overlap_chunked(&spec_1, &spec_2).is_ok());
809
810 let spec_1 = SideMetadataSpec {
811 name: "spec_1",
812 is_global: false,
813 offset: 1,
814 log_num_of_bits: 0,
815 log_bytes_in_region: 0,
816 };
817
818 assert!(verify_no_overlap_chunked(&spec_1, &spec_2).is_err());
819
820 let spec_1 = SideMetadataSpec {
821 name: "spec_1",
822 is_global: false,
823 offset: 0,
824 log_num_of_bits: 0,
825 log_bytes_in_region: 0,
826 };
827 let spec_2 = SideMetadataSpec {
828 name: "spec_2",
829 is_global: false,
830 offset: spec_1.get_offset_for_chunked()
832 + metadata_bytes_per_chunk(spec_1.log_bytes_in_region, spec_1.log_num_of_bits)
833 - 1,
834 log_num_of_bits: 0,
835 log_bytes_in_region: 0,
836 };
837
838 assert!(verify_no_overlap_chunked(&spec_1, &spec_2).is_err());
839 }
840
841 #[cfg(target_pointer_width = "32")]
842 #[test]
843 fn test_side_metadata_sanity_verify_local_specs_size() {
844 let spec_1 = SideMetadataSpec {
845 name: "spec_1",
846 is_global: false,
847 offset: 0,
848 log_num_of_bits: 0,
849 log_bytes_in_region: 0,
850 };
851
852 assert!(verify_local_specs_size(&[spec_1]).is_ok());
853 assert!(verify_local_specs_size(&[spec_1, spec_1]).is_err());
854 assert!(verify_local_specs_size(&[spec_1, spec_1, spec_1, spec_1, spec_1]).is_err());
855 }
856
857 #[test]
858 fn test_side_metadata_sanity_get_all_local_specs() {
859 let spec_1 = SideMetadataSpec {
860 name: "spec_1",
861 is_global: false,
862 offset: 0,
863 log_num_of_bits: 0,
864 log_bytes_in_region: 0,
865 };
866
867 let mut sanity = SideMetadataSanity::new();
868 sanity.verify_metadata_context(
869 "policy1",
870 &SideMetadataContext {
871 global: vec![],
872 local: vec![spec_1],
873 },
874 );
875 sanity.verify_metadata_context(
876 "policy2",
877 &SideMetadataContext {
878 global: vec![],
879 local: vec![spec_1],
880 },
881 );
882
883 let local_specs = sanity.get_all_specs(false);
884 assert_eq!(local_specs.len(), 1);
885 }
886}