mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-15 20:05:13 +02:00
Performance and precision pass (#64)
This commit is contained in:
parent
c7c5e0f3a1
commit
fb698d2c27
97 changed files with 9932 additions and 517 deletions
|
|
@ -463,6 +463,56 @@ fn sink_args_typed_safe(ctx: &AnalysisContext, sink: NodeIndex, sink_caps: Cap)
|
|||
type_facts_suppress(&values, sink_caps, type_facts)
|
||||
}
|
||||
|
||||
/// Suppress a `cfg-unguarded-sink` SQL_QUERY finding when any positional
|
||||
/// argument to the sink Call is provably a JPA / Hibernate Criteria query
|
||||
/// object ([`crate::ssa::type_facts::TypeKind::JpaCriteriaQuery`]).
|
||||
///
|
||||
/// Receiver values are deliberately excluded, the receiver of a JPA
|
||||
/// query method (`session.createQuery(cq)`, `em.createQuery(cq)`,
|
||||
/// `session.executeUpdate(cq)`) is the connection / EntityManager
|
||||
/// channel, never the SQL payload. Including the receiver in the type
|
||||
/// check would make this suppression unreachable since `Session` /
|
||||
/// `EntityManager` values are typed `Object` / `Unknown` and never
|
||||
/// `JpaCriteriaQuery` themselves.
|
||||
///
|
||||
/// Closes the dominant FP cluster across openmrs (169 of 216
|
||||
/// cfg-unguarded-sink), xwiki, and keycloak: Hibernate DAO methods
|
||||
/// build a `CriteriaQuery<Foo>` via `cb.createQuery(Foo.class)` +
|
||||
/// `Root` / `Predicate` API, then hand the query object to
|
||||
/// `session.createQuery(cq)` for execution. No string concatenation
|
||||
/// happens, JPA emits parameterized SQL by construction.
|
||||
fn sink_args_jpa_criteria_query_safe(
|
||||
ctx: &AnalysisContext,
|
||||
sink: NodeIndex,
|
||||
sink_caps: Cap,
|
||||
) -> bool {
|
||||
if !sink_caps.intersects(Cap::SQL_QUERY) {
|
||||
return false;
|
||||
}
|
||||
let Some(facts) = ctx.body_const_facts else {
|
||||
return false;
|
||||
};
|
||||
let Some(type_facts) = ctx.type_facts else {
|
||||
return false;
|
||||
};
|
||||
let Some(&sink_val) = facts.ssa.cfg_node_map.get(&sink) else {
|
||||
return false;
|
||||
};
|
||||
let Some(inst) = find_inst(&facts.ssa, sink_val) else {
|
||||
return false;
|
||||
};
|
||||
let SsaOp::Call { args, .. } = &inst.op else {
|
||||
return false;
|
||||
};
|
||||
let mut values: Vec<SsaValue> = Vec::new();
|
||||
for group in args {
|
||||
for v in group.iter() {
|
||||
values.push(*v);
|
||||
}
|
||||
}
|
||||
crate::ssa::type_facts::is_safe_query_object_arg(&values, sink_caps, type_facts)
|
||||
}
|
||||
|
||||
/// Walk the sink's Call SSA arguments and check whether every real argument
|
||||
/// resolves through a defining `SsaOp::Call` whose callee carries an SSA
|
||||
/// summary with `validated_params_to_return` covering every propagating
|
||||
|
|
@ -1210,6 +1260,17 @@ impl CfgAnalysis for UnguardedSink {
|
|||
continue;
|
||||
}
|
||||
|
||||
// JPA / Hibernate Criteria-query suppression: receiver-call SQL
|
||||
// sinks like `session.createQuery(cq)` / `em.executeUpdate(cq)`
|
||||
// are safe by construction when arg 0 is a structural Criteria
|
||||
// object built via `CriteriaBuilder` (returns parameterized
|
||||
// SQL). Receiver excluded from the check, the receiver is
|
||||
// never the payload. Closes openmrs / xwiki / keycloak
|
||||
// Hibernate-DAO FP cluster.
|
||||
if !has_taint && sink_args_jpa_criteria_query_safe(ctx, *sink, sink_caps) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Static-map suppression: the SSA value flowing into the sink is
|
||||
// proved by the static-HashMap-lookup idiom detector to be a
|
||||
// finite set of literals free of shell metacharacters. Mirrors
|
||||
|
|
|
|||
|
|
@ -88,7 +88,21 @@ pub struct BodyConstFacts {
|
|||
/// Lower a body to SSA and run constant propagation. Returns `None` when
|
||||
/// lowering fails (empty CFG, invalid entry), callers treat absence as
|
||||
/// "no SSA facts available" and fall back to the syntactic path.
|
||||
/// Perf-regression sentinel: total cumulative calls to
|
||||
/// [`build_body_const_facts`] across the process lifetime.
|
||||
///
|
||||
/// Used by the `analyse_file_fused_large_go` criterion bench in
|
||||
/// `benches/scan_bench.rs` to assert the per-file
|
||||
/// [`crate::ast`]`::ParsedFile::body_const_facts_cache` is collapsing the
|
||||
/// per-body re-lowering (~149 calls per file expected; pre-cache was ~447).
|
||||
/// The atomic increment is ~1 ns per call and disappears in the noise of
|
||||
/// the multi-millisecond SSA lowering it gates.
|
||||
#[doc(hidden)]
|
||||
pub static BUILD_BODY_CONST_FACTS_CALLS: std::sync::atomic::AtomicU64 =
|
||||
std::sync::atomic::AtomicU64::new(0);
|
||||
|
||||
pub fn build_body_const_facts(body: &crate::cfg::BodyCfg, lang: Lang) -> Option<BodyConstFacts> {
|
||||
BUILD_BODY_CONST_FACTS_CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let mut ssa = crate::ssa::lower_to_ssa_with_params(
|
||||
&body.graph,
|
||||
body.entry,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue