From a49138adcb627c017570f8b344b6a7793fbff689 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Fri, 6 Mar 2026 07:21:49 +0100 Subject: [PATCH] Fix LD_PRELOAD namespace restriction using memfd The Android dynamic linker (Bionic) restricts loading libraries from unauthorized paths via LD_PRELOAD. This causes the linker to reject the hook library with a "not accessible for the namespace" fatal error. To bypass this restriction, we exploit a fallback in Bionic that explicitly skips the namespace accessibility check for files located on tmpfs. We use `memfd_create` to create an anonymous tmpfs-backed file descriptor, copy the library into it using `sendfile`, and pass the new memfd to LD_PRELOAD instead. Reference: function `load_library` in https://cs.android.com/android/platform/superproject/main/+/main:bionic/linker/linker.cpp --- dex2oat/src/main/cpp/dex2oat.cpp | 27 +++++++++++++++++++++++ magisk-loader/magisk_module/sepolicy.rule | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/dex2oat/src/main/cpp/dex2oat.cpp b/dex2oat/src/main/cpp/dex2oat.cpp index bf441c320..32719bea7 100644 --- a/dex2oat/src/main/cpp/dex2oat.cpp +++ b/dex2oat/src/main/cpp/dex2oat.cpp @@ -1,4 +1,8 @@ +#include +#include #include +#include +#include #include #include @@ -144,7 +148,29 @@ int main(int argc, char **argv) { if (hooker_fd == -1) { LOGE("failed to read liboat_hook.so"); + } else { + int mem_fd = syscall(__NR_memfd_create, "liboat_hook_memfd", 0); + if (mem_fd >= 0) { + // Get the exact size of the original library + LOGD("Copying %d as mem_fd %d", hooker_fd, mem_fd); + struct stat st; + if (fstat(hooker_fd, &st) == 0) { + // Tell the kernel to copy the entire file directly to the memfd + off_t offset = 0; + sendfile(mem_fd, hooker_fd, &offset, st.st_size); + + // Swap the old FD with the new memfd + close(hooker_fd); + hooker_fd = mem_fd; + } else { + PLOGE("fstat failed"); + close(mem_fd); + } + } else { + PLOGE("memfd_create failed, falling back to original fd"); + } } + LOGD("sock: %s stock_fd: %d", sock.sun_path + 1, stock_fd); // Prepare arguments for execve @@ -176,6 +202,7 @@ int main(int argc, char **argv) { // Set LD_PRELOAD to point to the hooker library FD std::string preload_val = "LD_PRELOAD=/proc/self/fd/" + std::to_string(hooker_fd); + LOGD("Inject oat hook via %s", preload_val.data()); setenv("LD_PRELOAD", ("/proc/self/fd/" + std::to_string(hooker_fd)).c_str(), 1); // Pass original argv[0] as DEX2OAT_CMD diff --git a/magisk-loader/magisk_module/sepolicy.rule b/magisk-loader/magisk_module/sepolicy.rule index d0b254e05..cc170e6b2 100644 --- a/magisk-loader/magisk_module/sepolicy.rule +++ b/magisk-loader/magisk_module/sepolicy.rule @@ -7,7 +7,7 @@ type xposed_file file_type typeattribute xposed_file mlstrustedobject allow {dex2oat installd isolated_app shell} xposed_file {file dir} * -allow dex2oat unlabeled file * +allow dex2oat {unlabeled tmpfs} file * type xposed_data file_type typeattribute xposed_data mlstrustedobject