Skip to content

Fixed receiving "send" messages - #235

Merged
s1341 merged 1 commit into
frida:mainfrom
Xoffio:xo-send-msgs
May 19, 2026
Merged

s1341 merged 1 commit into
frida:mainfrom
Xoffio:xo-send-msgs

Conversation

@Xoffio

@Xoffio Xoffio commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Send messages were not being handled properly.

  • Fixed "send" messages not being handled properly
  • Updated examples

- Fixed receiving "send" messages
- Updated examples
@Xoffio

Xoffio commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

This fixes #210

@s1341
s1341 merged commit 881d030 into frida:main May 19, 2026
16 checks passed
jhasheng pushed a commit to jhasheng/frida-rust that referenced this pull request May 20, 2026
The merge in 5fe8f72 picked up parts of frida#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 frida#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.
s1341 pushed a commit that referenced this pull request May 20, 2026
…cript_from_bytes (#239)

* 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 from 6a92b72:

  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 for 6a92b72:

  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 in 5fe8f72 picked 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 <junjunus@hotmail.com>
Co-authored-by: yw <yw@yw>
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