Problem
Android 10+ marks aarch64 system libraries (including libc) as Execute-Only Memory (XOM) — pages have PROT_EXEC but not PROT_READ. vproc's inline hooks need to:
- Read the function prologue to save original instructions → fails because
PROT_READ is not set
- Write the trampoline → needs
PROT_WRITE, but W^X policy prevents PROT_WRITE | PROT_EXEC simultaneously
Additionally, SELinux denies execmem permissions to untrusted app domains, blocking anonymous RWX mmap for trampoline memory allocation.
libc Path Changes
Android 10+ moved libc from /system/lib64/libc.so to /apex/com.android.runtime/lib64/bionic/libc.so, and linker namespaces block standard dlopen access.
Proposed Fix
1. XOM: Add PROT_READ before reading prologue
mprotect(page, page_size, PROT_READ | PROT_EXEC);
// read original instructions
// write trampoline
// flush I-cache
mprotect(page, page_size, PROT_EXEC); // restore XOM
2. W^X + SELinux: Alias mapping (dual mapping)
Map the same physical page at two virtual addresses with different permissions:
// Create anonymous mapping
let mem = mmap(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// Create alias via memfd_create + MAP_FIXED
let fd = memfd_create("vproc_trampoline", MFD_ALLOW_SEALING);
// Map writable alias
let writable = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// Map executable alias
let executable = mmap(nullptr, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
Write trampoline to the writable mapping, execute from the executable mapping. SELinux sees no PROT_WRITE | PROT_EXEC on any single mapping.
Reference: selinux-mem
3. libc Path Discovery
Use dlsym(RTLD_DEFAULT, "fork") to resolve the actual libc address at runtime rather than hardcoding paths.
Current Impact
vproc's inline hooks currently work in Termux (which has relaxed SELinux and no XOM on libc). They may fail on stock Android 10+ devices when loaded by a regular app (hermux on non-rooted devices).
Affected Code
src/vexec.rs — hook_libc_fork(), hook_libc_pipe(), hook_libc_wait() — all assume readable+executable code pages
References
- Android XOM — execute-only memory for system libraries
- selinux-mem — alias mapping technique
- ShadowHook — production Android inline hook library handling XOM/W^X
Problem
Android 10+ marks aarch64 system libraries (including libc) as Execute-Only Memory (XOM) — pages have
PROT_EXECbut notPROT_READ. vproc's inline hooks need to:PROT_READis not setPROT_WRITE, but W^X policy preventsPROT_WRITE | PROT_EXECsimultaneouslyAdditionally, SELinux denies
execmempermissions to untrusted app domains, blocking anonymous RWXmmapfor trampoline memory allocation.libc Path Changes
Android 10+ moved libc from
/system/lib64/libc.soto/apex/com.android.runtime/lib64/bionic/libc.so, and linker namespaces block standarddlopenaccess.Proposed Fix
1. XOM: Add
PROT_READbefore reading prologue2. W^X + SELinux: Alias mapping (dual mapping)
Map the same physical page at two virtual addresses with different permissions:
Write trampoline to the writable mapping, execute from the executable mapping. SELinux sees no
PROT_WRITE | PROT_EXECon any single mapping.Reference: selinux-mem
3. libc Path Discovery
Use
dlsym(RTLD_DEFAULT, "fork")to resolve the actual libc address at runtime rather than hardcoding paths.Current Impact
vproc's inline hooks currently work in Termux (which has relaxed SELinux and no XOM on libc). They may fail on stock Android 10+ devices when loaded by a regular app (hermux on non-rooted devices).
Affected Code
src/vexec.rs—hook_libc_fork(),hook_libc_pipe(),hook_libc_wait()— all assume readable+executable code pagesReferences