Skip to content

fix: production-grade PAC + I/O-aware scheduling (fixes #28 #29)#30

Merged
Bahtya merged 35 commits into
mainfrom
fix/pac-io-scheduling
May 24, 2026
Merged

fix: production-grade PAC + I/O-aware scheduling (fixes #28 #29)#30
Bahtya merged 35 commits into
mainfrom
fix/pac-io-scheduling

Conversation

@Bahtya
Copy link
Copy Markdown
Owner

@Bahtya Bahtya commented May 22, 2026

Summary

  • PAC: Replace xpaclri (restore-time band-aid) with xpaci x11 (save-time PAC stripping) in minicoro ARM64 context switch, matching Boost.Context's approach
  • I/O scheduling: Go netpoller-style cooperative I/O — yield_for_io + batch poll() replaces blocking real-fd reads/writes
  • Crash handler: Add SIGILL/SIGABRT (PAC faults send SIGILL, not SIGSEGV), replace libc::syscall(64,...) with libc::write() for seccomp safety
  • bash SIGSEGV fix: Both first-load and cached binary paths now use _start (via spawn_elf) instead of direct main() call. Skipping __libc_init caused TLS/stack canary uninitialized state → heap corruption in initialize_shell_variables where envp[i] contained MTE-tagged .text addresses
  • Cleanup: Remove unused sigsetjmp/siglongjmp probe code, fix all compiler warnings

Root causes addressed

Issue Root cause Fix
PAC SIGILL ARMv8.3 PAC + stale lr signature on context restore xpaci x11 at save time
I/O blocking hang Real fd read/write/poll transparent to kernel yield_for_io + batch poll + interceptors
SIGILL not caught Crash handler only covered SIGSEGV/SIGBUS Added SIGILL + SIGABRT
Seccomp SIGKILL libc::syscall(64,...) intercepted libc::write() (bionic wrapper)
bash SIGSEGV Cached path called main() directly, skipping __libc_init → TLS uninitialized → heap corruption Both paths use _start via spawn_elf

ART on-device test results (vivo V2419A, Cortex-A710/A715/X3, Android 16)

15 passed, 2 failed (non-crash)

bash -c echo hello                                    PASS
bash -c true (minimal init)                           PASS
bash -c echo (large envp, 40+ vars)                   PASS
bash -c sh echo; true (fork subprocess)                PASS
bash -c 'echo hello_pipe | cat'                       PASS
3 sequential bash -c sessions                          PASS
sh -c echo hello                                      PASS
sh -c exit 42                                         PASS
sh -c 'echo hello | cat'                              PASS
sh -c echo $(echo nested)                             PASS
sh -c for loop                                        PASS
sh -c VAR=x; echo $VAR                                PASS
3 sequential sh sessions                               PASS
sh echo (hermux flow)                                 FAIL (exit=-120 timeout)
sh 100 chars through PTY                              FAIL (buffer issue)

Fixes #28, fixes #29

Test plan

  • cargo build --release — zero errors, zero new warnings
  • cargo test — 46/46 passing
  • Device test on vivo V2419A (PAC + MTE + Android 16) — 15/17 pass, no crashes

Bahtya

Bahtya added 30 commits May 22, 2026 15:53
- Fix FFI signature mismatch: C bridge now includes session_id param
- Add session management (create/destroy) to JNI bridge
- Fix ART symbol visibility: dlopen(path, RTLD_GLOBAL) for RTLD_DEFAULT
- Skip self-test under ART/MTE to avoid siglongjmp MutexGuard deadlock
- Make dup failure non-fatal in driver thread
- Add crash handler thread-safety (pthread_exit for non-test threads)
- Update ART test infrastructure (HTTP server, Makefile, build)

Bahtya
Match Hermux's termux.c flow for issue #29 crash reproduction:
- Use 6-arg FFI function pointers (no session_id), matching Hermux
- Replace openpty() with manual /dev/ptmx PTY setup
- Match Hermux's signal handler (re-raise for tombstone)
- Add createSubprocess + waitFor JNI methods
- Use Hermux shell paths and InputReader/TermSessionWaiter threads
- Add runHermuxSession() with persistent thread model

Bahtya
- ELF stacks: alloc() → mmap + PROT_MTE + guard page
- mprotect: preserve PROT_MTE on GOT patching, segment restore, inline hooks
- PAC: xpaclri before siglongjmp in ffi.rs probe and JNI bridge recovery handler
- Coroutine Drop: munmap (guard + stack) instead of dealloc

Issue #29
The C bridge calls vproc_ffi_create_process with 6 args (no session_id)
but the Rust function expects 7 args (session_id first). On aarch64 this
shifts all parameters — c_path becomes session_id, argv becomes path,
stdin_fd becomes envp. Dereferencing stdin_fd as a pointer causes SIGSEGV.

Add vproc_ffi_create_process_default, run_until_exit_default, and
vpid_exists_default (6-arg, 1-arg, 1-arg) that auto-create a global
default session. Update the C bridge to dlsym these compat symbols.

Also fix mprotect_mte_aware to not add PROT_MTE to executable pages —
code pages from dlopen don't have MTE tags, and mprotect with PROT_MTE
returns EINVAL, causing inline hooks (exit/execve) to silently fail.

Bahtya
…GKILL

On Android 16 untrusted_app with MTE async enabled, adding PROT_MTE
to mmap/mprotect causes the kernel to check memory tags on access.
Since vproc's virtualized ELF environment doesn't properly tag memory,
this triggers async SIGKILL. Disable PROT_MTE entirely for now.

Also disable raw_log in vproc_jni_bridge.c to avoid any potential
seccomp/MTE issues from stderr writes.

Bahtya
…iagnostics

All raw syscall(__NR_write,...) calls blocked by Android seccomp on
untrusted_app. Replace with libc::write() wrapper. Skip diagnostics
in test to avoid creating orphan vpid that blocks the driver thread.

Bahtya
Add Android logcat output to key JNI functions to diagnose where
createSubprocess hangs on device.

Bahtya
Avoid liblog dependency. Use write(2) to stderr which is redirected
to logcat by ArtTestActivity.

Bahtya
Avoid link-time liblog dependency by dynamically loading via dlsym.

Bahtya
PAC (Pointer Authentication) on ARMv8.3+ devices caused SIGILL during
coroutine context switch. The minimal xpaclri fix is replaced with xpaci
at save time, matching Boost.Context's approach. Real fd I/O that blocked
the driver thread is now cooperative via yield_for_io + batch poll.

Fixes #28, fixes #29.

- coro/minicoro.c: xpaci x11 strips PAC from saved lr at context save;
  .arch_extension pauth for assembler; runtime getauxval(AT_HWCAP) check
- src/coroutine.rs: IoWait struct + io_wait field for I/O-waiting coroutines
- src/executor.rs: yield_for_io/collect_io_waits/wake_io_ready functions
- src/preload.rs: non-blocking read/write via poll-before-read; poll()
  interceptor (virtual pipes + real fds); select() interceptor; SIGILL +
  SIGABRT added to crash handler; libc::syscall(64,...) → libc::write()
- src/vexec.rs: GOT patch for poll and select
- src/ffi.rs: remove unused sigsetjmp/siglongjmp probe code (24 lines)
- examples: clean up unused unsafe/variable warnings

Bahtya
The cached (second invocation) path was calling main() directly, skipping
__libc_init which sets up TLS/stack canary state. This caused heap corruption
in bash's initialize_shell_variables, where envp[i] contained MTE-tagged
.text addresses instead of valid env strings.

Both first-load and cached paths now use spawn_elf (_start → __libc_init →
main), which properly reinitializes libc state for each invocation.

ART test results: 15/17 pass (bash all PASS, sh 2 non-crash failures).

Bahtya
Bahtya added 3 commits May 23, 2026 23:49
… pass

- close() fallback uses real_close() to clear fdsan tags before close
- vpid_exists and run_until_exit check exit_codes HashMap for reaped procs
- Remove diag_log! macro — was writing to fd 2 which leaked into PTY output
- Make exit_codes pub(crate) on Executor
- Stress/concurrency tests converted to hermux flow (separate sessions)
- All 31 ART tests pass, zero crashes

Bahtya
…x 1259→1028ms

- Driver idle loop: nanosleep(10ms) → condvar.wait() (untimed, no timer slack)
- Fix deadlock: create_process now notifies Session.wake (was only notifying
  spawn_queue condvar which driver doesn't wait on)
- prctl(PR_SET_TIMERSLACK, 50µs) in create_process, run_until_exit, cp_thread,
  and JNI poll thread — eliminates Android 40ms background timer slack
- JNI vpid poll interval: 250ms → 10ms (with prctl, actual ~10ms not ~300ms)
- Unified signing keystore (debug.keystore at project root, survives clean)

Results: 31/31 pass, 20 rapid bash 10647→1034ms (-90%), no heavy init regression

Bahtya
Android scheduler restores background threads after condvar wake with
~50ms latency, worse than 10ms poll with prctl-reduced timer slack.
Keep polling for create_process and run_until_exit result waits.

Bahtya
Copy link
Copy Markdown
Owner Author

@Bahtya Bahtya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #30 Code Review: Production-grade PAC handling + I/O-aware scheduling

Overview

This PR fixes 5 distinct crash/hang issues on Android ARM64:

  1. PAC SIGILL — strip PAC at save time (not restore) in minicoro context switch
  2. I/O blocking hang — replace blocking real-fd I/O with cooperative poll-yield loops
  3. Missing signal handlers — add SIGILL/SIGABRT handlers for PAC faults
  4. Seccomp SIGKILL — replace libc::syscall(64,...) with libc::write()
  5. bash SIGSEGV — cached binary path now uses _start instead of direct main() call

Plus extensive test infrastructure (Hermux flow tests, stress tests, timing tests) and build system improvements.


What's done well

  • Root cause analysis is excellent. The PAC save-time stripping approach (xpaci x11) matches Boost.Context's approach and is safe on non-PAC hardware (NOP instruction).
  • I/O scheduling is well-designed. The yield_for_io + batch poll() + wake_io_ready pattern cleanly models Go's netpoller.
  • fdsan handling. fdsan_clear_tag is necessary and correctly implemented for the dlopen-based virtual exec environment.
  • AT_RANDOM fix. Using host process's random bytes instead of hardcoded 0xDEADBEEF... is critical for proper TLS/stack canary initialization.
  • Test coverage expanded significantly. Hermux flow tests, stress tests, and timing tests provide realistic coverage.

Issues & Suggestions

Critical — Correctness

1. select() fd_set manipulation assumes specific layout
src/preload.rs ~lines 1130-1142

const FD_SETSIZE: c_int = 1024;
unsafe fn fd_isset(fd: c_int, set: *const libc::fd_set) -> bool {
    let arr = set as *const [u32; 32];
    (*arr)[idx] & mask != 0
}

This assumes fd_set is u32[32] (Linux kernel layout). While Android/Bionic uses this layout, it's fragile. Also missing bounds check for fd >= FD_SETSIZE — fds above 1024 will cause out-of-bounds access.

2. poll() timeout loop may spin indefinitely
src/preload.rspoll() interceptor

When timeout == -1 (infinite) and no fds are ready, the coroutine yields via yield_for_io, but on resume immediately retries poll(timeout=0). If the real fd never becomes ready, this loops forever burning CPU with yield_for_io → resume → poll(0) → yield. The driver's batch poll(50ms) should eventually unblock, but the busy-loop is a concern for slow I/O scenarios.

3. wake_io_ready uses linear search for dedup
src/executor.rs:365-374

if !ready_pids.contains(&pid) {
    ready_pids.push(pid);
}
// and:
if !ex.ready_queue.contains(&pid) {
    ex.ready_queue.push_back(pid);
}

Both are O(n²). Use a HashSet<VPid> for dedup. Fine for current scale but worth noting.

Medium — Code Quality

4. Excessive diagnostic vdiag! calls left in
src/vexec.rs — ~20+ vdiag!("...") calls throughout the file. These unconditionally write to stderr. In production, each binary load generates 10+ extra write() syscalls. Consider:

  • A feature flag (e.g. vdiag) for conditional compilation
  • Or at minimum a OnceLock<AtomicBool> for runtime toggle

5. VPROC_CREATE_PROGRESS is pub static mut
src/ffi.rs:57

#[no_mangle]
static mut VPROC_CREATE_PROGRESS: u32 = 0;

This is a debug artifact — no code reads it anymore (it was used to investigate the hang). It should be removed or at least commented as debug-only.

6. Repeated std::mem::transmute for function pointers
src/preload.rs — throughout

let f: extern "C" fn(c_int, *mut c_void, usize) -> isize =
    std::mem::transmute(real("read\0"));

The same pattern repeats 15+ times. Consider a helper macro or generic function:

unsafe fn call_real<T>(sym: &'static str) -> T {
    std::mem::transmute(real(sym))
}

7. Spin-lock loop in create_process
src/ffi.rs:438-444

for _ in 0..100 {
    if let Ok(s) = session.try_lock() {
        sq = Some(Arc::clone(&s.spawn_queue));
        break;
    }
    nanosleep(1ms);
}

This is a spin-lock with 1ms interval × 100 retries (worst case 100ms). Consider a Condvar-based approach or restructuring to avoid the contention entirely.

Low — Style/Minor

8. Unused _mco_pac_available() in tests
coro/minicoro.c:1230 — Function defined but marked __attribute__((unused)) and never called. The PAC check is done inline in assembly (xpaci is NOP on non-PAC hw). Remove this dead code.

9. Java file hardcodes Hermux paths
TestTermuxSession.java:108-109

static final String SHELL = "/data/data/com.hermux/files/usr/bin/sh";
static final String BASH  = "/data/data/com.hermux/files/usr/bin/bash";

These won't work in ART untrusted_app context (getShellPath() handles the fallback), but the constants may confuse readers. Add a comment explaining they're Hermux production paths and getShellPath() provides the test fallback.


Performance Impact

  • Driver thread condvar.wait (Fix A) — Good CPU savings. Untimed futex wait avoids polling.
  • prctl(PR_SET_TIMERSLACK, 50µs) (Fix B) — Critical for latency on Android.
  • 10ms JNI poll (Fix C) — 250ms→10ms poll interval massively reduces vpid detection latency.
  • Diagnostic write() calls — 10+ extra syscalls per binary load. Minor but non-zero impact in production.

Verdict

The PR solidly fixes real crash and hang issues with good root cause analysis and appropriate fixes. Main concerns: production readiness of diagnostic logging (issue 4), and select() fd_set bounds checking (issue 1). I/O scheduling design is sound. Test infrastructure is significantly improved.

Recommendation: Merge, but follow up with cleanup (remove vdiag!/VPROC_CREATE_PROGRESS debug artifacts, add select() bounds check).

PR #30 review items 1,2,7,8,9 — safe fixes that don't affect hot paths:
- Mark FFI functions as unsafe with # Safety docs
- Add Default impls for Executor and VfdTable
- Use const {} for thread_local initializer
- Replace b"...\0".as_ptr() with c"" literals
- Simplify with is_some_and/unwrap_or/matches!/is_multiple_of
- Remove unnecessary casts and .into() calls
- Use iterators instead of index loops
- Add doc comments to public API surface
- Add Javadoc to SHELL/BASH constants in TestTermuxSession

31/31 APK tests pass, no performance regression.

Bahtya
@Bahtya
Copy link
Copy Markdown
Owner Author

Bahtya commented May 24, 2026

PR Review 修复结果

commit b6bb6c0 已处理 review 中可安全修复的项目。

已修复

# 问题 修复
5 VPROC_CREATE_PROGRESS 死代码 ✅ 已在之前移除
8 _mco_pac_available() 死代码 ✅ 已在之前移除
9 Java SHELL/BASH 缺少文档注释 ✅ 添加 Javadoc
clippy 73 个警告 ✅ 73→0(FFI unsafe 标记、Default impl、c"" 字面量等)
公共 API 缺少文档注释 ✅ 添加 /// doc comments 和 # Safety sections

跳过(已提 issue 跟进)

# 问题 原因 Issue
1 select() fd_set 越界 需要更多测试 #31
2 poll() 无限超时忙循环 需要设计讨论 #32
3 wake_io_ready O(n²) 之前优化尝试证明安全 #33
4 vdiag! 诊断调用 可能影响热路径 #34
6 real_fn transmute 辅助 Rust 泛型 transmute 限制 #35
7 spin-lock 循环 简化导致 fd 泄漏 #36

回归测试

31/31 APK 测试通过,性能无回归:

  • 20 rapid sh: 1035ms avg(基线 1033ms)
  • 20 rapid bash: 1032ms avg(基线 1034ms)

@Bahtya Bahtya merged commit 82ae231 into main May 24, 2026
Bahtya added a commit that referenced this pull request May 24, 2026
…ning (#37)

* fix: per-session FFI bridge, ART symbol visibility, self-test skip

- Fix FFI signature mismatch: C bridge now includes session_id param
- Add session management (create/destroy) to JNI bridge
- Fix ART symbol visibility: dlopen(path, RTLD_GLOBAL) for RTLD_DEFAULT
- Skip self-test under ART/MTE to avoid siglongjmp MutexGuard deadlock
- Make dup failure non-fatal in driver thread
- Add crash handler thread-safety (pthread_exit for non-test threads)
- Update ART test infrastructure (HTTP server, Makefile, build)

Bahtya

* test: align ART test APK with Hermux's exact vproc integration

Match Hermux's termux.c flow for issue #29 crash reproduction:
- Use 6-arg FFI function pointers (no session_id), matching Hermux
- Replace openpty() with manual /dev/ptmx PTY setup
- Match Hermux's signal handler (re-raise for tombstone)
- Add createSubprocess + waitFor JNI methods
- Use Hermux shell paths and InputReader/TermSessionWaiter threads
- Add runHermuxSession() with persistent thread model

Bahtya

* fix: MTE-aware stack allocation, PAC-aware longjmp (issue #29)

- ELF stacks: alloc() → mmap + PROT_MTE + guard page
- mprotect: preserve PROT_MTE on GOT patching, segment restore, inline hooks
- PAC: xpaclri before siglongjmp in ffi.rs probe and JNI bridge recovery handler
- Coroutine Drop: munmap (guard + stack) instead of dealloc

Issue #29

* fix: build fix — munmap cast, remove unused dealloc import

* fix: FFI parameter mismatch — add 6-arg compat functions

The C bridge calls vproc_ffi_create_process with 6 args (no session_id)
but the Rust function expects 7 args (session_id first). On aarch64 this
shifts all parameters — c_path becomes session_id, argv becomes path,
stdin_fd becomes envp. Dereferencing stdin_fd as a pointer causes SIGSEGV.

Add vproc_ffi_create_process_default, run_until_exit_default, and
vpid_exists_default (6-arg, 1-arg, 1-arg) that auto-create a global
default session. Update the C bridge to dlsym these compat symbols.

Also fix mprotect_mte_aware to not add PROT_MTE to executable pages —
code pages from dlopen don't have MTE tags, and mprotect with PROT_MTE
returns EINVAL, causing inline hooks (exit/execve) to silently fail.

Bahtya

* diag: add logging to FFI compat functions

* fix: replace raw syscall with libc write/read to avoid seccomp

* fix: remove libc::syscall(64,...) calls blocked by Android seccomp

* fix: disable PROT_MTE on ELF stack and mprotect to avoid MTE async SIGKILL

On Android 16 untrusted_app with MTE async enabled, adding PROT_MTE
to mmap/mprotect causes the kernel to check memory tags on access.
Since vproc's virtualized ELF environment doesn't properly tag memory,
this triggers async SIGKILL. Disable PROT_MTE entirely for now.

Also disable raw_log in vproc_jni_bridge.c to avoid any potential
seccomp/MTE issues from stderr writes.

Bahtya

* fix: replace remaining libc::syscall(64,...) with libc::write, skip diagnostics

All raw syscall(__NR_write,...) calls blocked by Android seccomp on
untrusted_app. Replace with libc::write() wrapper. Skip diagnostics
in test to avoid creating orphan vpid that blocks the driver thread.

Bahtya

* debug: add ALOGI tracing to createSubprocess/waitFor, enable raw_log

Add Android logcat output to key JNI functions to diagnose where
createSubprocess hangs on device.

Bahtya

* fix: add -llog to Makefile.art for __android_log_print

Bahtya

* fix: use write(2) based jni_log instead of __android_log_print

Avoid liblog dependency. Use write(2) to stderr which is redirected
to logcat by ArtTestActivity.

Bahtya

* fix: add ALOGE macro

* fix: use dlsym to load __android_log_print at runtime

Avoid link-time liblog dependency by dynamically loading via dlsym.

Bahtya

* debug: add log before create_process call

* debug: bypass LazyLock with direct session API, add session-based FFI

* fix: separate spawn_queue lock to avoid session mutex deadlock

* fix: replace condvar wait with polling+sleep for ART compatibility

* fix: use try_lock for session access to avoid ART mutex spinlock deadlock

* fix: disable raw_log to avoid vproc GOT-hooked write() interception

* fix: use libc::nanosleep instead of std::thread::sleep in polling loop

* debug: add Rust-side logcat output to vproc_ffi_create_process

* debug: log which API branch is used in createSubprocess

* debug: add VPROC_CREATE_PROGRESS marker for debugging hang

* debug: read VPROC_CREATE_PROGRESS from C bridge

* debug: move VPROC_CREATE_PROGRESS to module level, add progress_ptr log

* fix: move cp_thread to file scope, avoid GCC nested function extension

Bahtya

* fix: production-grade PAC handling + I/O-aware event-driven scheduling

PAC (Pointer Authentication) on ARMv8.3+ devices caused SIGILL during
coroutine context switch. The minimal xpaclri fix is replaced with xpaci
at save time, matching Boost.Context's approach. Real fd I/O that blocked
the driver thread is now cooperative via yield_for_io + batch poll.

Fixes #28, fixes #29.

- coro/minicoro.c: xpaci x11 strips PAC from saved lr at context save;
  .arch_extension pauth for assembler; runtime getauxval(AT_HWCAP) check
- src/coroutine.rs: IoWait struct + io_wait field for I/O-waiting coroutines
- src/executor.rs: yield_for_io/collect_io_waits/wake_io_ready functions
- src/preload.rs: non-blocking read/write via poll-before-read; poll()
  interceptor (virtual pipes + real fds); select() interceptor; SIGILL +
  SIGABRT added to crash handler; libc::syscall(64,...) → libc::write()
- src/vexec.rs: GOT patch for poll and select
- src/ffi.rs: remove unused sigsetjmp/siglongjmp probe code (24 lines)
- examples: clean up unused unsafe/variable warnings

Bahtya

* fix: use _start path for cached binaries, fixing bash SIGSEGV

The cached (second invocation) path was calling main() directly, skipping
__libc_init which sets up TLS/stack canary state. This caused heap corruption
in bash's initialize_shell_variables, where envp[i] contained MTE-tagged
.text addresses instead of valid env strings.

Both first-load and cached paths now use spawn_elf (_start → __libc_init →
main), which properly reinitializes libc state for each invocation.

ART test results: 15/17 pass (bash all PASS, sh 2 non-crash failures).

Bahtya

* fix: fdsan SIGABRT, exit_codes persistence, diag_log PTY leak — 31/31 pass

- close() fallback uses real_close() to clear fdsan tags before close
- vpid_exists and run_until_exit check exit_codes HashMap for reaped procs
- Remove diag_log! macro — was writing to fd 2 which leaked into PTY output
- Make exit_codes pub(crate) on Executor
- Stress/concurrency tests converted to hermux flow (separate sessions)
- All 31 ART tests pass, zero crashes

Bahtya

* perf: condvar driver idle + prctl timer slack + 10ms JNI poll — hermux 1259→1028ms

- Driver idle loop: nanosleep(10ms) → condvar.wait() (untimed, no timer slack)
- Fix deadlock: create_process now notifies Session.wake (was only notifying
  spawn_queue condvar which driver doesn't wait on)
- prctl(PR_SET_TIMERSLACK, 50µs) in create_process, run_until_exit, cp_thread,
  and JNI poll thread — eliminates Android 40ms background timer slack
- JNI vpid poll interval: 250ms → 10ms (with prctl, actual ~10ms not ~300ms)
- Unified signing keystore (debug.keystore at project root, survives clean)

Results: 31/31 pass, 20 rapid bash 10647→1034ms (-90%), no heavy init regression

Bahtya

* docs: note why polling beats condvar for result waits on Android

Android scheduler restores background threads after condvar wake with
~50ms latency, worse than 10ms poll with prctl-reduced timer slack.
Keep polling for create_process and run_until_exit result waits.

Bahtya

* fix: clippy 73→0 warnings, add doc comments and Safety sections

PR #30 review items 1,2,7,8,9 — safe fixes that don't affect hot paths:
- Mark FFI functions as unsafe with # Safety docs
- Add Default impls for Executor and VfdTable
- Use const {} for thread_local initializer
- Replace b"...\0".as_ptr() with c"" literals
- Simplify with is_some_and/unwrap_or/matches!/is_multiple_of
- Remove unnecessary casts and .into() calls
- Use iterators instead of index loops
- Add doc comments to public API surface
- Add Javadoc to SHELL/BASH constants in TestTermuxSession

31/31 APK tests pass, no performance regression.

Bahtya

* fix: reverse trampoline write order (#23) and restore RX after hook (#24)

Write the 64-bit target address before the LDR/BR instructions so an
interrupt can never observe a valid load pointing at uninitialized data.
Restore page permissions to PROT_READ|PROT_EXEC after writing to avoid
leaving libc code pages permanently writable (W^X compliance).

Closes #23, closes #24.

Bahtya
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant