use std::fs;
use std::io::Read;
use std::path::{Path, PathBuf};
use assert2::assert;
use idakit::prelude::*;
mod common;
struct Scratch(PathBuf);
impl Scratch {
fn new(name: &str, bytes: &[u8]) -> Self {
let path = std::env::temp_dir().join(name);
fs::write(&path, bytes).expect("write scratch file");
Self(path)
}
fn truncated(name: &str, src: impl AsRef<Path>, len: usize) -> Self {
let mut file = fs::File::open(src).expect("open source db");
let mut buf = vec![0u8; len];
let n = file.read(&mut buf).expect("read source prefix");
buf.truncate(n);
Self::new(name, &buf)
}
fn path(&self) -> String {
self.0.to_str().expect("utf-8 temp path").to_owned()
}
}
impl Drop for Scratch {
fn drop(&mut self) {
let _ = fs::remove_file(&self.0);
}
}
fn open_is_rejected(idb: &mut Database, path: &str) {
let result = idb.open(path).call();
assert!(result.is_err(), "open of {path:?} should be rejected");
}
fn assert_open_rejected(path: String) {
Ida::run(move |ida| {
ida.call(move |idb| open_is_rejected(idb, &path))
.unwrap_or_else(|e| e.resume())
})
.expect("kernel init failed");
}
#[test]
fn nonexistent_path_is_rejected() {
let missing = std::env::temp_dir().join("idakit-faults-missing.i64");
assert_open_rejected(missing.to_string_lossy().into_owned());
}
#[test]
fn empty_file_is_rejected() {
let scratch = Scratch::new("idakit-faults-empty.i64", b"");
assert_open_rejected(scratch.path());
}
#[test]
fn garbage_bytes_are_rejected() {
let scratch = Scratch::new("idakit-faults-garbage.i64", &[0xABu8; 4096]);
assert_open_rejected(scratch.path());
}
#[test]
fn directory_path_is_rejected() {
assert_open_rejected(std::env::temp_dir().to_string_lossy().into_owned());
}
#[test]
#[cfg_attr(
not(target_os = "linux"),
ignore = "the rejection makes idalib exit(); trapping it needs the Linux-only exit trap"
)]
fn unsupported_java_class_reports_reason() {
const TOO_NEW_CLASS: &[u8] = &[
0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x45, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let scratch = Scratch::new("idakit-faults-toonew.class", TOO_NEW_CLASS);
let path = scratch.path();
Ida::run(move |ida| {
ida.call(move |idb| {
let err = idb
.open(&path)
.call()
.expect_err("too-new class should be rejected");
assert!(let Error::KernelExit { diagnostic, .. } = &err);
let diag = diagnostic.as_deref().unwrap_or("");
assert!(
diag.contains("Java file format"),
"KernelExit diagnostic should carry the loader reason, got {diagnostic:?}"
);
})
.unwrap_or_else(|e| e.resume())
})
.expect("kernel init failed");
}
#[test]
#[cfg_attr(
not(target_os = "linux"),
ignore = "a corrupt-header database makes idalib call exit(); trapping that needs the \
GOT-redirect exit trap, which is Linux-only (elsewhere the process just exits)"
)]
fn truncated_database_is_rejected() {
let Some(db) = common::TestDb::source() else {
return;
};
let scratch = Scratch::truncated("idakit-faults-truncated.i64", &db, 4096);
assert_open_rejected(scratch.path());
}