mirror of
https://github.com/frida/frida-rust.git
synced 2026-11-09 01:59:37 -10:00
* fix(script): route on_message through CallbackHandler.script_handler (#189) Pre-fix, `handle_message` passed `*mut CallbackHandler` as the `g_signal_connect_data` user_data, but `call_on_message::<I>` re-cast that pointer to `*mut I` and dereferenced it: let handler: &mut I = &mut *(user_data as *mut I); handler.on_message(formatted_msg, data_vec); This reads I-sized bytes from the start of `CallbackHandler` (the `channel: (Sender, Receiver)` tuple) and treats them as `I`. It is undefined behavior, masked only when `I` is a ZST (e.g. the doc example's `struct Handler;`). Symptoms reported in #189: - `Arc<Mutex<_>>` handlers crash inside atomic_load. - Field reads return garbage / refcounts spike (e.g. 1624879470736). - Poisoned-mutex errors. For a handler whose fields include a `parking_lot`/`std::sync::Mutex`, the spurious atomic op on what frida-core treats as `Sender` internals can also deadlock `script.load_sync` outright if the JS script body issues a synchronous `send()` — the bogus SRWLOCK address looks held and `AcquireSRWLockExclusive` never returns. Fix: `user_data` is already `*mut CallbackHandler`, and `handle_message` stores the boxed trait object in `cb.script_handler`. Use that field instead of the type-pun. The `I` type parameter is kept (and given a `PhantomData::<I>` no-op) so monomorphization still emits a distinct fn pointer per handler type, preserving the original ABI for any external code that relied on it. Regression test (downstream): a `ScriptHandler` whose state includes a `u64` counter must increment cleanly from 1, 2, 3, ... rather than starting at a garbage value and corrupting adjacent memory. * feat(session): wrap compile_script + create_script_from_bytes frida-core has shipped a bytecode path for years: frida_session_compile_script_sync(session, source, opts) -> GBytes frida_session_create_script_from_bytes_sync(session, bytes, opts) -> FridaScript The Python and Node bindings expose both. frida-rust 0.17.2 only wraps the source-string path through `create_script`, leaving callers to either drop into frida-sys themselves or recompile on every load. This adds two thin methods on `Session`: fn compile_script(&self, source, &mut opts) -> Result<Vec<u8>> fn create_script_from_bytes<'b>(&self, bytes, &mut opts) -> Result<Script<'b>> `compile_script` copies the returned GBytes into an owned `Vec<u8>` and `g_bytes_unref`s the original. `create_script_from_bytes` wraps `g_bytes_new` / `g_bytes_unref` around the C call. Bytecode is runtime-specific — callers must compile and load with matching `ScriptOption::set_runtime`. Documented inline. Use cases this unblocks: - Startup speedup: skip the JS parse pass at every attach. - Soft script protection: ship .bin artifacts instead of readable JS. - Self-attach `compile_script` workflows so build pipelines can emit bytecode without spinning up an extra process. * docs(examples): add compile_script roundtrip example Demonstrates the new Session::compile_script and Session::create_script_from_bytes pair from6a92b72: source JS -> compile_script -> Vec<u8> bytecode -> write to compiled.bin (ship-as-artifact) -> read back -> create_script_from_bytes -> Script -> load Attaches to self (pid=0) so it runs out of the box with no external target, and pins ScriptRuntime::QJS on both ends to make the runtime-specific-bytecode constraint explicit. * test(session): add integration tests for compile_script + create_script_from_bytes Covers three regression surfaces for6a92b72: 1. compile_script returns a non-empty Vec<u8> and the result loads back through create_script_from_bytes (full QJS roundtrip via attach-to-self / pid=0). 2. compile_script with an interior-NUL source returns Error::CStringFailed without reaching the C side. 3. Both new methods work with bare ScriptOption::default(), matching the contract of create_script. CI already runs 'cargo test --features=auto-download' on the frida crate across Linux/macOS/Windows x x86/x86_64, so these run on every PR without workflow changes. * fix(script): complete dropped hunks from the #235 merge The merge in5fe8f72picked up parts of #235 (the `payload.get(N)` accessors in Exports::call, and the type change from `SendPayload` to `serde_json::Value`) but dropped two hunks: 1. The deletion of the now-dead `SendPayload` struct. 2. The accessor rewrite inside `call_on_message`, which still read `msg.payload.r#type`. After the type change this no longer compiled (E0609). Re-apply both hunks so the file matches what #235 intended: - Remove `SendPayload` (no remaining references in-tree). - Match the rpc envelope via `msg.payload.get(0).and_then(...)`, consistent with the same pattern at lines 367-368. * test(session): serialize frida self-attach tests via process-wide mutex cargo test runs tests in parallel by default. Two threads doing concurrent device.attach(0) / detach() inside the same process corrupts frida-core's agent state — on Windows the test binary exits with STATUS_STACK_BUFFER_OVERRUN (0xc0000409); on other platforms it would be similarly undefined. Add a static Mutex<()> that every #[test] in this file acquires for the full attach -> detach span. unwrap_or_else(into_inner) recovers a poisoned lock so a panic in one test does not cascade and turn the rest of the suite red. --------- Co-authored-by: yw <[email protected]> Co-authored-by: yw <yw@yw>