fix(pipeline): guard USAGE/WRITES/READS against cross-language binds - #1937
fix(pipeline): guard USAGE/WRITES/READS against cross-language binds#1937ilyabrykau-orca wants to merge 1 commit into
Conversation
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
USAGE, WRITES and READS edges resolve through the same short-name registry as CALLS but never consulted the DeusData#725 cross-language guard. On a Go tree with eBPF C probes every Go identifier spelled like a C one produced a reference edge into the C file: 31.5% of all WRITES on the originally measured repo crossed the Go->C boundary, led by dozens of Go test locals named event writing a C probe's automatic variable. Add cbm_suppress_cross_language_ref() - the reference-edge analog of cbm_suppress_cross_language_suffix_match - and consult it on BOTH resolvers of each edge type: the sequential pass (pass_usages.c resolve_usage_edges registry-fallback branch, resolve_rw_edges) and their parallel twins (pass_parallel.c resolve_file_usages, resolve_file_rw). The sequential-only version of this change left 344 Go->C WRITES alive on a ~1150-file repo because large repos resolve through pass_parallel.c - the field census caught it, and the parallel-twin test now pins it. Unlike the CALLS guard the predicate takes no strategy parameter: a reference edge carries no import-closure evidence, so every registry strategy is a bare-name guess across a boundary. LSP-backed semantic references resolve before the fallback and are unaffected. JS/TS stay one family, and C/C++ count as one family too (.h maps to CBM_LANG_CPP, so a .c file referencing its own header is not a boundary). Field-validated on the ~1150-file Go+C repo: Go->C/C++ WRITES 835->0, USAGE 1545->0; C->Go 15/90->0; 6432 reference edges dropped in total, every one cross-language (Go->.json 3034, Go->.hpp/.h 1612, Go->.c 746, Go->.sh/.yaml/.yml 443, ...) and none same-language. CALLS and IMPORTS totals are byte-identical to main. Fixes DeusData#1928 Signed-off-by: Ilya Brykau <ilya.brykau@orca.security>
|
Amended (force-push): the field census caught the sequential-only version leaving 344 Go→C WRITES / ~1200 USAGE alive on the ~1150-file repo — USAGE and WRITES/READS each have a second, independent resolver in Fresh census (this branch vs main, same tree): Go→C/C++ WRITES 835 → 0, USAGE 1545 → 0, C→Go 15/90 → 0; 6432 reference edges dropped in total, every one cross-language (Go→ |
532df00 to
d6417ad
Compare
Fixes #1928.
What
USAGE,WRITESandREADSedges resolve through the same short-name registry as CALLS but never consulted the #725 cross-language guard. On a Go tree with eBPF C probes, every Go identifier spelled like a C one produced a reference edge into the C file: 31.5% of all WRITES on the measured repo (866 of 2793) crossed the Go→C boundary — dozens of Go test locals namedevent"writing" a C probe's automatic variable. Go code cannot touch a C translation unit's identifiers; every edge in that class is false.How
New pure predicate
cbm_suppress_cross_language_ref(caller_lang, target_file_path)inregistry.c, next to (and reusing the language machinery of)cbm_suppress_cross_language_suffix_match, consulted on:resolve_usage_edges(USAGE), andresolve_rw_edges(WRITES/READS), which now receives the file's language.Two deliberate differences from the CALLS guard:
unique_namecarve-out that CALLS keeps (Python stdlib import binds to a same-named TypeScript function (cross-language, strategy=unique_name) — poisons hotspots and Louvain clusters #1572) does not apply here. LSP-backed semantic references resolve before the fallback and never reach the predicate (that path is where a genuine cgo binding would live, feat(extract): cgo is invisible — the C preamble inside a .go file is never parsed, and C.f() calls produce no edges #1929)..hmaps toCBM_LANG_CPPin the extension table, so a.cfile referencing its own header's declarations must not read as a boundary. (JS/TS/TSX/ArkTS stay one family, as in get_architecture hotspots report inflated fan_in that contradicts actual graph in-degree (same-name collision across languages) #725.)Measured expectation
From the issue: Go→C/C++ WRITES 866 → ~0, USAGE 1596 → ~0 (plus the C→Go reverse direction, 15 + 90). This also unblocks #1935 — landing ~4600 Go
Fieldnodes without this guard would mint ~5700 WRITES / ~22000 USAGE name-collision edges ontoerr/ctx/_fields.Tests
pipeline_go_rw_usage_never_cross_into_c(Go + C fixture). Both paths proven RED independently on main: firstFAIL … ASSERT(!(cross_file_edge_exists(s, project, "UsesHandle", "handle", "USAGE"))), then — with only the USAGE branch guarded —FAIL … ASSERT(!(cross_file_edge_exists(s, project, "WriteTotal", "total_events", "WRITES"))). GREEN with both wired. Same-language control (Bump→CounterWRITES) survives.cross_language_ref_drops_go_vs_cnext to the get_architecture hotspots report inflated fan_in that contradicts actual graph in-degree (same-name collision across languages) #725 unit test: Go↔C/C++/header drops both directions, same-language keeps, C↔.h keeps, JS↔TS keeps, unknown-language/NULL/empty keeps.cross_file_edge_exists(…, edge_type);cross_file_call_existsis now a one-line wrapper.scripts/test.shvenue leg (ASan+UBSan, all suites + contract steps): green.git clang-format --diff: clean.Base: upstream
maindirectly (independent of the #1907/#1913/#1915 stack). #1932 tracks the family.