Currently, this library uses a few abstractions which assume rwx abstractions exist. This model, however, is becoming increasingly less popular in embedded devices, with iOS and the Nintendo Switch both using doubly mapped memory for JIT, (one being rw- and another being either r-x or --x) rather than remapping existing memory, as this reduces the attack surface for ROP to escalate into arbitrary code execution. This approach is also becoming popular in other systems (another example being .NET 6 using this to harden their JIT against escalation of vulnerabilities)
In order to support more targets, it'd likely be better to abstract away the actual read/write accesses behind a Deref<Target = [u8]> implementation in order to allow target-specific access patterns.
My personal motivation here is possibly working with @Raytwo to add aarch64 support and hopefully eventually make use of it for providing a unified library for modding games on both PC and the Nintendo Switch. Since the Switch doesn't support rwx and has no direct equivalent to mmap*, these changes are necessary in order to make the library workable for our usecase.
*sorta
Implementation Proposal
From reading through the source I'm primarily seeing 2 things which rely on this assumption:
region::protect_with_handle - Being used to rwx map existing r-x memory temporarily in order to enable patching existing code
Rework to use a function for getting a RAII guard for accessing the memory as rw-. The RwGuard<'a> would implement Deref<Target = [u8]> in order to deref to the rw- mapping for the memory. For platforms continuing to use the region library, this would simply Deref to the rwx memory, for other platforms it would Deref to the second rw- mapping which would have different addresses but maps to the same memory. This guard would also implement Drop to handle any locking/unlocking/icache clearing that would need to be done.
impl<'a> Deref for RwGuard<'a> {
type Target = [u8];
}
impl<'a> DerefMut for RwGuard<'a> {}
2. `mmap` - Being used to rwx map entirely new memory to a specific address
* This one is easier, similar approach to the last one (return a struct which implements `Deref<Target=[u8]>`) but with less of a RAII guard style usage and more just "this is now a mapping that is owned by this type".
```rust
impl RwxMap {
fn new(addr: *const T, size: usize) -> Result<Self, Error>;
fn addr(&self) -> *const c_void;
// ...
}
This may require a .lock() method to return a RwGuard or something similar, in order to properly lock/unlock/protect/unprotect memory
Motivation
Currently, this library uses a few abstractions which assume rwx abstractions exist. This model, however, is becoming increasingly less popular in embedded devices, with iOS and the Nintendo Switch both using doubly mapped memory for JIT, (one being rw- and another being either r-x or --x) rather than remapping existing memory, as this reduces the attack surface for ROP to escalate into arbitrary code execution. This approach is also becoming popular in other systems (another example being .NET 6 using this to harden their JIT against escalation of vulnerabilities)
In order to support more targets, it'd likely be better to abstract away the actual read/write accesses behind a
Deref<Target = [u8]>
implementation in order to allow target-specific access patterns.My personal motivation here is possibly working with @Raytwo to add aarch64 support and hopefully eventually make use of it for providing a unified library for modding games on both PC and the Nintendo Switch. Since the Switch doesn't support rwx and has no direct equivalent to mmap*, these changes are necessary in order to make the library workable for our usecase.
*sorta
Implementation Proposal
From reading through the source I'm primarily seeing 2 things which rely on this assumption:
region::protect_with_handle
- Being used to rwx map existing r-x memory temporarily in order to enable patching existing coderw-
. TheRwGuard<'a>
would implementDeref<Target = [u8]>
in order to deref to therw-
mapping for the memory. For platforms continuing to use theregion
library, this would simplyDeref
to the rwx memory, for other platforms it would Deref to the secondrw-
mapping which would have different addresses but maps to the same memory. This guard would also implementDrop
to handle any locking/unlocking/icache clearing that would need to be done.impl<'a> Deref for RwGuard<'a> { type Target = [u8]; }
impl<'a> DerefMut for RwGuard<'a> {}
.lock()
method to return aRwGuard
or something similar, in order to properly lock/unlock/protect/unprotect memory