Stalker bugs
I've found some bugs, not sure if they are caused by the skill issues of mine, the incorrect usage of bindings, or the frida itself
Writing to stdout in the callback passed to the Transformer::from_callback causes the program to panic:
thread '<unnamed>' panicked at library/std/src/io/stdio.rs:860:20:
RefCell already borrowed
stack backtrace:
0: __rustc::rust_begin_unwind
at /rustc/9748d87dc70a9a6725c5dbd76ce29d04752b4f90/library/std/src/panicking.rs:697:5
1: core::panicking::panic_fmt
at /rustc/9748d87dc70a9a6725c5dbd76ce29d04752b4f90/library/core/src/panicking.rs:75:14
2: core::cell::panic_already_borrowed::do_panic::runtime
at /rustc/9748d87dc70a9a6725c5dbd76ce29d04752b4f90/library/core/src/panic.rs:218:21
3: core::cell::panic_already_borrowed::do_panic
at /rustc/9748d87dc70a9a6725c5dbd76ce29d04752b4f90/library/core/src/intrinsics/mod.rs:2367:9
4: core::cell::panic_already_borrowed
at /rustc/9748d87dc70a9a6725c5dbd76ce29d04752b4f90/library/core/src/panic.rs:223:9
5: core::cell::RefCell<T>::borrow_mut
at /rustc/9748d87dc70a9a6725c5dbd76ce29d04752b4f90/library/core/src/cell.rs:1089:25
6: <std::io::stdio::StdoutLock as std::io::Write>::write_all
at /rustc/9748d87dc70a9a6725c5dbd76ce29d04752b4f90/library/std/src/io/stdio.rs:860:20
7: <std::io::default_write_fmt::Adapter<T> as core::fmt::Write>::write_str
at /rustc/9748d87dc70a9a6725c5dbd76ce29d04752b4f90/library/std/src/io/mod.rs:628:30
8: core::fmt::write
at /rustc/9748d87dc70a9a6725c5dbd76ce29d04752b4f90/library/core/src/fmt/mod.rs:1493:23
...
When attaching to a thread by id, instr.put_callout will make the program hang indefinitely, probably a segfault/panic; when attaching to the current thread from the interceptor callback, everything works fine (why does interceptor.attach accept a &mut ref, and not a box or something like this? This leads to UB if the value is dropped)
use std::sync::LazyLock;
use ctor::ctor;
use frida_gum::interceptor::{Interceptor, InvocationContext, InvocationListener};
use frida_gum::stalker::{Event, EventMask, EventSink, Stalker, Transformer};
use frida_gum::{Gum, Process};
static GUM: LazyLock<Gum> = LazyLock::new(Gum::obtain);
struct Sink;
impl EventSink for Sink {
fn query_mask(&mut self) -> EventMask {
EventMask::None
}
fn start(&mut self) {}
fn process(&mut self, _event: &Event) {}
fn flush(&mut self) {}
fn stop(&mut self) {}
}
struct WriteListener {
followed: bool,
}
impl InvocationListener for WriteListener {
fn on_enter(&mut self, _context: InvocationContext) {
if !self.followed {
self.followed = true;
println!("Following self...");
let transformer = Transformer::from_callback(&GUM, |block, _| {
for instr in block {
let ix = instr.instr();
let data = ix.bytes();
// syscall
if data == [0x0f, 0x05] {
// causes a panic
println!("damn, the program panicked");
}
instr.keep();
}
});
let mut stalker = Stalker::new(&GUM);
stalker.follow_me(&transformer, None::<&mut Sink>);
}
println!("entered");
}
fn on_leave(&mut self, _context: InvocationContext) {
println!("left");
}
}
#[ctor]
fn entry() {
let process = Process::obtain(&GUM);
let module = process.find_module_by_name("libc.so.6").unwrap();
let write = module.find_export_by_name("write").unwrap();
let mut interceptor = Box::leak(Box::new(Interceptor::obtain(&GUM)));
// Follow a single thread once
interceptor.attach(
write,
Box::leak(Box::new(WriteListener { followed: false })),
);
}
use std::collections::HashSet;
use std::sync::{LazyLock, Mutex};
use std::thread;
use std::time::Duration;
use ctor::ctor;
use frida_gum::stalker::{Event, EventMask, EventSink, Stalker, Transformer};
use frida_gum::{Gum, Process};
static GUM: LazyLock<Gum> = LazyLock::new(Gum::obtain);
struct SampleEventSink;
impl EventSink for SampleEventSink {
fn query_mask(&mut self) -> EventMask {
EventMask::Exec
}
fn start(&mut self) {}
fn process(&mut self, _event: &Event) {}
fn flush(&mut self) {}
fn stop(&mut self) {}
}
fn follow_thread(thread_id: usize) {
let mut stalker = Stalker::new(&GUM);
let transformer = Transformer::from_callback(&GUM, |basic_block, _output| {
for instr in basic_block {
// Uncommenting any of these makes the process hang
// instr.put_callout(|_| {});
instr.keep();
// Uncommenting any of these makes the process hang
// instr.put_callout(|_| {});
}
});
stalker.follow(thread_id, &transformer, None::<&mut SampleEventSink>);
}
// a static is not needed here as entry() is called only one time
static FOLLOWED: LazyLock<Mutex<HashSet<usize>>> = LazyLock::new(Mutex::default);
fn entry() {
println!("entry");
let process = Process::obtain(&GUM);
let mut followed = FOLLOWED.lock().unwrap();
let this_thread_id = process.current_thread_id();
followed.insert(process.current_thread_id() as usize); // do not follow the thread that was created for following
// uses enumerate_threads() from PR #213, but I'm pretty sure the implementation returns the correct thread ids
for thread in process.enumerate_threads() {
let thread_id = thread.id() as usize;
if !followed.contains(&thread_id) {
println!("following {thread_id}; this thread id: {this_thread_id}");
followed.insert(thread_id);
// thread::spawn(move || follow_thread(thread_id));
follow_thread(thread_id)
}
}
}
#[ctor]
fn main() {
thread::spawn(entry);
// wait enough time for the entry function to run
thread::sleep(Duration::from_millis(100));
}
injection done using the LD_PRELOAD
Can you please split this issue in two, one for each bug?
also, make the titles indicative.