mmap_rs_with_map_from_existing/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(
3    missing_docs,
4    rustdoc::broken_intra_doc_links,
5    missing_debug_implementations
6)]
7
8mod areas;
9pub mod error;
10mod mmap;
11mod os_impl;
12
13pub use areas::*;
14pub use error::Error;
15pub use mmap::*;
16
17#[cfg(test)]
18mod tests {
19    #[test]
20    fn reserve_none() {
21        use crate::{MemoryAreas, MmapNone, MmapOptions, Protection, ShareMode};
22
23        let mapping = MmapOptions::new(MmapOptions::page_size())
24            .unwrap()
25            .reserve_none()
26            .unwrap();
27
28        assert!(mapping.as_ptr() != std::ptr::null());
29
30        let mapping: MmapNone = mapping.try_into().unwrap();
31
32        assert!(mapping.as_ptr() != std::ptr::null());
33
34        let region = MemoryAreas::query(mapping.as_ptr() as usize)
35            .unwrap()
36            .unwrap();
37
38        assert_eq!(region.share_mode, ShareMode::Private);
39        assert!(!region.protection.contains(Protection::READ));
40        assert!(!region.protection.contains(Protection::WRITE));
41        assert!(!region.protection.contains(Protection::EXECUTE));
42    }
43
44    #[test]
45    fn reserve() {
46        use crate::{MemoryAreas, Mmap, MmapOptions, Protection, ShareMode};
47
48        let mapping = MmapOptions::new(MmapOptions::page_size())
49            .unwrap()
50            .map()
51            .unwrap();
52
53        assert!(mapping.as_ptr() != std::ptr::null());
54
55        let mapping: Mmap = mapping.try_into().unwrap();
56
57        assert!(mapping.as_ptr() != std::ptr::null());
58
59        let region = MemoryAreas::query(mapping.as_ptr() as usize)
60            .unwrap()
61            .unwrap();
62
63        assert_eq!(region.share_mode, ShareMode::Private);
64        assert!(region.protection.contains(Protection::READ));
65        assert!(!region.protection.contains(Protection::WRITE));
66        assert!(!region.protection.contains(Protection::EXECUTE));
67    }
68
69    #[test]
70    fn reserve_mut() {
71        use crate::{MemoryAreas, MmapMut, MmapOptions, Protection, ShareMode};
72
73        let mapping = MmapOptions::new(MmapOptions::page_size())
74            .unwrap()
75            .reserve_mut()
76            .unwrap();
77
78        assert!(mapping.as_ptr() != std::ptr::null());
79
80        let mut mapping: MmapMut = mapping.try_into().unwrap();
81
82        mapping[0] = 0x42;
83
84        assert!(mapping.as_ptr() != std::ptr::null());
85        assert_eq!(mapping[0], 0x42);
86
87        let region = MemoryAreas::query(mapping.as_ptr() as usize)
88            .unwrap()
89            .unwrap();
90
91        assert_eq!(region.share_mode, ShareMode::Private);
92        assert!(region.protection.contains(Protection::READ));
93        assert!(region.protection.contains(Protection::WRITE));
94        assert!(!region.protection.contains(Protection::EXECUTE));
95    }
96
97    #[test]
98    fn map_none() {
99        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
100
101        let mapping = MmapOptions::new(MmapOptions::page_size())
102            .unwrap()
103            .map_none()
104            .unwrap();
105
106        assert!(mapping.as_ptr() != std::ptr::null());
107
108        let region = MemoryAreas::query(mapping.as_ptr() as usize)
109            .unwrap()
110            .unwrap();
111
112        assert_eq!(region.share_mode, ShareMode::Private);
113        assert!(!region.protection.contains(Protection::READ));
114        assert!(!region.protection.contains(Protection::WRITE));
115        assert!(!region.protection.contains(Protection::EXECUTE));
116    }
117
118    #[test]
119    fn map() {
120        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
121
122        let mapping = MmapOptions::new(MmapOptions::page_size())
123            .unwrap()
124            .map()
125            .unwrap();
126
127        assert!(mapping.as_ptr() != std::ptr::null());
128
129        let region = MemoryAreas::query(mapping.as_ptr() as usize)
130            .unwrap()
131            .unwrap();
132
133        assert_eq!(region.share_mode, ShareMode::Private);
134        assert!(region.protection.contains(Protection::READ));
135        assert!(!region.protection.contains(Protection::WRITE));
136        assert!(!region.protection.contains(Protection::EXECUTE));
137    }
138
139    #[test]
140    fn map_existing() {
141        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
142
143        const PRETEND_CODE: u8 = 0xC3; // ret in x86, but we're just pretending.
144
145        // Map a page.
146        let mut mapping = MmapOptions::new(MmapOptions::page_size())
147            .unwrap()
148            .map_mut()
149            .unwrap();
150
151        assert!(mapping.as_ptr() != std::ptr::null());
152
153        // Pretend we are a JIT and write some code. 
154        unsafe { *mapping.as_mut_ptr() = PRETEND_CODE }; 
155
156        // Verify the origiinal mapping exists as RW (non-execute)
157        let region = MemoryAreas::query(mapping.as_ptr() as usize)
158            .unwrap()
159            .unwrap();
160
161        assert_eq!(region.share_mode, ShareMode::Private);
162        assert!(region.protection.contains(Protection::READ));
163        assert!(region.protection.contains(Protection::WRITE));
164        assert!(!region.protection.contains(Protection::EXECUTE));
165
166        // Re-acquire the mapping.
167        let remapped = unsafe {
168            MmapOptions::new(MmapOptions::page_size())
169                .unwrap()
170                .with_address(mapping.as_ptr() as usize)
171                .map_from_existing()
172                .unwrap()
173        };
174
175        // Verify our executable data from the old mapping is here.
176        unsafe { assert_eq!(PRETEND_CODE, *remapped.as_ptr()) };
177
178        // Then change mapping behaviour to executable.
179        let _unused = remapped.make_exec();
180
181        let region = MemoryAreas::query(mapping.as_ptr() as usize)
182            .unwrap()
183            .unwrap();
184
185        assert_eq!(region.share_mode, ShareMode::Private);
186        assert!(region.protection.contains(Protection::READ));
187        assert!(!region.protection.contains(Protection::WRITE)); // now enabled
188        assert!(region.protection.contains(Protection::EXECUTE));
189    }
190
191    #[test]
192    fn map_mut() {
193        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
194
195        let mut mapping = MmapOptions::new(MmapOptions::page_size())
196            .unwrap()
197            .map_mut()
198            .unwrap();
199
200        mapping[0] = 0x42;
201
202        assert!(mapping.as_ptr() != std::ptr::null());
203        assert_eq!(mapping[0], 0x42);
204
205        let region = MemoryAreas::query(mapping.as_ptr() as usize)
206            .unwrap()
207            .unwrap();
208
209        assert_eq!(region.share_mode, ShareMode::Private);
210        assert!(region.protection.contains(Protection::READ));
211        assert!(region.protection.contains(Protection::WRITE));
212        assert!(!region.protection.contains(Protection::EXECUTE));
213    }
214
215    #[test]
216    fn map_file() {
217        use crate::{MemoryAreas, MmapFlags, MmapOptions, Protection, ShareMode};
218        use std::io::Write;
219        use tempfile::NamedTempFile;
220
221        let mut file = NamedTempFile::new().unwrap();
222
223        let mut bytes = vec![0u8; MmapOptions::page_size()];
224        bytes[0] = 0x42;
225        file.as_file_mut().write(&bytes).unwrap();
226
227        let mapping = unsafe {
228            MmapOptions::new(MmapOptions::page_size())
229                .unwrap()
230                .with_flags(MmapFlags::SHARED)
231                .with_file(file.as_file(), 0)
232                .map()
233                .unwrap()
234        };
235
236        let other_mapping = unsafe {
237            MmapOptions::new(MmapOptions::page_size())
238                .unwrap()
239                .with_flags(MmapFlags::SHARED)
240                .with_file(file.as_file(), 0)
241                .map()
242                .unwrap()
243        };
244
245        assert_ne!(mapping.as_ptr(), std::ptr::null());
246        assert_ne!(other_mapping.as_ptr(), std::ptr::null());
247        assert_eq!(mapping[0], 0x42);
248        assert_eq!(other_mapping[0], 0x42);
249
250        let region = MemoryAreas::query(mapping.as_ptr() as usize)
251            .unwrap()
252            .unwrap();
253
254        assert_eq!(region.share_mode, ShareMode::Shared);
255        assert!(region.protection.contains(Protection::READ));
256        assert!(!region.protection.contains(Protection::WRITE));
257        assert!(!region.protection.contains(Protection::EXECUTE));
258        #[cfg(not(target_os = "freebsd"))]
259        assert!(region.path().is_some());
260    }
261
262    #[test]
263    fn map_file_mut() {
264        use crate::{MemoryAreas, MmapFlags, MmapOptions, Protection, ShareMode};
265        use std::io::{Read, Seek, SeekFrom, Write};
266        use tempfile::NamedTempFile;
267
268        let mut file = NamedTempFile::new().unwrap();
269
270        let mut bytes = vec![0u8; MmapOptions::page_size()];
271        file.as_file_mut().write(&bytes).unwrap();
272
273        let mut mapping = unsafe {
274            MmapOptions::new(MmapOptions::page_size())
275                .unwrap()
276                .with_flags(MmapFlags::SHARED)
277                .with_file(file.as_file(), 0)
278                .map_mut()
279                .unwrap()
280        };
281
282        let other_mapping = unsafe {
283            MmapOptions::new(MmapOptions::page_size())
284                .unwrap()
285                .with_flags(MmapFlags::SHARED)
286                .with_file(file.as_file(), 0)
287                .map()
288                .unwrap()
289        };
290
291        assert_ne!(mapping.as_ptr(), std::ptr::null());
292        assert_ne!(other_mapping.as_ptr(), std::ptr::null());
293        mapping[0] = 0x42;
294        assert_eq!(other_mapping[0], 0x42);
295        mapping.flush(0..MmapOptions::page_size()).unwrap();
296        file.as_file_mut().sync_all().unwrap();
297
298        let region = MemoryAreas::query(mapping.as_ptr() as usize)
299            .unwrap()
300            .unwrap();
301
302        assert_eq!(region.share_mode, ShareMode::Shared);
303        assert!(region.protection.contains(Protection::READ));
304        assert!(region.protection.contains(Protection::WRITE));
305        assert!(!region.protection.contains(Protection::EXECUTE));
306        #[cfg(not(target_os = "freebsd"))]
307        assert!(region.path().is_some());
308
309        file.as_file_mut().seek(SeekFrom::Start(0)).unwrap();
310        file.as_file().read(&mut bytes).unwrap();
311        assert_eq!(bytes[0], 0x42);
312    }
313
314    #[test]
315    fn map_file_private() {
316        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
317        use std::io::{Read, Seek, SeekFrom, Write};
318        use tempfile::NamedTempFile;
319
320        let mut file = NamedTempFile::new().unwrap();
321
322        let mut bytes = vec![0u8; MmapOptions::page_size()];
323        file.as_file_mut().write(&bytes).unwrap();
324
325        let mut mapping = unsafe {
326            MmapOptions::new(MmapOptions::page_size())
327                .unwrap()
328                .with_file(file.as_file(), 0)
329                .map_mut()
330                .unwrap()
331        };
332
333        let other_mapping = unsafe {
334            MmapOptions::new(MmapOptions::page_size())
335                .unwrap()
336                .with_file(file.as_file(), 0)
337                .map()
338                .unwrap()
339        };
340
341        assert_ne!(mapping.as_ptr(), std::ptr::null());
342        assert_ne!(other_mapping.as_ptr(), std::ptr::null());
343        mapping[0] = 0x42;
344        assert_ne!(other_mapping[0], 0x42);
345        mapping.flush(0..MmapOptions::page_size()).unwrap();
346        file.as_file_mut().sync_all().unwrap();
347
348        let region = MemoryAreas::query(mapping.as_ptr() as usize)
349            .unwrap()
350            .unwrap();
351
352        assert_eq!(region.share_mode, ShareMode::Private);
353        assert!(region.protection.contains(Protection::READ));
354        assert!(region.protection.contains(Protection::WRITE));
355        assert!(!region.protection.contains(Protection::EXECUTE));
356        #[cfg(not(target_os = "freebsd"))]
357        assert!(region.path().is_some());
358
359        file.as_file_mut().seek(SeekFrom::Start(0)).unwrap();
360        file.as_file().read(&mut bytes).unwrap();
361        assert_ne!(bytes[0], 0x42);
362    }
363
364    #[test]
365    fn reserve_and_split() {
366        use crate::{MmapMut, MmapOptions};
367
368        let mut mapping = MmapOptions::new(2 * MmapOptions::page_size())
369            .unwrap()
370            .reserve_mut()
371            .unwrap();
372
373        let rest = mapping.split_off(MmapOptions::page_size()).unwrap();
374
375        assert!(mapping.as_ptr() < rest.as_ptr());
376
377        let mut rest: MmapMut = rest.try_into().unwrap();
378
379        rest[0] = 0x42;
380        assert_eq!(rest[0], 0x42);
381
382        assert_eq!(mapping.len(), MmapOptions::page_size());
383        assert_eq!(rest.len(), MmapOptions::page_size());
384        assert!(mapping.as_ptr() < rest.as_ptr());
385    }
386
387    #[test]
388    fn split_off() {
389        use crate::MmapOptions;
390
391        let mut mapping = MmapOptions::new(2 * MmapOptions::page_size())
392            .unwrap()
393            .map_mut()
394            .unwrap();
395
396        assert!(mapping.split_off(1).is_err());
397
398        mapping[0] = 0x1;
399        mapping[MmapOptions::page_size()] = 0x2;
400
401        let rest = mapping.split_off(MmapOptions::page_size()).unwrap();
402
403        assert_eq!(mapping[0], 0x1);
404        assert_eq!(rest[0], 0x2);
405        assert_eq!(mapping.len(), MmapOptions::page_size());
406        assert_eq!(rest.len(), MmapOptions::page_size());
407        assert!(mapping.as_ptr() < rest.as_ptr());
408    }
409
410    #[test]
411    fn split_to() {
412        use crate::MmapOptions;
413
414        let mut mapping = MmapOptions::new(2 * MmapOptions::page_size())
415            .unwrap()
416            .map_mut()
417            .unwrap();
418
419        assert!(mapping.split_to(1).is_err());
420
421        mapping[0] = 0x1;
422        mapping[MmapOptions::page_size()] = 0x2;
423
424        let rest = mapping.split_to(MmapOptions::page_size()).unwrap();
425
426        assert_eq!(mapping[0], 0x2);
427        assert_eq!(rest[0], 0x1);
428        assert_eq!(mapping.len(), MmapOptions::page_size());
429        assert_eq!(rest.len(), MmapOptions::page_size());
430        assert!(mapping.as_ptr() > rest.as_ptr());
431    }
432
433    #[test]
434    fn split_file() {
435        use crate::MmapOptions;
436        use std::io::Write;
437        use tempfile::NamedTempFile;
438
439        let mut file = NamedTempFile::new().unwrap();
440
441        let mut bytes = vec![0u8; 2 * MmapOptions::page_size()];
442        bytes[0] = 0x1;
443        bytes[MmapOptions::page_size()] = 0x2;
444        file.as_file_mut().write(&bytes).unwrap();
445
446        let mut mapping = unsafe {
447            MmapOptions::new(2 * MmapOptions::page_size())
448                .unwrap()
449                .with_file(file.as_file(), 0)
450                .map()
451                .unwrap()
452        };
453
454        assert!(mapping.split_off(1).is_err());
455
456        let rest = mapping.split_off(MmapOptions::page_size()).unwrap();
457
458        assert_eq!(mapping[0], 0x1);
459        assert_eq!(rest[0], 0x2);
460        assert_eq!(mapping.len(), MmapOptions::page_size());
461        assert_eq!(rest.len(), MmapOptions::page_size());
462        assert!(mapping.as_ptr() < rest.as_ptr());
463    }
464
465    #[test]
466    fn split_and_merge() {
467        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
468
469        // Allocate three pages.
470        let mut left = MmapOptions::new(3 * MmapOptions::page_size())
471            .unwrap()
472            .map_mut()
473            .unwrap();
474
475        // Split into left, middle and right.
476        let mut middle = left.split_off(MmapOptions::page_size()).unwrap();
477        let right = middle.split_off(MmapOptions::page_size()).unwrap();
478
479        assert!(left.as_ptr() < middle.as_ptr());
480        assert!(middle.as_ptr() < right.as_ptr());
481
482        // Merging left and right should fail in either order.
483        let Err((_, mut right)) = left.merge(right) else { panic!("expected merge to fail") };
484        let Err((_, left)) = right.merge(left) else { panic!("expected merge to fail") };
485
486        // Merging left and middle should succeed.
487        let Err((_, mut left)) = middle.merge(left) else { panic!("expected merge to fail") };
488        left.merge(middle).unwrap();
489
490        // Merging left and right should succeed.
491        let Err((_, mut left)) = right.merge(left) else { panic!("expected merge to fail") };
492        left.merge(right).unwrap();
493
494        // Ensure the size is correct.
495        assert_eq!(left.size(), 3 * MmapOptions::page_size());
496
497        let region = MemoryAreas::query(left.as_ptr() as usize).unwrap().unwrap();
498
499        assert_eq!(region.share_mode, ShareMode::Private);
500        assert!(region.protection.contains(Protection::READ));
501        assert!(region.protection.contains(Protection::WRITE));
502        assert!(!region.protection.contains(Protection::EXECUTE));
503
504        // Ensure that the region we got has the same start address or a lower one.
505        assert!(region.range.start <= left.start());
506
507        // Calculate the actual size from the start address.
508        let padding = left.start().saturating_sub(region.range.start);
509        let size = region.range.len().saturating_sub(padding);
510
511        // Ensure that the region is at least large enough to fully cover our memory mapping.
512        assert!(size >= 3 * MmapOptions::page_size());
513    }
514
515    #[test]
516    fn query_range() {
517        use crate::{MemoryAreas, MmapOptions};
518
519        // Allocate three pages.
520        let mut left = MmapOptions::new(3 * MmapOptions::page_size())
521            .unwrap()
522            .map_mut()
523            .unwrap();
524
525        // Split into left, middle and right.
526        let mut middle = left.split_off(MmapOptions::page_size()).unwrap();
527        let right = middle.split_off(MmapOptions::page_size()).unwrap();
528
529        assert!(left.as_ptr() < middle.as_ptr());
530        assert!(middle.as_ptr() < right.as_ptr());
531
532        // Drop the middle page.
533        drop(middle);
534
535        // Query the range, which should yield two memory regions.
536        let start = left.as_ptr() as usize;
537        let end = right.as_ptr() as usize + right.len();
538
539        let mut areas = MemoryAreas::query_range(start..end).unwrap();
540
541        let region = areas.next().unwrap().unwrap();
542        assert_eq!(
543            region.end(),
544            left.as_ptr() as usize + MmapOptions::page_size()
545        );
546        let mut region = areas.next().unwrap().unwrap();
547
548        if region.start() != right.as_ptr() as usize {
549            region = areas.next().unwrap().unwrap();
550        }
551
552        assert_eq!(region.start(), right.as_ptr() as usize);
553        assert!(areas.next().is_none());
554    }
555}