ntdll: Map executable PE sections anonymously when W^X denies RX on Android#25
Open
utkarshdalal wants to merge 5 commits into
Open
ntdll: Map executable PE sections anonymously when W^X denies RX on Android#25utkarshdalal wants to merge 5 commits into
utkarshdalal wants to merge 5 commits into
Conversation
…ndroid. On Android, SELinux W^X enforcement (the execmod permission) refuses PROT_EXEC on a modified private file mapping. Wine maps PE sections MAP_PRIVATE file-backed and then relocates them, dirtying the COW pages, so the final mprotect to RX in map_image_into_view is denied with EACCES and the section is left non-executable. The first instruction fetch into such a section (observed on arm64ec ntdll/kernelbase/kernel32) then faults with SEGV_ACCERR. Add remap_exec_anon(), which privatizes the offending range into anonymous memory (which only needs execmem, not execmod) preserving the already relocated contents, sets the requested protection, and flushes the instruction cache. Wire it into both the normal per-section protection path and the ImageMappedFlat branch, updating the per-page vprot bookkeeping so Wine's view of the protection stays accurate. All changes are gated on __ANDROID__. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…roid. Android SELinux execmod denies PROT_EXEC on modified private file mappings (COW-dirtied by PE relocations). Detecting this via set_vprot() failure is unreliable because LD_PRELOAD shims strip PROT_EXEC and return success, hiding the kernel EACCES. Privatize executable sections into anonymous memory before set_vprot() so the subsequent mprotect(RX) targets anon memory and only requires execmem, which the app sandbox permits. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
remap_exec_anon's __builtin___clear_cache is gated on PROT_EXEC in the unix_prot argument. Passing PROT_READ|PROT_WRITE skipped the flush, which on aarch64 leaves stale icache for the freshly-copied bytes (mprotect on Linux/aarch64 does not invalidate icache). Pass the section's real unix prot via get_unix_prot(vprot) so the flush fires. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The W^X privatization only matters when PE bytes execute as native ARM hardware code -- i.e. arm64ec. x86_64 Wine on Android runs PE .text under FEX/box64, which only reads it (no PROT_EXEC at hardware level), so it doesn't hit execmod and shouldn't pay the page-cache-sharing cost. Compile-time gate (__ANDROID__ && __aarch64__) skips the helper entirely on x86_64 Android builds. Runtime gate (is_arm64ec()) skips the privatize on a hypothetical plain ARM64 Wine, since current_machine is a compile- time constant equal to ARM64 on aarch64 builds, leaving only the main_image_info.Machine check at runtime. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Android Wine build in this repo is always aarch64, so the compile- time __aarch64__ gate was noise. Runtime is_arm64ec() is the standard pattern used throughout ntdll (thread.c, loader.c, process.c, signal_arm64.c) and is sufficient on its own. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
On Android, executable PE sections (
.text) can end up non-executable to the CPU even though Wine believes it set them RX. The first instruction fetch into such a section — observed on arm64ec forntdll/kernelbase/kernel32— faults withSIGSEGV/SEGV_ACCERR.This adds
remap_exec_anon()and wires it intomap_image_into_view, so that when the RXmprotectis denied we privatize the section into anonymous memory and set it executable at load time.Why
Wine maps PE sections
MAP_PRIVATEfile-backed (VPROT_WRITECOPY) and then relocates them, which dirties the COW pages. Android's SELinux W^X enforcement — specifically theexecmodpermission ("make executable a modified private file mapping") — denies the subsequentmprotect(..., PROT_EXEC)withEACCES. The section is then left readable/writable but not executable.Anonymous memory is not subject to
execmod(it only needsexecmem, which is granted in this environment), so copying the already-relocated bytes into an anonymous mapping and setting RX there succeeds.How
remap_exec_anon()(new,__ANDROID__-only): set the range readable, stage its contents into a temporary anonymous mapping,MAP_FIXED-replace the range with anonymous private memory, copy the contents back, apply the requested protection, and__builtin___clear_cache()if executable.set_vprotfails to set RX on an executable section, callremap_exec_anonand, on success, update the per-page vprot bookkeeping viaset_page_vprotso Wine's recorded protection matches reality. OnlyERRs if the rebake itself fails.ImageMappedFlatbranch: same treatment for flat/native-subsystem images where the whole image is set RX in one call.Notes for reviewers
__ANDROID__; non-Android builds are unchanged.virtual_mutex, replacing the need for an external reactiveSIGSEGV-driven rebake (LD_PRELOAD shim).execmodand matches what the previous shim-based workaround already did.shared_fdshared-writable-exec section path;.textdoes not normally take it, but it could get the same helper if needed.🤖 Generated with Claude Code