Skip to content

Commit 76d1174

Browse files
committed
Make the snapshot region readonly and enable CoW
There are still a few changes needed to be able to ensure that the host doesn't try to write to the snapshot region before it can be mmap'd readonly into the guest. However, the guest no longer tries to write to the snapshot region. Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com>
1 parent 3a54e19 commit 76d1174

20 files changed

Lines changed: 392 additions & 121 deletions

File tree

fuzz/fuzz_targets/host_call.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ fuzz_target!(
3535
let mut cfg = SandboxConfiguration::default();
3636
cfg.set_output_data_size(64 * 1024); // 64 KB output buffer
3737
cfg.set_input_data_size(64 * 1024); // 64 KB input buffer
38+
cfg.set_scratch_size(512 * 1024); // large scratch region to contain those buffers, any data copies, etc.
3839
let u_sbox = UninitializedSandbox::new(
3940
GuestBinary::FilePath(simple_guest_for_fuzzing_as_string().expect("Guest Binary Missing")),
4041
Some(cfg)

src/hyperlight_common/src/arch/amd64/vmem.rs

Lines changed: 118 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ limitations under the License.
2626
//! allocating intermediate tables as needed and setting appropriate flags on leaf PTEs
2727
2828
use crate::vmem::{
29-
BasicMapping, Mapping, MappingKind, TableMovabilityBase, TableOps, TableReadOps, Void,
29+
BasicMapping, CowMapping, Mapping, MappingKind, TableMovabilityBase, TableOps, TableReadOps,
30+
Void,
3031
};
3132

3233
// Paging Flags
@@ -64,6 +65,11 @@ const PAGE_CACHE_ENABLED: u64 = 0 << 4; // PCD - page cache disable bit not set
6465
const PAGE_WRITE_BACK: u64 = 0 << 3; // PWT - page write-through bit not set (write-back caching)
6566
const PAGE_PAT_WB: u64 = 0 << 7; // PAT - page attribute table index bit (0 for write-back memory when PCD=0, PWT=0)
6667

68+
// We use various patterns of the available-for-software-use bits to
69+
// represent certain special mappings.
70+
const PTE_AVL_MASK: u64 = 0x0000_0000_0000_0E00;
71+
const PAGE_AVL_COW: u64 = 1 << 9;
72+
6773
/// Returns PAGE_RW if writable is true, 0 otherwise
6874
#[inline(always)]
6975
const fn page_rw_flag(writable: bool) -> u64 {
@@ -417,7 +423,7 @@ unsafe fn map_page<
417423
r: MapResponse<Op, P>,
418424
) {
419425
let pte = match &mapping.kind {
420-
MappingKind::BasicMapping(bm) =>
426+
MappingKind::Basic(bm) =>
421427
// TODO: Support not readable
422428
// NOTE: On x86-64, there is no separate "readable" bit in the page table entry.
423429
// This means that pages cannot be made write-only or execute-only without also being readable.
@@ -437,6 +443,19 @@ unsafe fn map_page<
437443
page_rw_flag(bm.writable) | // R/W - set if writable
438444
PAGE_PRESENT // P - this entry is present
439445
}
446+
MappingKind::Cow(cm) => {
447+
(mapping.phys_base + (r.vmin - mapping.virt_base)) |
448+
page_nx_flag(cm.executable) | // NX - no execute unless allowed
449+
PAGE_AVL_COW |
450+
PAGE_PAT_WB | // PAT index bit for write-back memory
451+
PAGE_DIRTY_CLEAR | // dirty bit (set by CPU when written)
452+
PAGE_ACCESSED_CLEAR | // accessed bit cleared (will be set by CPU when page is accessed - but we dont use the access bit for anything at present)
453+
PAGE_CACHE_ENABLED | // leave caching enabled
454+
PAGE_WRITE_BACK | // use write-back caching
455+
PAGE_USER_ACCESS_DISABLED | // dont allow user access (no code runs in user mode for now)
456+
0 | // R/W - Cow page is never writable
457+
PAGE_PRESENT // P - this entry is present
458+
}
440459
};
441460
unsafe {
442461
write_entry_updating(op, r.update_parent, r.entry_ptr, pte);
@@ -517,7 +536,7 @@ pub unsafe fn virt_to_phys<'a, Op: TableReadOps + 'a>(
517536
op: impl core::convert::AsRef<Op> + Copy + 'a,
518537
address: u64,
519538
len: u64,
520-
) -> impl Iterator<Item = (VirtAddr, PhysAddr, BasicMapping)> + 'a {
539+
) -> impl Iterator<Item = Mapping> + 'a {
521540
// Undo sign-extension, and mask off any sub-page bits
522541
let vmin = (address & ((1u64 << VA_BITS) - 1)) & !(PAGE_SIZE as u64 - 1);
523542
let vmax = core::cmp::min(vmin + len, 1u64 << VA_BITS);
@@ -540,12 +559,27 @@ pub unsafe fn virt_to_phys<'a, Op: TableReadOps + 'a>(
540559
let sgn_bit = r.vmin >> (VA_BITS - 1);
541560
let sgn_bits = 0u64.wrapping_sub(sgn_bit) << VA_BITS;
542561
let virt_addr = sgn_bits | r.vmin;
543-
let perms = BasicMapping {
544-
readable: true,
545-
writable: (pte & PAGE_RW) != 0,
546-
executable: (pte & PAGE_NX) == 0,
562+
563+
let executable = (pte & PAGE_NX) == 0;
564+
let avl = pte & PTE_AVL_MASK;
565+
let kind = if avl == PAGE_AVL_COW {
566+
MappingKind::Cow(CowMapping {
567+
readable: true,
568+
executable,
569+
})
570+
} else {
571+
MappingKind::Basic(BasicMapping {
572+
readable: true,
573+
writable: (pte & PAGE_RW) != 0,
574+
executable,
575+
})
547576
};
548-
Some((virt_addr, phys_addr, perms))
577+
Some(Mapping {
578+
phys_base: phys_addr,
579+
virt_base: virt_addr,
580+
len: PAGE_SIZE as u64,
581+
kind,
582+
})
549583
})
550584
}
551585

@@ -721,7 +755,7 @@ mod tests {
721755
phys_base: 0x1000,
722756
virt_base: 0x1000,
723757
len: PAGE_SIZE as u64,
724-
kind: MappingKind::BasicMapping(BasicMapping {
758+
kind: MappingKind::Basic(BasicMapping {
725759
readable: true,
726760
writable: true,
727761
executable: false,
@@ -754,7 +788,7 @@ mod tests {
754788
phys_base: 0x2000,
755789
virt_base: 0x2000,
756790
len: PAGE_SIZE as u64,
757-
kind: MappingKind::BasicMapping(BasicMapping {
791+
kind: MappingKind::Basic(BasicMapping {
758792
readable: true,
759793
writable: false,
760794
executable: true,
@@ -777,7 +811,7 @@ mod tests {
777811
phys_base: 0x10000,
778812
virt_base: 0x10000,
779813
len: 4 * PAGE_SIZE as u64, // 4 pages = 16KB
780-
kind: MappingKind::BasicMapping(BasicMapping {
814+
kind: MappingKind::Basic(BasicMapping {
781815
readable: true,
782816
writable: true,
783817
executable: false,
@@ -810,7 +844,7 @@ mod tests {
810844
phys_base: 0x1000,
811845
virt_base: 0x1000,
812846
len: PAGE_SIZE as u64,
813-
kind: MappingKind::BasicMapping(BasicMapping {
847+
kind: MappingKind::Basic(BasicMapping {
814848
readable: true,
815849
writable: true,
816850
executable: false,
@@ -824,7 +858,7 @@ mod tests {
824858
phys_base: 0x5000,
825859
virt_base: 0x5000,
826860
len: PAGE_SIZE as u64,
827-
kind: MappingKind::BasicMapping(BasicMapping {
861+
kind: MappingKind::Basic(BasicMapping {
828862
readable: true,
829863
writable: true,
830864
executable: false,
@@ -849,7 +883,7 @@ mod tests {
849883
phys_base: 0x1000,
850884
virt_base: 0x1000,
851885
len: PAGE_SIZE as u64,
852-
kind: MappingKind::BasicMapping(BasicMapping {
886+
kind: MappingKind::Basic(BasicMapping {
853887
readable: true,
854888
writable: true,
855889
executable: false,
@@ -860,8 +894,75 @@ mod tests {
860894

861895
let result = unsafe { virt_to_phys(&ops, 0x1000, 1).next() };
862896
assert!(result.is_some(), "Should find mapped address");
863-
let pte = result.unwrap();
864-
assert_eq!(pte.1, 0x1000);
897+
let mapping = result.unwrap();
898+
assert_eq!(mapping.phys_base, 0x1000);
899+
}
900+
901+
#[test]
902+
fn test_virt_to_phys_unaligned_virt() {
903+
let ops = MockTableOps::new();
904+
let mapping = Mapping {
905+
phys_base: 0x1000,
906+
virt_base: 0x1000,
907+
len: PAGE_SIZE as u64,
908+
kind: MappingKind::Basic(BasicMapping {
909+
readable: true,
910+
writable: true,
911+
executable: false,
912+
}),
913+
};
914+
915+
unsafe { map(&ops, mapping) };
916+
917+
let result = unsafe { virt_to_phys(&ops, 0x1234, 1).next() };
918+
assert!(result.is_some(), "Should find mapped address");
919+
let mapping = result.unwrap();
920+
assert_eq!(mapping.phys_base, 0x1000);
921+
}
922+
923+
#[test]
924+
fn test_virt_to_phys_perms() {
925+
let test = |kind| {
926+
let ops = MockTableOps::new();
927+
let mapping = Mapping {
928+
phys_base: 0x1000,
929+
virt_base: 0x1000,
930+
len: PAGE_SIZE as u64,
931+
kind,
932+
};
933+
unsafe { map(&ops, mapping) };
934+
let result = unsafe { virt_to_phys(&ops, 0x1000, 1).next() };
935+
let mapping = result.unwrap();
936+
assert_eq!(mapping.kind, kind);
937+
};
938+
test(MappingKind::Basic(BasicMapping {
939+
readable: true,
940+
writable: false,
941+
executable: false,
942+
}));
943+
test(MappingKind::Basic(BasicMapping {
944+
readable: true,
945+
writable: false,
946+
executable: true,
947+
}));
948+
test(MappingKind::Basic(BasicMapping {
949+
readable: true,
950+
writable: true,
951+
executable: false,
952+
}));
953+
test(MappingKind::Basic(BasicMapping {
954+
readable: true,
955+
writable: true,
956+
executable: true,
957+
}));
958+
test(MappingKind::Cow(CowMapping {
959+
readable: true,
960+
executable: false,
961+
}));
962+
test(MappingKind::Cow(CowMapping {
963+
readable: true,
964+
executable: true,
965+
}));
865966
}
866967

867968
#[test]
@@ -880,7 +981,7 @@ mod tests {
880981
phys_base: 0x1000,
881982
virt_base: 0x1000,
882983
len: PAGE_SIZE as u64,
883-
kind: MappingKind::BasicMapping(BasicMapping {
984+
kind: MappingKind::Basic(BasicMapping {
884985
readable: true,
885986
writable: true,
886987
executable: false,

src/hyperlight_common/src/arch/i686/vmem.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ limitations under the License.
1717
// This file is just dummy definitions at the moment, in order to
1818
// allow compiling the guest for real mode boot scenarios.
1919

20-
use crate::vmem::{BasicMapping, Mapping, TableOps, TableReadOps, Void};
20+
use crate::vmem::{Mapping, TableOps, TableReadOps, Void};
2121

2222
pub const PAGE_SIZE: usize = 4096;
2323
pub const PAGE_TABLE_SIZE: usize = 4096;
@@ -31,10 +31,7 @@ pub unsafe fn map<Op: TableOps>(_op: &Op, _mapping: Mapping) {
3131
}
3232

3333
#[allow(clippy::missing_safety_doc)]
34-
pub unsafe fn virt_to_phys<Op: TableOps>(
35-
_op: &Op,
36-
_address: u64,
37-
) -> impl Iterator<Item = (VirtAddr, PhysAddr, BasicMapping)> {
34+
pub unsafe fn virt_to_phys<Op: TableOps>(_op: &Op, _address: u64) -> impl Iterator<Item = Mapping> {
3835
panic!(
3936
"vmem::virt_to_phys: i686 guests do not support booting the full hyperlight guest kernel"
4037
);

src/hyperlight_common/src/vmem.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,16 +180,23 @@ pub trait TableOps: TableReadOps {
180180
);
181181
}
182182

183-
#[derive(Debug)]
183+
#[derive(Debug, PartialEq, Clone, Copy)]
184184
pub struct BasicMapping {
185185
pub readable: bool,
186186
pub writable: bool,
187187
pub executable: bool,
188188
}
189189

190-
#[derive(Debug)]
190+
#[derive(Debug, PartialEq, Clone, Copy)]
191+
pub struct CowMapping {
192+
pub readable: bool,
193+
pub executable: bool,
194+
}
195+
196+
#[derive(Debug, PartialEq, Clone, Copy)]
191197
pub enum MappingKind {
192-
BasicMapping(BasicMapping),
198+
Basic(BasicMapping),
199+
Cow(CowMapping),
193200
/* TODO: What useful things other than basic mappings actually
194201
* require touching the tables? */
195202
}

0 commit comments

Comments
 (0)