@@ -28,6 +28,10 @@ use crate::volatile_memory::{self, compute_offset, VolatileMemory, VolatileSlice
28
28
pub enum Error {
29
29
/// The specified file offset and length cause overflow when added.
30
30
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 ,
31
35
/// The forbidden `MAP_FIXED` flag was specified.
32
36
MapFixed ,
33
37
/// Mappings using the same fd overlap in terms of file offset and length.
@@ -45,6 +49,14 @@ impl fmt::Display for Error {
45
49
f,
46
50
"The specified file offset and length cause overflow when added"
47
51
) ,
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
+ ) ,
48
60
Error :: MapFixed => write ! ( f, "The forbidden `MAP_FIXED` flag was specified" ) ,
49
61
Error :: MappingOverlap => write ! (
50
62
f,
@@ -79,6 +91,7 @@ pub struct MmapRegion {
79
91
file_offset : Option < FileOffset > ,
80
92
prot : i32 ,
81
93
flags : i32 ,
94
+ owned : bool ,
82
95
}
83
96
84
97
// Send and Sync aren't automatically inherited for the raw address pointer.
@@ -160,6 +173,48 @@ impl MmapRegion {
160
173
file_offset,
161
174
prot,
162
175
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 ,
163
218
} )
164
219
}
165
220
@@ -190,6 +245,11 @@ impl MmapRegion {
190
245
self . flags
191
246
}
192
247
248
+ /// Returns `true` if the mapping is owned by this `MmapRegion` instance.
249
+ pub fn owned ( & self ) -> bool {
250
+ self . owned
251
+ }
252
+
193
253
/// Checks whether this region and `other` are backed by overlapping
194
254
/// [`FileOffset`](struct.FileOffset.html) objects.
195
255
///
@@ -252,8 +312,10 @@ impl Drop for MmapRegion {
252
312
fn drop ( & mut self ) {
253
313
// This is safe because we mmap the area at addr ourselves, and nobody
254
314
// 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
+ }
257
319
}
258
320
}
259
321
}
@@ -373,6 +435,28 @@ mod tests {
373
435
assert_eq ! ( r. file_offset( ) . unwrap( ) . start( ) , offset as u64 ) ;
374
436
assert_eq ! ( r. prot( ) , libc:: PROT_READ | libc:: PROT_WRITE ) ;
375
437
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( ) ) ;
376
460
}
377
461
378
462
#[ test]
0 commit comments