Skip to content

Commit aff1dd4

Browse files
committed
fix: Validate return value of get_slice in VolatileMemory
An issue was discovered in the default implementations of the VolatileMemory::{get_atomic_ref, aligned_as_ref, aligned_as_mut, get_ref, get_array_ref} trait functions, which allows out-of-bounds memory access if the VolatileMemory::get_slice function returns a VolatileSlice whose length is less than the function’s count argument. No implementations of get_slice provided in vm_memory are affected. Users of custom VolatileMemory implementations may be impacted if the custom implementation does not adhere to get_slice's documentation. This commit fixes this issue by inserting a check that verifies that the VolatileSlice returned by get_slice is of the correct length. Signed-off-by: Patrick Roy <roypat@amazon.co.uk>
1 parent 06ebc2a commit aff1dd4

File tree

3 files changed

+78
-21
lines changed

3 files changed

+78
-21
lines changed

CHANGELOG.md

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,23 @@
11
# Changelog
2-
## [Unreleased]
32

4-
### Added
5-
6-
### Changed
3+
## [v0.12.2]
74

85
### Fixed
6+
- [[#251]](https://github.com/rust-vmm/vm-memory/pull/251): Inserted checks
7+
that verify that the value returned by `VolatileMemory::get_slice` is of
8+
the correct length.
99

1010
### Deprecated
11+
- [[#244]](https://github.com/rust-vmm/vm-memory/pull/241) Deprecate volatile
12+
memory's `as_ptr()` interfaces. The new interfaces to be used instead are:
13+
`ptr_guard()` and `ptr_guard_mut()`.
1114

1215
## [v0.12.1]
1316

1417
### Fixed
1518
- [[#241]](https://github.com/rust-vmm/vm-memory/pull/245) mmap_xen: Don't drop
1619
the FileOffset while in use #245
1720

18-
## [Unreleased]
19-
20-
### Deprecated
21-
22-
- [[#244]](https://github.com/rust-vmm/vm-memory/pull/241) Deprecate volatile
23-
memory's `as_ptr()` interfaces. The new interfaces to be used instead are:
24-
`ptr_guard()` and `ptr_guard_mut()`.
25-
2621
## [v0.12.0]
2722

2823
### Added

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "vm-memory"
3-
version = "0.12.1"
3+
version = "0.12.2"
44
description = "Safe abstractions for accessing the VM physical memory"
55
keywords = ["memory"]
66
categories = ["memory-management"]

src/volatile_memory.rs

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ pub trait VolatileMemory {
109109

110110
/// Returns a [`VolatileSlice`](struct.VolatileSlice.html) of `count` bytes starting at
111111
/// `offset`.
112+
///
113+
/// Note that the property `get_slice(offset, count).len() == count` MUST NOT be
114+
/// relied on for the correctness of unsafe code. This is a safe function inside of a
115+
/// safe trait, and implementors are under no obligation to follow its documentation.
112116
fn get_slice(&self, offset: usize, count: usize) -> Result<VolatileSlice<BS<Self::B>>>;
113117

114118
/// Gets a slice of memory for the entire region that supports volatile access.
@@ -119,8 +123,18 @@ pub trait VolatileMemory {
119123
/// Gets a `VolatileRef` at `offset`.
120124
fn get_ref<T: ByteValued>(&self, offset: usize) -> Result<VolatileRef<T, BS<Self::B>>> {
121125
let slice = self.get_slice(offset, size_of::<T>())?;
122-
// SAFETY: This is safe because the pointer is range-checked by get_slice, and
123-
// the lifetime is the same as self.
126+
127+
assert_eq!(
128+
slice.len(),
129+
size_of::<T>(),
130+
"VolatileMemory::get_slice(offset, count) returned slice of length != count."
131+
);
132+
133+
// SAFETY: This is safe because the invariants of the constructors of VolatileSlice ensure that
134+
// slice.addr is valid memory of size slice.len(). The assert above ensures that
135+
// the length of the slice is exactly enough to hold one `T`. Lastly, the lifetime of the
136+
// returned VolatileRef match that of the VolatileSlice returned by get_slice and thus the
137+
// lifetime one `self`.
124138
unsafe {
125139
Ok(VolatileRef::with_bitmap(
126140
slice.addr,
@@ -146,8 +160,18 @@ pub trait VolatileMemory {
146160
size: size_of::<T>(),
147161
})?;
148162
let slice = self.get_slice(offset, nbytes as usize)?;
149-
// SAFETY: This is safe because the pointer is range-checked by get_slice, and
150-
// the lifetime is the same as self.
163+
164+
assert_eq!(
165+
slice.len(),
166+
nbytes as usize,
167+
"VolatileMemory::get_slice(offset, count) returned slice of length != count."
168+
);
169+
170+
// SAFETY: This is safe because the invariants of the constructors of VolatileSlice ensure that
171+
// slice.addr is valid memory of size slice.len(). The assert above ensures that
172+
// the length of the slice is exactly enough to hold `n` instances of `T`. Lastly, the lifetime of the
173+
// returned VolatileArrayRef match that of the VolatileSlice returned by get_slice and thus the
174+
// lifetime one `self`.
151175
unsafe {
152176
Ok(VolatileArrayRef::with_bitmap(
153177
slice.addr,
@@ -171,7 +195,21 @@ pub trait VolatileMemory {
171195
unsafe fn aligned_as_ref<T: ByteValued>(&self, offset: usize) -> Result<&T> {
172196
let slice = self.get_slice(offset, size_of::<T>())?;
173197
slice.check_alignment(align_of::<T>())?;
174-
Ok(&*(slice.addr as *const T))
198+
199+
assert_eq!(
200+
slice.len(),
201+
size_of::<T>(),
202+
"VolatileMemory::get_slice(offset, count) returned slice of length != count."
203+
);
204+
205+
// SAFETY: This is safe because the invariants of the constructors of VolatileSlice ensure that
206+
// slice.addr is valid memory of size slice.len(). The assert above ensures that
207+
// the length of the slice is exactly enough to hold one `T`.
208+
// Dereferencing the pointer is safe because we check the alignment above, and the invariants
209+
// of this function ensure that no aliasing pointers exist. Lastly, the lifetime of the
210+
// returned VolatileArrayRef match that of the VolatileSlice returned by get_slice and thus the
211+
// lifetime one `self`.
212+
unsafe { Ok(&*(slice.addr as *const T)) }
175213
}
176214

177215
/// Returns a mutable reference to an instance of `T` at `offset`. Mutable accesses performed
@@ -191,7 +229,21 @@ pub trait VolatileMemory {
191229
let slice = self.get_slice(offset, size_of::<T>())?;
192230
slice.check_alignment(align_of::<T>())?;
193231

194-
Ok(&mut *(slice.addr as *mut T))
232+
assert_eq!(
233+
slice.len(),
234+
size_of::<T>(),
235+
"VolatileMemory::get_slice(offset, count) returned slice of length != count."
236+
);
237+
238+
// SAFETY: This is safe because the invariants of the constructors of VolatileSlice ensure that
239+
// slice.addr is valid memory of size slice.len(). The assert above ensures that
240+
// the length of the slice is exactly enough to hold one `T`.
241+
// Dereferencing the pointer is safe because we check the alignment above, and the invariants
242+
// of this function ensure that no aliasing pointers exist. Lastly, the lifetime of the
243+
// returned VolatileArrayRef match that of the VolatileSlice returned by get_slice and thus the
244+
// lifetime one `self`.
245+
246+
unsafe { Ok(&mut *(slice.addr as *mut T)) }
195247
}
196248

197249
/// Returns a reference to an instance of `T` at `offset`. Mutable accesses performed
@@ -206,8 +258,18 @@ pub trait VolatileMemory {
206258
let slice = self.get_slice(offset, size_of::<T>())?;
207259
slice.check_alignment(align_of::<T>())?;
208260

209-
// SAFETY: This is safe because the pointer is range-checked by get_slice, and
210-
// the lifetime is the same as self.
261+
assert_eq!(
262+
slice.len(),
263+
size_of::<T>(),
264+
"VolatileMemory::get_slice(offset, count) returned slice of length != count."
265+
);
266+
267+
// SAFETY: This is safe because the invariants of the constructors of VolatileSlice ensure that
268+
// slice.addr is valid memory of size slice.len(). The assert above ensures that
269+
// the length of the slice is exactly enough to hold one `T`.
270+
// Dereferencing the pointer is safe because we check the alignment above. Lastly, the lifetime of the
271+
// returned VolatileArrayRef match that of the VolatileSlice returned by get_slice and thus the
272+
// lifetime one `self`.
211273
unsafe { Ok(&*(slice.addr as *const T)) }
212274
}
213275

0 commit comments

Comments
 (0)