src/mimir_lens/room_feed/boundary.rs
//! **The system boundary, as the lenses read it**: which of OUR functions
//! can reach which class of system call (file, network, process, memory,
//! time …), and for each one a shortest path you can show.
//!
//! The facts are the code room's with the dependency universe folded in:
//! `syscall` nodes (the kernel's table, one per call, `ty` = its class),
//! `SYSCALLS` edges (a C function's `ffi` node → the calls it makes, through
//! the curated glibc / musl map; a fn handing the raw door a number; the raw
//! door → `<indirect>`), written once per toolchain / target by
//! `mimir_ingest::boundary`; and CALLS from our code down through std and
//! the dependencies to those functions. A call whose `SYSCALLS` hop is
//! UNCERTAIN (`partial`: the libc version, a vDSO, contention decide) says so
//! on its row. An `extern "C"` item declared in our OWN code is answered from
//! the same C map here (the producer maps the dependencies only).
//!
//! Three readers share it: the `system_boundary` lens (per repo: the classes
//! it reaches and from where), `data_reach` (its `syscalls` section: which of
//! our fns reach a class, `class` = e.g. `network`), `reachable_vuln` (what
//! the vulnerable crate our code calls can reach at the boundary).
use std::collections::{BTreeMap, BTreeSet};
use mimir_ingest::boundary::{self, Class, Libc, Mapped, Target};
use mimir_room::{EdgeSel, Mask, MaskSpec, Room, Scope};
use mimir_schema::{EdgeKind, Origin, kind as nk, moniker};
use serde::Serialize;
use serde_json::{Value, json};
use super::graph::internal;
use super::{Basis, CALLS_LOWER_BOUND, Query};
use crate::code_room::{Reach, Refusal, State};
/// The kinds a boundary walk follows.
pub const WALK: [EdgeKind; 2] = [EdgeKind::Calls, EdgeKind::Syscalls];
/// Rows (functions with their path) shown per class.
pub const ROWS_PER_CLASS: usize = 12;
/// The ring room of a request: the request's repo (or every repo) with the
/// dependency universe folded in.
pub fn ring_room(reach: &Reach<'_>, repo: &str) -> Result<Room, Refusal> {
let handle = reach.code_room()?;
let scope = if repo.is_empty() { Scope::current() } else { Scope::repo(repo, None) };
handle.room(&scope.with_universe(true))
}
/// What [`reach_classes`] says when the room holds no system call node.
pub(crate) const NO_BOUNDARY: &str =
"no system call node in the room: the boundary (`mimir_ingest::boundary::ensure`) has not run for this lake yet — it runs once per toolchain / target after a consume";
/// Does the lake hold ANY `syscall` node — answered by a targeted scan of
/// the node streams, never a fold. `data_reach` opens the ring room (the
/// dependency universe folded in) only for its `syscalls` section; a lake
/// the boundary never ran on folded 8.2 M ring nodes in 951 s (lake copy,
/// 2026-10-19) to answer "no boundary". `false` is exact — no row of kind
/// `syscall` in any node stream means the room's syscall mask is empty
/// (symbols carry no kind) — while any row, or any error, reads `true` and
/// the fold proceeds exactly as before.
pub fn boundary_in_lake(reach: &Reach<'_>) -> bool {
let Ok(handle) = reach.code_room() else { return true };
let Ok(log) = handle.log() else { return true };
let filter = skade::ScanFilter::is_in("kind", [nk::SYSCALL]);
// on a thread of its own, on the lake's runtime: callable from any thread
std::thread::scope(|s| {
s.spawn(|| {
log.block_on(async {
for kind in [mimir_ingest::Kind::Nodes, mimir_ingest::Kind::BinNodes] {
match log.scan_rows_where(kind, &filter, &[]).await {
Ok(rows) => {
if rows.batches.iter().any(|b| b.num_rows() > 0) {
return true;
}
}
Err(_) => return true,
}
}
false
})
})
.join()
.unwrap_or(true)
})
}
/// One function of ours that reaches a class, with one shortest path.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct Row {
pub class: String,
/// Our function, by its readable path.
pub function: String,
pub repo: String,
#[serde(rename = "crate")]
pub krate: String,
/// `file:line` of the function.
pub at: Option<String>,
/// The system call the shortest path ends in.
pub syscall: String,
/// The path, our function first, the system call last (readable names).
pub path: Vec<String>,
pub hops: usize,
/// A hop to the system call is uncertain (the C function MAY make it).
pub uncertain: bool,
/// The kernel function the call enters (from the kernel's BTF), when read.
pub kernel_entry: Option<String>,
/// The function itself leaves our code toward the call (its next hop is a
/// dependency): where our code crosses into the boundary.
pub border: bool,
}
/// One class of system call, over the request's functions.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ClassReach {
pub class: String,
/// How many of our functions can reach a call of this class.
pub functions: usize,
/// The calls of the class our code can reach.
pub syscalls: Vec<String>,
/// Functions shown, border crossings first.
pub from: Vec<Row>,
}
/// The boundary of one request.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
pub struct Reached {
pub classes: Vec<ClassReach>,
/// Our functions that reach no system call at all (with ones that do: the
/// share of pure code).
pub functions_seen: usize,
pub functions_reaching: usize,
/// `extern "C"` items declared in our own code the C map has no answer for.
pub unmapped_own_ffi: Vec<String>,
}
fn target() -> Target {
Target::host().unwrap_or_else(|| Target { triple: "x86_64-unknown-linux-gnu".into(), arch: "x86_64", libc: Libc::Glibc })
}
fn readable(room: &Room, o: u64) -> String {
let n = room.node(o);
if n.kind == Some(nk::SYSCALL) {
let name = n.name.unwrap_or("?");
let nr = boundary::syscall(&target_arch(room, o), name).map(|s| format!(" #{}", s.nr)).unwrap_or_default();
return format!("syscall {name}{nr} ({})", n.ty.unwrap_or("?"));
}
if n.krate == Some(moniker::KERNEL_PACKAGE) {
return format!("kernel {}", n.name.unwrap_or("?"));
}
room.display_path(o)
}
fn target_arch(room: &Room, o: u64) -> String {
moniker::parse(room.moniker(o)).and_then(|(_, _, d)| d.split('/').next().map(str::to_string)).unwrap_or_default()
}
fn mask_of(room: &Room, v: &BTreeSet<u64>) -> Mask {
room.mask_of(&v.iter().copied().collect::<Vec<_>>())
}
/// What our code reaches at the system boundary: per class (`only` = the
/// classes asked for, empty = all), the functions and one path each.
pub fn reach_classes(room: &Room, repos: &[String], only: &[String], per_class: usize, basis: &mut Basis) -> Result<Reached, Refusal> {
let root = format!("code room {} (dependencies folded in)", room.scope());
let sys = room.mask(&MaskSpec { node_kinds: vec![nk::SYSCALL.into()], ..Default::default() }).map_err(internal)?.value;
basis.fact("code_nodes:syscall", sys.len(), root.clone());
if sys.is_empty() {
basis.gap(NO_BOUNDARY);
return Ok(Reached::default());
}
let mut own: Vec<u64> = Vec::new();
for k in [nk::FN, nk::METHOD] {
let spec = MaskSpec { node_kinds: vec![k.into()], origins: vec![Origin::Local], defined_only: true, repos: repos.to_vec(), ..Default::default() };
own.extend_from_slice(room.mask(&spec).map_err(internal)?.value.ordinals());
}
own.sort_unstable();
own.dedup();
let own_set: BTreeSet<u64> = own.iter().copied().collect();
basis.fact("code_nodes:fn+method (own)", own.len(), root.clone());
basis.lower_bound(CALLS_LOWER_BOUND);
basis.lower_bound("a std / dependency crate dumped by a driver older than the fn-as-value CALLS edge (`each_addr(addr, TcpStream::connect)`) misses those hops until it is dumped again");
// the classes of the calls, and our own `extern "C"` items through the C map
let tg = target();
let mut targets: BTreeMap<Class, BTreeSet<u64>> = BTreeMap::new();
let mut call_of_ffi: BTreeMap<u64, (String, bool)> = BTreeMap::new();
for &o in sys.ordinals() {
let c = room.node(o).ty.and_then(Class::parse).unwrap_or(Class::System);
targets.entry(c).or_default().insert(o);
}
let own_ffi = room.mask(&MaskSpec { node_kinds: vec![nk::FFI.into()], origins: vec![Origin::Local], defined_only: true, ..Default::default() }).map_err(internal)?.value;
let mut unmapped = BTreeSet::new();
let is_call = |n: &str| boundary::syscall(tg.arch, n).is_some();
for &o in own_ffi.ordinals() {
let n = room.node(o);
let symbol = room.attr(o, "link_name").and_then(|v| v.as_str().map(str::to_string)).or_else(|| n.name.map(str::to_string)).unwrap_or_default();
match boundary::libc_map::lookup(&symbol, tg.libc, &is_call) {
Some(Mapped::Calls(calls)) => {
for (c, sure) in calls {
targets.entry(boundary::class_of(c)).or_default().insert(o);
call_of_ffi.entry(o).or_insert((c.to_string(), sure));
}
}
Some(Mapped::Pure) => {}
None => {
unmapped.insert(symbol);
}
}
}
let mut out = Reached { functions_seen: own.len(), unmapped_own_ffi: unmapped.into_iter().collect(), ..Default::default() };
// the class reach of our code: the kept walk advanced by the room's
// delta (`reach_inc`), or — the reference, the zero-loss oracle — the
// original walks over the whole room
let reaching = if crate::mimir_lens::reach_inc::reference() {
classes_reference(room, &own, &own_set, &targets, &call_of_ffi, only, per_class, &mut out)?
} else {
classes_kept(room, repos, &own, &targets, &call_of_ffi, only, per_class, &mut out)?
};
out.functions_reaching = reaching;
out.classes.sort_by(|a, b| b.functions.cmp(&a.functions).then(a.class.cmp(&b.class)));
if !out.unmapped_own_ffi.is_empty() {
basis.note(format!("{} `extern \"C\"` item(s) of our own code link a C symbol the boundary map has no answer for: {}", out.unmapped_own_ffi.len(), out.unmapped_own_ffi.iter().take(12).cloned().collect::<Vec<_>>().join(", ")));
}
Ok(out)
}
/// The REFERENCE class reach (the zero-loss oracle of the kept walk): ONE
/// forward walk from our functions, then per class one backward walk from
/// the targets reached, the border edges and the rows. Returns how many of
/// our functions reach an asked class.
#[allow(clippy::too_many_arguments)]
fn classes_reference(
room: &Room,
own: &[u64],
own_set: &BTreeSet<u64>,
targets: &BTreeMap<Class, BTreeSet<u64>>,
call_of_ffi: &BTreeMap<u64, (String, bool)>,
only: &[String],
per_class: usize,
out: &mut Reached,
) -> Result<usize, Refusal> {
// what our code reaches at all: ONE forward walk
let fwd: BTreeSet<u64> = room.reach(own, &WALK, None, None).map_err(internal)?.value.reached.into_iter().collect();
let mut reaching: BTreeSet<u64> = BTreeSet::new();
for (class, ts) in targets {
if !only.is_empty() && !only.iter().any(|c| c == class.as_str()) {
continue;
}
let hit: BTreeSet<u64> = ts.iter().copied().filter(|t| fwd.contains(t) || own_set.contains(t)).collect();
if hit.is_empty() {
continue;
}
let tv: Vec<u64> = hit.iter().copied().collect();
let back: BTreeSet<u64> = room.dependents_by(&tv[..], &WALK, None, None).map_err(internal)?.value.reached.into_iter().collect();
let fns: BTreeSet<u64> = back.iter().copied().filter(|o| own_set.contains(o)).collect();
if fns.is_empty() {
continue;
}
reaching.extend(fns.iter().copied());
// the border: our fns whose CALLS leave our code for a node on the way
let onward: BTreeSet<u64> = back.iter().copied().chain(hit.iter().copied()).filter(|o| !own_set.contains(o)).collect();
let sel = EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&mask_of(room, &fns)), to: Some(&mask_of(room, &onward)), cap: None, heaviest_first: false };
let border: BTreeSet<u64> = room.edges(&sel).map_err(internal)?.value.edges.into_iter().map(|e| e.from).collect();
let mut order: Vec<u64> = fns.iter().copied().collect();
order.sort_by_key(|o| (!border.contains(o), room.node(*o).repo.unwrap_or("").to_string(), room.display_path(*o)));
let mut rows = Vec::new();
for &f in order.iter().take(per_class) {
if let Some(r) = row(room, f, &tv, *class, border.contains(&f), call_of_ffi)? {
rows.push(r);
}
}
let names: BTreeSet<String> = hit
.iter()
.map(|t| match call_of_ffi.get(t) {
Some((c, _)) => c.clone(),
None => room.node(*t).name.unwrap_or("?").to_string(),
})
.collect();
out.classes.push(ClassReach { class: class.as_str().into(), functions: fns.len(), syscalls: names.into_iter().collect(), from: rows });
}
Ok(reaching.len())
}
/// The class reach from the KEPT walk ([`crate::mimir_lens::reach_inc`]):
/// the same classes, counts, order and rows as [`classes_reference`] — the
/// tests hold the two equal — from per-function class masks the kept walk
/// advances over the room's delta.
#[allow(clippy::too_many_arguments)]
fn classes_kept(
room: &Room,
repos: &[String],
own: &[u64],
targets: &BTreeMap<Class, BTreeSet<u64>>,
call_of_ffi: &BTreeMap<u64, (String, bool)>,
only: &[String],
per_class: usize,
out: &mut Reached,
) -> Result<usize, Refusal> {
use crate::mimir_lens::reach_inc;
let mut tclass: BTreeMap<u64, u32> = BTreeMap::new();
for (c, ts) in targets {
for t in ts {
*tclass.entry(*t).or_default() |= reach_inc::class_bit(*c);
}
}
let scope = reach_inc::scope_key(room, repos);
let sets = reach_inc::boundary_sets(room, &scope, own, &tclass, call_of_ffi).map_err(internal)?;
let mut reaching: BTreeSet<u64> = BTreeSet::new();
// display paths, once per function across the classes
let mut shown: BTreeMap<usize, String> = BTreeMap::new();
for (class, ts) in targets {
if !only.is_empty() && !only.iter().any(|c| c == class.as_str()) {
continue;
}
let bit = reach_inc::class_bit(*class);
let hit: BTreeSet<u64> = ts.iter().copied().filter(|t| sets.hit.contains(t)).collect();
if hit.is_empty() {
continue;
}
let tv: Vec<u64> = hit.iter().copied().collect();
let fns: Vec<usize> = (0..own.len()).filter(|&i| sets.cm_own[i] & bit != 0).collect();
if fns.is_empty() {
continue;
}
reaching.extend(fns.iter().map(|&i| own[i]));
// the reference's order — border first, then repo, then the readable
// path, ties by ordinal (its stable sort over ascending ordinals) —
// with the paths read only for the groups the rows take from
let mut groups: BTreeMap<(bool, String), Vec<usize>> = BTreeMap::new();
for &i in &fns {
groups.entry((sets.border[i] & bit == 0, room.node(own[i]).repo.unwrap_or("").to_string())).or_default().push(i);
}
let mut order: Vec<usize> = Vec::new();
for (_, mut g) in groups {
if order.len() >= per_class {
break;
}
for &i in &g {
shown.entry(i).or_insert_with(|| room.display_path(own[i]));
}
g.sort_by(|a, b| shown[a].cmp(&shown[b]).then(own[*a].cmp(&own[*b])));
order.extend(g);
}
let mut rows = Vec::new();
for &i in order.iter().take(per_class) {
if let Some(r) = row(room, own[i], &tv, *class, sets.border[i] & bit != 0, call_of_ffi)? {
rows.push(r);
}
}
let names: BTreeSet<String> = hit
.iter()
.map(|t| match call_of_ffi.get(t) {
Some((c, _)) => c.clone(),
None => room.node(*t).name.unwrap_or("?").to_string(),
})
.collect();
out.classes.push(ClassReach { class: class.as_str().into(), functions: fns.len(), syscalls: names.into_iter().collect(), from: rows });
}
Ok(reaching.len())
}
/// One row: the shortest path from `f` to a target of the class.
fn row(room: &Room, f: u64, targets: &[u64], class: Class, border: bool, call_of_ffi: &BTreeMap<u64, (String, bool)>) -> Result<Option<Row>, Refusal> {
let Some(p) = room.path(&[f][..], targets, &WALK, None, None).map_err(internal)?.value else { return Ok(None) };
let last = *p.nodes.last().expect("a path has an end");
let mut path: Vec<String> = p.nodes.iter().map(|o| readable(room, *o)).collect();
let mut uncertain = false;
// the SYSCALLS hop's certainty
if p.nodes.len() >= 2 && p.kinds.last() == Some(&EdgeKind::Syscalls) {
let prev = p.nodes[p.nodes.len() - 2];
let sel = EdgeSel { kinds: vec![EdgeKind::Syscalls], from: Some(&room.mask_of(&[prev])), to: Some(&room.mask_of(&[last])), cap: None, heaviest_first: false };
uncertain = room.edges(&sel).map_err(internal)?.value.edges.iter().any(|e| e.partial);
}
// our own `extern "C"` item: the C map names the call
let syscall = match call_of_ffi.get(&last) {
Some((c, sure)) => {
uncertain |= !sure;
path.push(format!("syscall {c} (C map, {})", boundary::class_of(c)));
c.clone()
}
None => room.node(last).name.unwrap_or("?").to_string(),
};
let kernel_entry = if room.node(last).kind == Some(nk::SYSCALL) {
let sel = EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&room.mask_of(&[last])), to: None, cap: Some(1), heaviest_first: false };
room.edges(&sel).map_err(internal)?.value.edges.first().map(|e| room.node(e.to).name.unwrap_or("?").to_string())
} else {
None
};
let n = room.node(f);
Ok(Some(Row {
class: class.as_str().into(),
function: room.display_path(f),
repo: n.repo.unwrap_or("").into(),
krate: n.krate.unwrap_or("").into(),
at: n.file.map(|file| format!("{file}:{}", n.start_line.unwrap_or(0))),
syscall,
hops: p.hops(),
path,
uncertain,
kernel_entry,
border,
}))
}
/// The class list a request asks for (`class` = `network` or `network,file`).
pub fn classes_asked(q: &Query<'_>) -> Result<Vec<String>, Refusal> {
let raw = q.str("class");
let asked: Vec<String> = raw.split(',').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect();
if let Some(bad) = asked.iter().find(|c| Class::parse(c).is_none()) {
let known: Vec<&str> = Class::ALL.iter().map(|c| c.as_str()).collect();
return Err(Refusal::new(State::InvalidArgument, format!("`{bad}` is not a system call class (known: {})", known.join(", "))));
}
Ok(asked)
}
/// `system_boundary` — per repo: the classes of system call it can reach and
/// from where (border functions first, each with a shortest path).
pub fn system_boundary(reach: &Reach<'_>, _wh: &super::Warehouse<'_>, q: &Query<'_>) -> Result<(Basis, Value), Refusal> {
let mut basis = Basis::default();
let only = classes_asked(q)?;
let g = super::graph::Graph::open(reach, q, &mut basis)?;
let per_class = q.limit.clamp(1, ROWS_PER_CLASS);
let r = reach_classes(&g.room, &g.repos, &only, per_class, &mut basis)?;
let worst: Vec<&Row> = r.classes.iter().flat_map(|c| c.from.iter()).take(q.limit).collect();
let summary: Vec<String> = r.classes.iter().map(|c| format!("{} {} fn(s)", c.class, c.functions)).collect();
let note = if r.classes.is_empty() {
"system_boundary: no function of the request reaches a system call in this room.".to_string()
} else {
format!("system_boundary: {} of {} function(s) reach the system boundary — {}.", r.functions_reaching, r.functions_seen, summary.join(", "))
};
Ok((
basis,
json!({
"kind": "system_boundary",
"classes": r.classes,
"functions_seen": r.functions_seen,
"functions_reaching": r.functions_reaching,
"unmapped_own_ffi": r.unmapped_own_ffi,
"worst": worst,
"evidence_note": note,
"model_attribution": "Composition of code-room facts with the dependency universe folded in: CALLS from our code through std and the dependencies × SYSCALLS (C function → system call, through the curated glibc / musl map; the raw door) × the kernel's syscall table (x86_64) and, when read, its BTF (the entry function). Uncertain hops are marked.",
}),
))
}
/// Severity order of the classes when ONE path is shown for a crate.
const SHOWN_FIRST: [Class; 10] =
[Class::Network, Class::Process, Class::File, Class::System, Class::Ipc, Class::Signal, Class::Memory, Class::Event, Class::Time, Class::Random];
/// What the code of `krate@version` that `repo`'s own functions call can
/// reach at the system boundary: the classes, and one shortest path (our
/// function → into the crate → … → the call) for the most severe class.
/// `None` = our code calls nothing of that crate in this room.
pub fn reach_of_crate(room: &Room, repo: &str, krate: &str, version: &str) -> Result<Option<Value>, Refusal> {
let mut own: Vec<u64> = Vec::new();
for k in [nk::FN, nk::METHOD] {
let spec = MaskSpec { node_kinds: vec![k.into()], origins: vec![Origin::Local], defined_only: true, repos: vec![repo.to_string()], ..Default::default() };
own.extend_from_slice(room.mask(&spec).map_err(internal)?.value.ordinals());
}
let names: BTreeSet<String> = [krate.to_string(), krate.replace('-', "_")].into();
let spec = MaskSpec { crates: names.iter().map(|n| format!("{n}@{version}")).collect(), ..Default::default() };
let theirs = room.mask(&spec).map_err(internal)?.value;
if own.is_empty() || theirs.is_empty() {
return Ok(None);
}
let own_mask = room.mask_of(&own);
let sel = EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&own_mask), to: Some(&theirs), cap: None, heaviest_first: false };
let calls = room.edges(&sel).map_err(internal)?.value.edges;
let entries: BTreeSet<u64> = calls.iter().map(|e| e.to).collect();
if entries.is_empty() {
return Ok(None);
}
let ev: Vec<u64> = entries.iter().copied().collect();
let reached = room.reach(&ev[..], &WALK, None, None).map_err(internal)?.value.reached;
let mut by_class: BTreeMap<Class, Vec<u64>> = BTreeMap::new();
for o in reached {
let n = room.node(o);
if n.kind == Some(nk::SYSCALL) {
by_class.entry(n.ty.and_then(Class::parse).unwrap_or(Class::System)).or_default().push(o);
}
}
let classes: Vec<&str> = by_class.keys().map(|c| c.as_str()).collect();
let shown = SHOWN_FIRST.iter().chain([Class::Indirect].iter()).find(|c| by_class.contains_key(c));
let mut path = Vec::new();
if let Some(c) = shown
&& let Some(p) = room.path(&ev[..], &by_class[c][..], &WALK, None, None).map_err(internal)?.value
{
let first = p.nodes[0];
if let Some(caller) = calls.iter().find(|e| e.to == first) {
path.push(readable(room, caller.from));
}
path.extend(p.nodes.iter().map(|o| readable(room, *o)));
}
Ok(Some(json!({
"classes": classes,
"syscalls": by_class.values().map(Vec::len).sum::<usize>(),
"entries_called": entries.len(),
"path": path,
})))
}
/// The kept per-finding walks of one `reachable_vuln` ask
/// ([`crate::mimir_lens::reach_inc`]): opened before the findings, closed
/// (kept) after them.
pub struct KeptFindings {
local: std::collections::HashSet<u64>,
path: String,
rows: std::collections::HashMap<(String, String, String), crate::mimir_lens::reach_inc::KeptRow>,
total: usize,
rewalked: usize,
}
impl KeptFindings {
pub fn open(room: &Room, scope: &str) -> anyhow::Result<Self> {
let (kept, local, path) = crate::mimir_lens::reach_inc::vuln_kept(room, scope)?;
let rows = kept.map(|k| k.rows.clone()).unwrap_or_default();
Ok(Self { local, path, rows, total: 0, rewalked: 0 })
}
pub fn close(self, room: &Room, scope: &str) -> anyhow::Result<()> {
use crate::mimir_lens::reach_inc;
let path = if self.path.starts_with("full") { self.path } else if self.rewalked == 0 { "carried".to_string() } else { "advanced".to_string() };
reach_inc::vuln_keep(scope, reach_inc::KeptVuln { universe: room.universe_key().unwrap_or(0), local: self.local, rows: self.rows });
reach_inc::vuln_record(path, self.total, self.rewalked);
Ok(())
}
}
/// [`reach_of_crate`] over the kept walks: a finding whose entries are the
/// same items and whose walk met no local node (the universe half, carried
/// while the universe holds) answers from the kept walk, with the caller
/// prefix of its path read from the current calls; any other is walked.
pub fn reach_of_crate_kept(room: &Room, repo: &str, krate: &str, version: &str, k: &mut KeptFindings) -> Result<Option<Value>, Refusal> {
use crate::mimir_lens::reach_inc::{KeptRow, key_of};
let mut own: Vec<u64> = Vec::new();
for kd in [nk::FN, nk::METHOD] {
let spec = MaskSpec { node_kinds: vec![kd.into()], origins: vec![Origin::Local], defined_only: true, repos: vec![repo.to_string()], ..Default::default() };
own.extend_from_slice(room.mask(&spec).map_err(internal)?.value.ordinals());
}
let names: BTreeSet<String> = [krate.to_string(), krate.replace('-', "_")].into();
let spec = MaskSpec { crates: names.iter().map(|n| format!("{n}@{version}")).collect(), ..Default::default() };
let theirs = room.mask(&spec).map_err(internal)?.value;
if own.is_empty() || theirs.is_empty() {
return Ok(None);
}
let own_mask = room.mask_of(&own);
let sel = EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&own_mask), to: Some(&theirs), cap: None, heaviest_first: false };
let calls = room.edges(&sel).map_err(internal)?.value.edges;
let entries: BTreeSet<u64> = calls.iter().map(|e| e.to).collect();
if entries.is_empty() {
return Ok(None);
}
k.total += 1;
let mut ekeys: Vec<u64> = entries.iter().map(|o| key_of(room.moniker(*o))).collect();
ekeys.sort_unstable();
let id = (repo.to_string(), krate.to_string(), version.to_string());
if let Some(r) = k.rows.get(&id)
&& r.pure
&& r.entries == ekeys
&& !ekeys.iter().any(|e| k.local.contains(e))
{
let mut v = r.value.clone();
if let Some(first) = r.first.as_deref().and_then(|m| room.ordinal(m))
&& let Some(caller) = calls.iter().find(|e| e.to == first)
&& let Some(p) = v["path"].as_array_mut()
{
p.insert(0, Value::String(readable(room, caller.from)));
}
return Ok(Some(v));
}
k.rewalked += 1;
let ev: Vec<u64> = entries.iter().copied().collect();
let reached = room.reach(&ev[..], &WALK, None, None).map_err(internal)?.value.reached;
let pure = !ev.iter().chain(reached.iter()).any(|o| k.local.contains(&key_of(room.moniker(*o))));
let mut by_class: BTreeMap<Class, Vec<u64>> = BTreeMap::new();
for o in reached {
let n = room.node(o);
if n.kind == Some(nk::SYSCALL) {
by_class.entry(n.ty.and_then(Class::parse).unwrap_or(Class::System)).or_default().push(o);
}
}
let classes: Vec<&str> = by_class.keys().map(|c| c.as_str()).collect();
let shown = SHOWN_FIRST.iter().chain([Class::Indirect].iter()).find(|c| by_class.contains_key(c));
let mut tail = Vec::new();
let mut prefix = None;
let mut first = None;
if let Some(c) = shown
&& let Some(p) = room.path(&ev[..], &by_class[c][..], &WALK, None, None).map_err(internal)?.value
{
let f = p.nodes[0];
first = Some(room.moniker(f).to_string());
if let Some(caller) = calls.iter().find(|e| e.to == f) {
prefix = Some(readable(room, caller.from));
}
tail.extend(p.nodes.iter().map(|o| readable(room, *o)));
}
let value = json!({
"classes": classes,
"syscalls": by_class.values().map(Vec::len).sum::<usize>(),
"entries_called": entries.len(),
"path": tail,
});
k.rows.insert(id, KeptRow { entries: ekeys, value: value.clone(), first, pure });
let mut v = value;
if let (Some(pre), Some(p)) = (prefix, v["path"].as_array_mut()) {
p.insert(0, Value::String(pre));
}
Ok(Some(v))
}