Skip to content

Commit 641d08f

Browse files
roypatandreeaflorescu
authored andcommitted
Make copy_slice_volatile use pointers instead of slices
First commit in a series of commits to elimate slices to guest memory. Additionally, changes pointer arithmetic to use std:ptr::add and adds safety comments explaining why invariants of pointer arithmetic are upheld. Function is now marked unsafe because part of the invariants have to be upheld by the caller. Requirement for the memory regions to be non overlapping is due to the copy loop not correctly copying values if the regions do overlap (however, since the function is only called for copy from guest-memory to rust-memory or vice-versa, this is trivially unheld in praxis). Lastly, changes `copy_single` to take pointer arguments instead of usize, to avoid unneccessary conversion of the form *u8 -> usize -> *u8. Related to #45 Signed-off-by: Patrick Roy <[email protected]>
1 parent 06e797b commit 641d08f

File tree

1 file changed

+37
-12
lines changed

1 file changed

+37
-12
lines changed

src/volatile_memory.rs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,32 +1318,55 @@ mod copy_slice_impl {
13181318
// - `src_addr` and `dst_addr` must be properly aligned with respect to `align`.
13191319
// - `src_addr` must point to a properly initialized value, which is true here because
13201320
// we're only using integer primitives.
1321-
unsafe fn copy_single(align: usize, src_addr: usize, dst_addr: usize) {
1321+
unsafe fn copy_single(align: usize, src_addr: *const u8, dst_addr: *mut u8) {
13221322
match align {
13231323
8 => write_volatile(dst_addr as *mut u64, read_volatile(src_addr as *const u64)),
13241324
4 => write_volatile(dst_addr as *mut u32, read_volatile(src_addr as *const u32)),
13251325
2 => write_volatile(dst_addr as *mut u16, read_volatile(src_addr as *const u16)),
1326-
1 => write_volatile(dst_addr as *mut u8, read_volatile(src_addr as *const u8)),
1326+
1 => write_volatile(dst_addr, read_volatile(src_addr)),
13271327
_ => unreachable!(),
13281328
}
13291329
}
13301330

1331-
fn copy_slice_volatile(dst: &mut [u8], src: &[u8]) -> usize {
1332-
let total = min(src.len(), dst.len());
1331+
/// Copies `total` bytes from `src` to `dst` using a loop of volatile reads and writes
1332+
///
1333+
/// SAFETY: `src` and `dst` must be point to a contiguously allocated memory region of at least
1334+
/// length `total`. The regions must not overlap
1335+
unsafe fn copy_slice_volatile(mut dst: *mut u8, mut src: *const u8, total: usize) -> usize {
13331336
let mut left = total;
13341337

1335-
let mut src_addr = src.as_ptr() as usize;
1336-
let mut dst_addr = dst.as_ptr() as usize;
1337-
let align = min(alignment(src_addr), alignment(dst_addr));
1338+
let align = min(alignment(src as usize), alignment(dst as usize));
13381339

13391340
let mut copy_aligned_slice = |min_align| {
1340-
while align >= min_align && left >= min_align {
1341+
if align < min_align {
1342+
return;
1343+
}
1344+
1345+
while left >= min_align {
13411346
// SAFETY: Safe because we check alignment beforehand, the memory areas are valid
13421347
// for reads/writes, and the source always contains a valid value.
1343-
unsafe { copy_single(min_align, src_addr, dst_addr) };
1344-
src_addr += min_align;
1345-
dst_addr += min_align;
1348+
unsafe { copy_single(min_align, src, dst) };
1349+
13461350
left -= min_align;
1351+
1352+
if left == 0 {
1353+
break;
1354+
}
1355+
1356+
// SAFETY: We only explain the invariants for `src`, the argument for `dst` is
1357+
// analogous.
1358+
// - `src` and `src + min_align` are within (or one byte past) the same allocated object
1359+
// This is given by the invariant on this function ensuring that [src, src + total)
1360+
// are part of the same allocated object, and the condition on the while loop
1361+
// ensures that we do not go outside this object
1362+
// - The computed offset in bytes cannot overflow isize, because `min_align` is at
1363+
// most 8 when the closure is called (see below)
1364+
// - The sum `src as usize + min_align` can only wrap around if src as usize + min_align - 1 == usize::MAX,
1365+
// however in this case, left == 0, and we'll have exited the loop above.
1366+
unsafe {
1367+
src = src.add(min_align);
1368+
dst = dst.add(min_align);
1369+
}
13471370
}
13481371
};
13491372

@@ -1360,7 +1383,9 @@ mod copy_slice_impl {
13601383
pub(super) fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize {
13611384
let total = min(src.len(), dst.len());
13621385
if total <= size_of::<usize>() {
1363-
copy_slice_volatile(dst, src);
1386+
// SAFETY: we take pointers to slices, which are assured to reference contiguously
1387+
// allocated memory of length total (as total is the minimum of the lengths of the slices)
1388+
unsafe { copy_slice_volatile(dst.as_mut_ptr(), src.as_ptr(), total) };
13641389
} else {
13651390
dst[..total].copy_from_slice(&src[..total]);
13661391
}

0 commit comments

Comments
 (0)