Skip to content

Fix SELinux enforcement detection and improve exploit reliability - #182

Open
hexlo404 wants to merge 7 commits into
diabl0w:masterfrom
hexlo404:master
Open

hexlo404 wants to merge 7 commits into
diabl0w:masterfrom
hexlo404:master

Conversation

@hexlo404

@hexlo404 hexlo404 commented Nov 3, 2026

Copy link
Copy Markdown

Summary

This PR collects seven fixes for the LKM and the exploit code. The main change corrects the SELinux enforcement detection in lkm/dfroot.c: the existing selinux_state readback can make the module treat an enforcing system as permissive, which skips the avc_has_perm fallback patches exactly on the kernels that need them. The remaining commits harden error handling in the native code, fix two assembly macros, close an IpSec resource leak in the app, and make the LKM Makefile tolerate an unset OBJCOPY.

Changes

SELinux enforcement detection (lkm/dfroot.c)

The module writes false to the first member of struct selinux_state and reads the value back to decide whether the avc_has_perm patches can be skipped. That member is enforcing only when CONFIG_SECURITY_SELINUX_DEVELOP is enabled. On kernels built without it the write lands on an unrelated bool such as checkreqprot and changes nothing, while the readback still returns false, so the module concludes SELinux is permissive and skips the patches even though the system is still enforcing. On such devices the rooting flow then fails through plain AVC denials.

The module now verifies the real state through the selinuxfs node /sys/fs/selinux/enforce, read with filp_open, kernel_read, and filp_close resolved through kallsyms. The patches are skipped only when that node reads back 0. If any of the symbols is missing or the read fails, the module assumes enforcing and keeps the fallback patches, so the failure mode stays conservative. This keeps the intent of #172 — do not leave avc_has_perm patched while the system enforces (for example after a SU manager restores enforce=1) — but bases the decision on the state SELinux actually reports instead of a struct layout assumption.

XFRM sender port validation (app/src/main/jni/exp.c)

patch_file_cbc() only logged a failed bind() of the sender socket and kept going. The xfrm state matches on that source port, so with the bind missing every encapsulated write was silently dropped and the CBC patching failed without a meaningful error, which could be mistaken for a device that is not vulnerable. A failed bind now closes the socket and aborts that path with an error. setup() also rejects out-of-range port arguments up front.

Process handling and property parsing (app/src/main/jni/exp.c)

  • spawn_crash_dump() now checks the waitpid() result; on failure it returns an error instead of reporting an exit status of 0.
  • The getprop fallback in read_device_versions() checks the popen() and fgets() results, strips a trailing newline from the property value, and rejects empty values instead of parsing them into a bogus release string. The release string is assembled with snprintf() into an adequately sized buffer instead of sprintf() concatenation on a fixed 15-byte buffer.

ELF parser reliability (app/src/main/jni/elf_parser.c)

Adds the missing <stdint.h> include and keeps the read() return value when fetching section and symbol names. The name buffers are terminated at the number of bytes actually read (name[n] = 0) instead of at the last byte of the buffer, so a short read no longer leaves uninitialized stack bytes between the data and the terminator.

Assembly stack macros (app/src/main/jni/include.inc)

  • push used str \Rn, [sp, -16], which stores below the stack pointer without moving it, so consecutive pushes overwrite the same slot. It now uses the pre-indexed writeback form str \Rn, [sp, #-16]!.
  • pop referenced \Rn although the macro parameter is named R, and the post-index offset was missing the # prefix. It is now ldr \R, [sp], #16, which pairs correctly with the fixed push.

IpSec resource management (app/src/main/java/df/root/ExploitRunner.java)

If buildTransportModeTransform() failed, the already allocated SecurityParameterIndex and the UDP encapsulation socket leaked: they were only released around the native call, never around the setup that could throw in between. Every resource is now released in its own finally block, so a transform setup failure still closes the SPI and the encapsulation socket.

LKM Makefile (lkm/Makefile)

The objcopy invocation is now wrapped in ifdef OBJCOPY. With OBJCOPY empty the recipe previously produced a command line starting with --strip-unneeded, which the shell cannot execute, aborting the build with Error 127. With OBJCOPY configured the behavior is unchanged; without it the strip step is simply skipped instead of failing the build.

Testing

GitHub Actions

The Build workflow ran on 7886c19, the exact head of this branch:

https://github.com/hexlo404/DFRoot/actions/runs/41679234552

Result: success. All eight KMI variants built (android12-5.10, android13-5.10, android13-5.15, android14-5.15, android14-6.1, android15-6.6, android16-6.12, android17-6.18), and the APK job completed its 14-step sequence (the GitHub Release step only runs on tag pushes and was skipped as designed), producing the signed release APK. The run generated nine artifacts in total: the eight dfroot.ko variants and the APK.

Device test

I manually tested this on a physical Samsung Galaxy S23 running Android 14 (kernel 5.15.94, android13-5.15 KMI): the tool ran through, reported success, and completed its expected operation. This is a single-device manual test on real hardware, not a CI result, and I am not generalizing it to other Samsung models.

Impact

  • Correctness: the SELinux state decision no longer depends on the struct selinux_state layout; it reflects what selinuxfs reports.
  • Reliability: failed port binds, failed reads, and failed child-process waits now stop the affected path with a clear error instead of failing later in misleading ways.
  • Robustness: short reads in the ELF parser and newline-terminated getprop values are handled.
  • Resource handling: IpSec resources are released on all failure paths.
  • Buildability: the LKM Makefile no longer aborts with Error 127 when OBJCOPY is unset.

@diabl0w

diabl0w commented Nov 6, 2026

Copy link
Copy Markdown
Owner

only the first commit is relevant here right?

@hexlo404

hexlo404 commented Nov 6, 2026

Copy link
Copy Markdown
Author

only the first commit is relevant here right?

Hi @diabl0w, thanks for reviewing!

Actually, all 7 commits are relevant and intentionally included here. While the first commit (the SELinux enforcement fix in lkm/dfroot.c) is the core change, the remaining six commits are targeted fixes for other reliability issues found in the native code, assembly macros, app resource management, and the Makefile.

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