Skip to content

Commit fecbb28

Browse files
slpandreeaflorescu
authored andcommitted
mmap_unix: support for externally managed mappings
Add the unsafe MmapRegion::build_raw method to allow users to instance MmapRegion with externally managed mappings. Add also a field to indicate whether the mapping is owned by the MmapRegion instance or not, so we don't try to munmap it on MmapRegion::Drop. Signed-off-by: Sergio Lopez <[email protected]>
1 parent eff9d5c commit fecbb28

File tree

3 files changed

+91
-3
lines changed

3 files changed

+91
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
- [[#106]](https://github.com/rust-vmm/vm-memory/issues/106): Asserts trigger
66
on zero-length access.
77

8+
### Added
9+
- [[#109]](https://github.com/rust-vmm/vm-memory/pull/109): Added `build_raw` to
10+
`MmapRegion` which can be used to operate on externally created mappings.
11+
812
## [v0.2.0]
913

1014
### Added

coverage_config_x86_64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 85.4,
2+
"coverage_score": 85.1,
33
"exclude_path": "mmap_windows.rs",
44
"crate_features": "backend-mmap,backend-atomic"
55
}

src/mmap_unix.rs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ use crate::volatile_memory::{self, compute_offset, VolatileMemory, VolatileSlice
2828
pub enum Error {
2929
/// The specified file offset and length cause overflow when added.
3030
InvalidOffsetLength,
31+
/// The specified pointer to the mapping is not page-aligned.
32+
InvalidPointer,
33+
/// The specified size for the region is not a multiple of the page size.
34+
InvalidSize,
3135
/// The forbidden `MAP_FIXED` flag was specified.
3236
MapFixed,
3337
/// Mappings using the same fd overlap in terms of file offset and length.
@@ -45,6 +49,14 @@ impl fmt::Display for Error {
4549
f,
4650
"The specified file offset and length cause overflow when added"
4751
),
52+
Error::InvalidPointer => write!(
53+
f,
54+
"The specified pointer to the mapping is not page-aligned",
55+
),
56+
Error::InvalidSize => write!(
57+
f,
58+
"The specified size for the region is not a multiple of the page size",
59+
),
4860
Error::MapFixed => write!(f, "The forbidden `MAP_FIXED` flag was specified"),
4961
Error::MappingOverlap => write!(
5062
f,
@@ -79,6 +91,7 @@ pub struct MmapRegion {
7991
file_offset: Option<FileOffset>,
8092
prot: i32,
8193
flags: i32,
94+
owned: bool,
8295
}
8396

8497
// Send and Sync aren't automatically inherited for the raw address pointer.
@@ -160,6 +173,48 @@ impl MmapRegion {
160173
file_offset,
161174
prot,
162175
flags,
176+
owned: true,
177+
})
178+
}
179+
180+
/// Creates a MmapRegion instance for an externally managed mapping.
181+
///
182+
/// This method is intended to be used exclusively in situations in which the mapping backing
183+
/// the region is provided by an entity outside the control of the caller (e.g. the dynamic
184+
/// linker).
185+
///
186+
/// # Arguments
187+
/// * `addr` - Pointer to the start of the mapping. Must be page-aligned.
188+
/// * `size` - The size of the memory region in bytes. Must be a multiple of the page size.
189+
/// * `prot` - Must correspond to the memory protection attributes of the existing mapping.
190+
/// * `flags` - Must correspond to the flags that were passed to `mmap` for the creation of
191+
/// the existing mapping.
192+
///
193+
/// # Safety
194+
///
195+
/// To use this safely, the caller must guarantee that `addr` and `size` define a region within
196+
/// a valid mapping that is already present in the process.
197+
pub unsafe fn build_raw(addr: *mut u8, size: usize, prot: i32, flags: i32) -> Result<Self> {
198+
// Safe because this call just returns the page size and doesn't have any side effects.
199+
let page_size = libc::sysconf(libc::_SC_PAGESIZE) as usize;
200+
201+
// Check that the pointer to the mapping is page-aligned.
202+
if (addr as usize) & (page_size - 1) != 0 {
203+
return Err(Error::InvalidPointer);
204+
}
205+
206+
// Check that the size is a multiple of the page size.
207+
if size & (page_size - 1) != 0 {
208+
return Err(Error::InvalidSize);
209+
}
210+
211+
Ok(Self {
212+
addr,
213+
size,
214+
file_offset: None,
215+
prot,
216+
flags,
217+
owned: false,
163218
})
164219
}
165220

@@ -190,6 +245,11 @@ impl MmapRegion {
190245
self.flags
191246
}
192247

248+
/// Returns `true` if the mapping is owned by this `MmapRegion` instance.
249+
pub fn owned(&self) -> bool {
250+
self.owned
251+
}
252+
193253
/// Checks whether this region and `other` are backed by overlapping
194254
/// [`FileOffset`](struct.FileOffset.html) objects.
195255
///
@@ -252,8 +312,10 @@ impl Drop for MmapRegion {
252312
fn drop(&mut self) {
253313
// This is safe because we mmap the area at addr ourselves, and nobody
254314
// else is holding a reference to it.
255-
unsafe {
256-
libc::munmap(self.addr as *mut libc::c_void, self.size);
315+
if self.owned {
316+
unsafe {
317+
libc::munmap(self.addr as *mut libc::c_void, self.size);
318+
}
257319
}
258320
}
259321
}
@@ -373,6 +435,28 @@ mod tests {
373435
assert_eq!(r.file_offset().unwrap().start(), offset as u64);
374436
assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
375437
assert_eq!(r.flags(), libc::MAP_NORESERVE | libc::MAP_PRIVATE);
438+
assert!(r.owned());
439+
}
440+
441+
#[test]
442+
fn test_mmap_region_build_raw() {
443+
let addr = 0;
444+
let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
445+
let prot = libc::PROT_READ | libc::PROT_WRITE;
446+
let flags = libc::MAP_NORESERVE | libc::MAP_PRIVATE;
447+
448+
let r = unsafe { MmapRegion::build_raw((addr + 1) as *mut u8, size, prot, flags) };
449+
assert_eq!(format!("{:?}", r.unwrap_err()), format!("InvalidPointer"));
450+
451+
let r = unsafe { MmapRegion::build_raw(addr as *mut u8, size + 1, prot, flags) };
452+
assert_eq!(format!("{:?}", r.unwrap_err()), format!("InvalidSize"));
453+
454+
let r = unsafe { MmapRegion::build_raw(addr as *mut u8, size, prot, flags).unwrap() };
455+
456+
assert_eq!(r.size(), size);
457+
assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
458+
assert_eq!(r.flags(), libc::MAP_NORESERVE | libc::MAP_PRIVATE);
459+
assert!(!r.owned());
376460
}
377461

378462
#[test]

0 commit comments

Comments
 (0)