Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

Sewer56
Copy link

@Sewer56 Sewer56 commented Sep 8, 2023

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:

  • Validates that the data at the memory-mapped object's address matches what is expected, ensuring that the raw address mapping is correctly established.

  • Checks that permission changes performed from one Mmap object are reflected in the other Mmap object, validating that objects point to the same physical page.

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
Copy link
Owner

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:

  • Mmap::from_raw() should only be used on the original allocation pointer and size.
  • Mmap::into_raw() should probably fail for split Mmap objects on Microsoft Windows, i.e. return a Result.
  • Mmap::into_raw() needs to ensure that VirtualFree/munmap doesn't get called on the allocation.

@Sewer56
Copy link
Author

Sewer56 commented Sep 23, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants