Skip to content

Commit 721ff32

Browse files
authored
fix: avoid revisiting locations in CFG traversal (#121)
1 parent 4c7804e commit 721ff32

File tree

3 files changed

+56
-29
lines changed

3 files changed

+56
-29
lines changed

jingle/examples/unwind.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fn main() {
3030
z3::set_global_param("trace", "true");
3131
let loaded = load_with_gimli(bin_path, "/Applications/ghidra").unwrap();
3232

33-
let mut direct = UnwindingAnalysis::new(1);
33+
let mut direct = UnwindingAnalysis::new(10);
3434
let pcode_graph = direct.run(&loaded, direct.make_initial_state(FUNC_NESTED.into()));
3535
// let pcode_graph = pcode_graph.basic_blocks();
3636
let addrs = pcode_graph.nodes().collect::<Vec<_>>();

jingle/src/analysis/cfg/mod.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ use petgraph::graph::NodeIndex;
1010
use petgraph::prelude::DiGraph;
1111
use petgraph::visit::EdgeRef;
1212
use std::borrow::Borrow;
13-
use std::collections::HashMap;
13+
use std::cell::RefCell;
14+
use std::collections::{HashMap, HashSet};
1415
use std::fmt::{Formatter, LowerHex};
16+
use std::rc::Rc;
1517
use z3::ast::Bool;
1618

1719
mod model;
@@ -38,17 +40,37 @@ pub struct PcodeCfg<N: CfgState = ConcretePcodeAddress, D = PcodeOperation> {
3840
pub struct PcodeCfgVisitor<'a, N: CfgState = ConcretePcodeAddress, D = PcodeOperation> {
3941
cfg: &'a PcodeCfg<N, D>,
4042
location: N,
43+
pub(crate) visited_locations: Rc<RefCell<HashSet<N>>>,
4144
}
4245

4346
impl<'a, N: CfgState, D: ModelTransition<N::Model>> PcodeCfgVisitor<'a, N, D> {
44-
pub(crate) fn successors(&self) -> impl Iterator<Item = Self> {
47+
pub(crate) fn successors(&mut self) -> impl Iterator<Item = Self> {
4548
self.cfg
4649
.successors(&self.location)
4750
.into_iter()
4851
.flatten()
49-
.map(|n| Self {
50-
cfg: self.cfg,
51-
location: n.clone(),
52+
.flat_map(|n| {
53+
// Use a short-lived borrow so the RefMut is released before we construct the new visitor
54+
let is_repeat = {
55+
let mut set = self.visited_locations.borrow_mut();
56+
if set.contains(n) {
57+
println!("Trimming repeat of {n:x?}");
58+
true
59+
} else {
60+
set.insert(n.clone());
61+
false
62+
}
63+
};
64+
65+
if is_repeat {
66+
None
67+
} else {
68+
Some(Self {
69+
cfg: self.cfg,
70+
location: n.clone(),
71+
visited_locations: self.visited_locations.clone(),
72+
})
73+
}
5274
})
5375
}
5476

@@ -288,10 +310,11 @@ impl<D: ModelTransition<MachineState>> PcodeCfg<UnwoundLocation, D> {
288310
location: &UnwoundLocation,
289311
ctl_model: CtlFormula<UnwoundLocation, D>,
290312
) -> Bool {
291-
let visitor = PcodeCfgVisitor {
313+
let mut visitor = PcodeCfgVisitor {
292314
location: location.clone(),
293315
cfg: self,
316+
visited_locations: Rc::new(RefCell::new(HashSet::new())),
294317
};
295-
ctl_model.check(&visitor)
318+
ctl_model.check(&mut visitor)
296319
}
297320
}

jingle/src/analysis/ctl.rs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ impl<N: CfgState, D: ModelTransition<N::Model>> std::fmt::Debug for CtlFormula<N
309309
}
310310
}
311311
impl<N: CfgState, D: ModelTransition<N::Model>> CtlFormula<N, D> {
312-
pub fn check(&self, g: &PcodeCfgVisitor<N, D>) -> Bool {
312+
pub fn check(&self, g: &mut PcodeCfgVisitor<N, D>) -> Bool {
313313
match self {
314314
CtlFormula::Bottom => Bool::from_bool(false),
315315
CtlFormula::Top => Bool::from_bool(true),
@@ -351,24 +351,26 @@ impl<N: CfgState, D: ModelTransition<N::Model>> CtlFormula<N, D> {
351351
}
352352
}
353353

354-
pub(crate) fn check_next_exists(&self, g: &PcodeCfgVisitor<N, D>) -> Bool {
355-
let state = g.state().unwrap();
356-
let connect: Vec<_> = g
357-
.successors()
354+
pub(crate) fn check_next_exists(&self, g: &mut PcodeCfgVisitor<N, D>) -> Bool {
355+
let state = g.state().unwrap().clone();
356+
let trans = g.transition().cloned();
357+
let successors: Vec<_> = g.successors().collect();
358+
let connect: Vec<_> = successors
359+
.iter()
358360
.map(|a| {
359361
let successor = a.state().unwrap();
360-
let after = g.transition().unwrap().transition(state).unwrap();
362+
let after = trans.clone().unwrap().transition(&state).unwrap();
361363

362364
after.location_eq(successor)
363365
})
364366
.collect();
365367
let connect = Bool::or(&connect);
366-
let bools: Vec<_> = g
367-
.successors()
368-
.flat_map(|a| {
368+
let bools: Vec<_> = successors
369+
.into_iter()
370+
.flat_map(|mut a| {
371+
let check = self.check(&mut a);
369372
let successor = a.state().unwrap();
370-
let check = self.check(&a);
371-
let after = g.transition().unwrap().transition(state).unwrap();
373+
let after = trans.clone().unwrap().transition(&state).unwrap();
372374
let imp = after
373375
.location_eq(successor)
374376
.implies(after.state_eq(successor));
@@ -378,24 +380,26 @@ impl<N: CfgState, D: ModelTransition<N::Model>> CtlFormula<N, D> {
378380
Bool::or(&bools).bitand(connect)
379381
}
380382

381-
pub(crate) fn check_next_universal(&self, g: &PcodeCfgVisitor<N, D>) -> Bool {
382-
let state = g.state().unwrap();
383-
let connect: Vec<_> = g
384-
.successors()
383+
pub(crate) fn check_next_universal(&self, g: &mut PcodeCfgVisitor<N, D>) -> Bool {
384+
let state = g.state().unwrap().clone();
385+
let trans = g.transition().cloned();
386+
let successors: Vec<_> = g.successors().collect();
387+
let connect: Vec<_> = successors
388+
.iter()
385389
.map(|a| {
386390
let successor = a.state().unwrap();
387-
let after = g.transition().unwrap().transition(state).unwrap();
391+
let after = trans.clone().unwrap().transition(&state).unwrap();
388392

389393
after.location_eq(successor)
390394
})
391395
.collect();
392396
let connect = Bool::or(&connect);
393-
let bools: Vec<_> = g
394-
.successors()
395-
.flat_map(|a| {
397+
let bools: Vec<_> = successors
398+
.into_iter()
399+
.flat_map(|mut a| {
400+
let check = self.check(&mut a);
396401
let successor = a.state().unwrap();
397-
let check = self.check(&a);
398-
let after = g.transition().unwrap().transition(state).unwrap();
402+
let after = trans.clone().unwrap().transition(&state).unwrap();
399403
let imp = after
400404
.location_eq(successor)
401405
.implies(after.state_eq(successor));

0 commit comments

Comments
 (0)