use crate::signatures::rules::RuleSet;
use crate::types::*;
use std::collections::{HashMap, HashSet};
pub struct SignatureEngine {
rules: RuleSet,
}
impl SignatureEngine {
pub fn new() -> Self {
Self {
rules: RuleSet::default(),
}
}
pub fn detect_from_crash(&self, crash: &CrashReport) -> Vec<BugSignature> {
let mut signatures = Vec::new();
let facts = self.extract_facts(crash);
for rule in self.rules.rules() {
match rule.name.as_str() {
"use_after_free" => {
signatures.extend(self.infer_use_after_free(&facts, crash));
}
"double_free" => {
signatures.extend(self.infer_double_free(&facts, crash));
}
"deadlock" => {
signatures.extend(self.infer_deadlock(&facts, crash));
}
"data_race" => {
signatures.extend(self.infer_data_race(&facts, crash));
}
_ => {} }
}
signatures.extend(self.infer_null_deref(&facts, crash));
signatures.extend(self.infer_buffer_overflow(&facts, crash));
signatures
}
fn extract_facts(&self, crash: &CrashReport) -> HashSet<Fact> {
let mut facts = HashSet::new();
let stderr = &crash.stderr;
if stderr.contains("malloc") || stderr.contains("alloc") {
facts.insert(Fact::Alloc {
var: "heap_var".to_string(),
location: 0,
});
}
if stderr.contains("free") || stderr.contains("drop") {
facts.insert(Fact::Free {
var: "heap_var".to_string(),
location: 1,
});
}
if stderr.contains("use") || stderr.contains("access") {
facts.insert(Fact::Use {
var: "heap_var".to_string(),
location: 2,
});
}
if stderr.contains("lock") || stderr.contains("mutex") {
facts.insert(Fact::Lock {
mutex: "mutex1".to_string(),
location: 0,
});
}
if stderr.contains("unlock") {
facts.insert(Fact::Unlock {
mutex: "mutex1".to_string(),
location: 1,
});
}
if stderr.contains("thread") || stderr.contains("spawn") {
facts.insert(Fact::ThreadSpawn {
id: "thread1".to_string(),
location: 0,
});
}
facts
}
fn infer_use_after_free(
&self,
facts: &HashSet<Fact>,
crash: &CrashReport,
) -> Vec<BugSignature> {
let mut signatures = Vec::new();
for fact1 in facts {
if let Fact::Free {
var: var1,
location: free_loc,
} = fact1
{
for fact2 in facts {
if let Fact::Use {
var: var2,
location: use_loc,
} = fact2
{
if var1 == var2 && free_loc < use_loc {
signatures.push(BugSignature {
signature_type: SignatureType::UseAfterFree,
confidence: 0.85,
evidence: vec![
format!("Free at location {}", free_loc),
format!("Use at location {}", use_loc),
"Temporal ordering violation detected".to_string(),
],
location: Some(format!("Location {}", use_loc)),
});
}
}
}
}
}
if crash.stderr.contains("use after free")
|| crash.stderr.contains("use-after-free")
|| (crash.stderr.contains("freed") && crash.stderr.contains("accessed"))
{
signatures.push(BugSignature {
signature_type: SignatureType::UseAfterFree,
confidence: 0.95,
evidence: vec!["Direct mention in error message".to_string()],
location: None,
});
}
signatures
}
fn infer_double_free(&self, facts: &HashSet<Fact>, crash: &CrashReport) -> Vec<BugSignature> {
let mut signatures = Vec::new();
let mut free_locations: HashMap<String, Vec<usize>> = HashMap::new();
for fact in facts {
if let Fact::Free { var, location } = fact {
free_locations
.entry(var.clone())
.or_default()
.push(*location);
}
}
for (var, locations) in free_locations {
if locations.len() > 1 {
signatures.push(BugSignature {
signature_type: SignatureType::DoubleFree,
confidence: 0.90,
evidence: vec![
format!("Variable {} freed multiple times", var),
format!("Locations: {:?}", locations),
],
location: Some(format!("Locations {:?}", locations)),
});
}
}
if crash.stderr.contains("double free")
|| crash.stderr.contains("double-free")
|| crash.stderr.contains("freed twice")
{
signatures.push(BugSignature {
signature_type: SignatureType::DoubleFree,
confidence: 0.95,
evidence: vec!["Direct mention in error message".to_string()],
location: None,
});
}
signatures
}
fn infer_deadlock(&self, facts: &HashSet<Fact>, crash: &CrashReport) -> Vec<BugSignature> {
let mut signatures = Vec::new();
let mut locks: Vec<(String, usize)> = Vec::new();
for fact in facts {
if let Fact::Lock { mutex, location } = fact {
locks.push((mutex.clone(), *location));
}
}
if locks.len() >= 2 {
signatures.push(BugSignature {
signature_type: SignatureType::Deadlock,
confidence: 0.70,
evidence: vec![
format!("{} locks detected", locks.len()),
"Potential lock ordering issue".to_string(),
],
location: None,
});
}
if crash.stderr.contains("deadlock")
|| crash.stderr.contains("deadlocked")
|| (crash.stderr.contains("waiting") && crash.stderr.contains("lock"))
{
signatures.push(BugSignature {
signature_type: SignatureType::Deadlock,
confidence: 0.90,
evidence: vec!["Deadlock pattern in error message".to_string()],
location: None,
});
}
signatures
}
fn infer_data_race(&self, facts: &HashSet<Fact>, crash: &CrashReport) -> Vec<BugSignature> {
let mut signatures = Vec::new();
let has_writes = facts.iter().any(|f| matches!(f, Fact::Write { .. }));
let has_reads = facts.iter().any(|f| matches!(f, Fact::Read { .. }));
let has_threads = facts.iter().any(|f| matches!(f, Fact::ThreadSpawn { .. }));
if has_writes && has_reads && has_threads {
signatures.push(BugSignature {
signature_type: SignatureType::DataRace,
confidence: 0.65,
evidence: vec![
"Concurrent reads and writes detected".to_string(),
"Multiple threads present".to_string(),
],
location: None,
});
}
if crash.stderr.contains("data race")
|| crash.stderr.contains("race condition")
|| crash.stderr.contains("ThreadSanitizer")
{
signatures.push(BugSignature {
signature_type: SignatureType::DataRace,
confidence: 0.95,
evidence: vec!["Race condition detected by sanitizer".to_string()],
location: None,
});
}
signatures
}
fn infer_null_deref(&self, _facts: &HashSet<Fact>, crash: &CrashReport) -> Vec<BugSignature> {
let mut signatures = Vec::new();
if crash.signal == Some("SIGSEGV".to_string())
|| crash.stderr.contains("null pointer")
|| crash.stderr.contains("nullptr")
|| crash.stderr.contains("nil pointer")
|| crash.stderr.contains("address 0x0")
{
signatures.push(BugSignature {
signature_type: SignatureType::NullPointerDeref,
confidence: 0.90,
evidence: vec!["SIGSEGV or null pointer pattern detected".to_string()],
location: None,
});
}
signatures
}
fn infer_buffer_overflow(
&self,
_facts: &HashSet<Fact>,
crash: &CrashReport,
) -> Vec<BugSignature> {
let mut signatures = Vec::new();
if crash.stderr.contains("buffer overflow")
|| crash.stderr.contains("stack smashing")
|| crash.stderr.contains("heap corruption")
|| crash.stderr.contains("AddressSanitizer")
{
signatures.push(BugSignature {
signature_type: SignatureType::BufferOverflow,
confidence: 0.95,
evidence: vec!["Buffer overflow pattern detected".to_string()],
location: None,
});
}
signatures
}
}
impl Default for SignatureEngine {
fn default() -> Self {
Self::new()
}
}