StephanvanSchaik / mmap-rs

A cross-platform and safe Rust API to create and manage memory mappings in the virtual address space of the calling process.
Apache License 2.0
61 stars 17 forks source link

Added: Unsafe API for Creating Mmap(s) from Existing Addresses #40

Open Sewer56 opened 1 year ago

Sewer56 commented 1 year ago

Added: Ability to create Mmap unsafely from an existing address.

This PR extends the functionality of our memory-mapping library by allowing Mmap objects to be created from an existing, raw memory address. This feature has been added through the new MmapOptions::map_from_existing API, which is unsafe and returns a platform-specific MmapMut object.

Lifetime Management

Memory-mapping objects created from raw addresses become 'owned' by the calling code.

This means that the lifetime of the mapped region is now managed by the MmapMut object, and dropping this object will cause the corresponding memory region to be unmapped.

This is documented alongside the newly added map_from_existing API.

Tests

A basic unit test was added. This test creates a new MmapMut object from an existing, unmanaged memory address and performs the following checks:

Use Case

I am in the process of porting a well known C# function hooking library to Rust (Work in Progress), while making it as portable as possible. (This means supporting non-x86 architectures and non-Windows OSes)

mmap-rs supports more 'edge-case' platforms such as FreeBSD, Android and iOS; so for those platforms where testing is more difficult, I would prefer to leverage existing code, even if I wind up wasting some CPU cycles on Mmap object initialization for some more temporary actions (like temp changing permissions).

Notes

Open to making any changes requested before merging; I'm still (relatively) new to Rust, so if I've missed anything here, please let me know.

StephanvanSchaik commented 1 year ago

Hi,

I have been trying to implement something similar in PR #14. I think it makes more sense to implement this as some sort of Mmap::from_raw() and Mmap::into_raw() API that bypasses using MmapOptions entirely, since there is no reason to specify the options (you already should have a pointer and a size).

The reason I haven't made much progress on this recently, is that Microsoft Windows requires you to call VirtualFree on the entire allocation that you originally received from VirtualAlloc. Since mmap-rs allows you to split and merge Mmap objects, that original allocation needs to be tracked with a reference counted object (see PR #29), otherwise VirtualFree could fail. However, this also means that for Mmap::from_raw() there is currently no way to retrieve that reference counted object (assuming there is one).

I think the constraints are:

Sewer56 commented 1 year ago

Aah, I see. I also now understand why SharedArea is around for the Windows implementation. I think the constraints are pretty solid and well founded here.

Another thing to note is that the from_raw and into_raw APIs might also want to return/accept implementation specific flags.

When I made this PR, I passed JIT flag by default to the underlying implementations, as it is not known/indeterminate if the given memory region originally was intended with executing in mind (this could be customizable though). This in turn prevented the library-side restriction of setting pages as executable.

Anyway, to provide some more context/information: In my specific use case at least, the intention was to work with not only maps created by mmap-rs specifically, but memory that was pre-mapped (such as binary code from [.exe/.elf etc.]); and mmap-rs would work as a fallback for platforms without specific implementations (which delegate to (VirtualProtect/mprotect/<protect>) etc.