mod common;
use std::thread;
use assert2::assert;
use idakit::prelude::*;
use common::TestDb;
const STACK: usize = 8 << 20;
#[derive(Clone, PartialEq, Eq, Debug)]
struct Fingerprint {
funcs: usize,
entry: u64,
name: String,
pseudocode: String,
}
fn fingerprint(db: &mut Database) -> Fingerprint {
db.clear_decompilation_cache();
let funcs = db.functions().count();
let first = db.functions().next().expect("at least one function");
let entry = first.address();
let name = first.name().as_str().to_owned();
let pseudocode = db
.decompile(entry)
.expect("decompile the first function")
.pseudocode()
.expect("pseudocode for the first function");
Fingerprint {
funcs,
entry: entry.get(),
name,
pseudocode,
}
}
fn settle(db: &mut Database) -> Fingerprint {
let mut prev = fingerprint(db);
for _ in 0..8 {
let next = fingerprint(db);
if next == prev {
return next;
}
prev = next;
}
panic!("decompiler output never settled across 8 fresh reads");
}
fn hop(db: Database) -> (Database, Fingerprint) {
thread::Builder::new()
.stack_size(STACK)
.spawn(move || {
let mut db = db;
let fp = fingerprint(&mut db);
(db, fp)
})
.expect("spawn hop thread")
.join()
.unwrap_or_else(|e| std::panic::resume_unwind(e))
}
#[test]
fn database_send_survives_thread_moves() {
let Some(db) = TestDb::acquire() else {
println!("skipping: no corpus configured");
return;
};
thread::Builder::new()
.stack_size(STACK)
.spawn(move || run(&db))
.expect("spawn host thread")
.join()
.unwrap_or_else(|e| std::panic::resume_unwind(e));
}
fn run(db: &TestDb) {
let mut idb = Ida::new().here().expect("kernel init failed");
idb.open(db.path()).call().expect("open failed");
let baseline = settle(&mut idb);
let (mut idb, on_b) = hop(idb);
let on_host_again = fingerprint(&mut idb);
let (mut idb, on_c) = hop(idb);
assert!(
!baseline.pseudocode.is_empty(),
"expected non-empty pseudocode"
);
assert!(
baseline == on_b,
"fingerprint diverged after moving to thread B"
);
assert!(
baseline == on_host_again,
"fingerprint diverged after B died and the host reclaimed the database"
);
assert!(
baseline == on_c,
"fingerprint diverged after moving to thread C"
);
idb.close(false);
println!(
"Database Send move-chain OK: {} funcs, entry {:#x}, fresh decompile stable across 3 threads",
baseline.funcs, baseline.entry
);
}