Juliet Benchmark History
This page is the detailed, round-by-round engineering history behind SqC’s
Juliet Test Suite results: every FP-reduction round, per-CWE tier breakdowns,
the Round 1 baseline table, competitor comparisons, and the full version
history. It exists so the granular “why did this number change” narrative has
a home without bloating the top-level JULIET_RESULTS.md, which tracks only
current state.
See JULIET_RESULTS.md (repo root) for the current run’s numbers, and
Testing Methodology for how the benchmark itself works (ground truth
classification, manifests, metrics).
Benchmark Environment
Runtime varies significantly by machine and parallelism. Historical runs below were captured on a mix of hardware; always record machine/job count when reporting new results.
Run |
Machine |
Cores |
Jobs |
Total Runtime |
Notes |
|---|---|---|---|---|---|
v0.2.1 – v0.2.25 (full suite) |
24-core workstation |
24 |
12 |
~40–50 min |
MCP benchmark server, full 118 CWE suite |
v0.3.8 – v0.3.9 (12-CWE subset) |
4-core laptop |
4 |
4 |
TBD |
|
The parallel runner (run_juliet_parallel.sh) defaults to JOBS=12.
Higher core counts reduce wall-clock time but don’t affect per-CWE results.
When comparing runtimes across versions, ensure the same machine and job count
were used.
FP Reduction History (Full Round List)
Every tracked round from the original baseline through the last full-suite
run (v0.3.17). testing-methodology.rst carries a condensed milestone
version of this table; this is the complete list.
Round |
Version |
Fixes |
TP |
FP |
TP Rate |
FP Delta |
|---|---|---|---|---|---|---|
Baseline |
v0.2.1 |
– |
586,539 |
839,341 |
41.1% |
– |
Round 1 |
INT08-C, CON08-C, DCL20-C, ARR38-C |
552,645 |
752,422 |
42.3% |
-86,919 |
|
Round 2 |
EXP33-C, SIG31-C, ARR01-C, DCL30-C, DCL02-C |
555,700 |
736,563 |
43.0% |
-15,859 |
|
Round 3 |
DCL31-C, DCL07-C, FLP34-C |
402,013 |
537,589 |
42.8% |
-198,974 |
|
Round 4 |
EXP12-C, FLP03-C, INT32-C |
363,914 |
492,648 |
42.5% |
-44,941 |
|
Round 5 |
FLP02-C, DCL06-C, INT30-C |
340,894 |
475,813 |
41.7% |
-16,835 |
|
Round 6 |
Cross-file analysis ( |
247,757 |
327,191 |
43.1% |
-148,622 |
|
Round 7 |
EXP36-C, EXP34-C, ARR37-C |
231,053 |
301,475 |
43.4% |
-25,716 |
|
Round 8 |
DCL40-C, FLP32-C, ERR33-C |
230,992 |
296,415 |
43.8% |
-5,060 |
|
Round 9 |
CFG, data-flow, inter-procedural |
230,643 |
296,342 |
43.8% |
-73 |
|
Round 11 |
DCL07-C/DCL31-C: ALL_CAPS + POSIX std_functions |
207,800 |
272,782 |
43.2% |
-23,560 |
|
Round 12 |
v0.2.4 |
INT07-C, INT32-C, EXP10-C, EXP34-C, INT30-C, MEM10-C, STR31-C, Windows API |
189,950 |
243,849 |
43.8% |
-28,933 |
Round 13 |
STR31-C: L-prefix + literal-source suppression |
189,016 |
239,724 |
44.1% |
-4,125 |
|
Round 15 |
EXP34-C: if/else branch merge (variant 12) — measured by MCP server, not directly comparable to legacy runner counts above |
n/a |
n/a |
44.7% |
n/a |
|
Round 16 |
v0.2.6 |
DCL13-C main() + MEM10-C param-only null check — same MCP-server caveat as Round 15 |
n/a |
n/a |
44.8% |
n/a |
– |
v0.2.7 |
INT36-C TP restore + INT31-C FP fix |
172,780 |
215,671 |
44.5% |
-1 |
– |
v0.2.11 |
INT32-C bounds-check detection, INT30-C macro fixes |
172,780 |
215,669 |
44.5% |
-2 |
– |
v0.2.12 |
DCL13-C pointer modification + INT01-C sizeof skip |
169,161 |
210,138 |
44.6% |
-5,531 |
– |
v0.2.13 |
INT31-C implicit narrowing + real-world FP fixes |
158,403 |
196,177 |
44.7% |
-13,961 |
– |
v0.2.15 |
Real-world FP cleanup (17 patterns) |
146,714 |
185,499 |
44.2% |
-10,678 |
– |
v0.2.16 |
EXP34-C: call-site null propagation (Phase 2) |
146,733 |
185,510 |
44.2% |
+11 |
– |
v0.2.17 |
Phase 3: MEM10-C, API00-C, API02-C, prescan enhancement |
146,913 |
185,591 |
44.2% |
+81 |
– |
v0.2.18 |
INT31-C pointer cast, ARR36-C type filter, API00-C void-cast, INT30-C guards |
145,639 |
184,645 |
44.1% |
-946 |
– |
v0.2.19 |
INT30-C loop guards, prescan null guards, ARR00-C crash fix |
145,639 |
184,644 |
44.1% |
-1 |
– |
v0.2.20 |
API00-C static skip, INT01-C dedup, EXP37-C init_declarator, EXP34-C array NotNull |
144,278 |
181,924 |
44.2% |
-2,720 |
– |
v0.2.21 |
const_eval value-range analysis + real-world Round 6 FP fixes |
137,921 |
175,667 |
44.0% |
-6,257 |
– |
v0.2.22 |
INT30-C: extend upper-bound guard to if_statement |
137,921 |
175,673 |
44.0% |
+6 |
– |
v0.2.23 |
INT32-C const_eval alloc/memory/abs + INT30-C uint64_t + built-in macros |
131,661 |
163,585 |
44.6% |
-12,088 |
– |
v0.2.25 |
ARR32-C tightening, INT18-C/EXP05-C removal, value-range FP fixes |
130,199 |
161,965 |
44.6% |
-1,620 |
– |
v0.3.5 |
Struct field type resolution, DCL13-C/DCL30-C/EXP33-C FP fixes |
130,004 |
161,510 |
44.6% |
-455 |
– |
v0.3.8 |
STR31-C, API00-C, EXP34-C, EXP33-C FP fixes — 12-CWE subset (~25K files), not full suite: TP=61,799 FP=77,826 |
n/a |
n/a |
44.3% |
n/a |
– |
v0.3.9 |
DCL08-C, EXP36-C, INT36-C, INT01-C, EXP33-C FP fixes — same 12-CWE subset: TP=61,349 FP=77,245 (-450 TP / -581 FP vs v0.3.8) |
n/a |
n/a |
44.3% |
n/a |
– |
v0.3.14 |
EXP33-C for-loop init, INT30-C subtraction guard, EXP34-C param null propagation — full suite resumes here |
126,106 |
158,036 |
44.4% |
-3,474 |
– |
v0.3.17 |
CWE-78 macro alias resolution + CWE-253 incorrect-return check |
128,038 |
160,496 |
44.4% |
– |
Trend: Diminishing returns on FP reduction via rule tuning — Round 3 removed 199K FP; by Round 8 only 5K. Cumulative FP reduction from baseline through v0.3.17: -678,845 (-80.9%).
Per-Round Fix Details
v0.3.19 — Fast Benchmark Mode + CWE-78 Taint Tracking
First benchmark using fast mode (--fast): per-CWE manifests that
include only CWE-matched rules, eliminating noise from unrelated rules. 68
CWEs scanned (29 skipped — no CWE-mapped rules), 12 parallel jobs.
Fast benchmark mode: generate_rule_cwe_map.py generates 147 per-CWE TOML
manifests. run_juliet_parallel.sh --fast uses them. Noise ratio drops to
0% by design.
ENV03-C function-scoped sanitization: clearenv()/setenv("PATH")
now checked per-function instead of file-level. A clearenv() in one
function no longer suppresses violations in other functions in the same file.
STR02-C intra-function taint tracking: Replaced blanket
“non-literal = risky” check with taint analysis. Tracks recv(),
fgets(), fgetws(), scanf(), getenv(), etc. as taint sources.
Handles cast expressions like (char *)(data + offset). Function
parameters treated as tainted by default. Taint propagation through
strcpy/sprintf/memcpy.
CWE-78 impact (v0.3.17 → v0.3.19, fast mode):
Metric |
v0.3.17 |
v0.3.19 |
Delta |
|---|---|---|---|
CWE-78 TP |
1,282 |
1,204 |
-78 |
CWE-78 FP |
1,773 |
1,443 |
-330 |
CWE-78 Precision |
42.0% |
45.5% |
+3.5pp |
CWE-78 Per-file |
13.0% |
13.0% |
– |
STR02-C TP |
~278 |
200 |
-78 (cross-function) |
STR02-C FP |
~382 |
52 |
-330 (-86%) |
CWE-aware highlights (fast mode, CWE-matched rules only):
CWE |
CWE-matched TP |
CWE-matched FP |
Precision |
Per-file |
Notes |
|---|---|---|---|---|---|
252 |
179 |
0 |
100% |
16.5% |
ERR33-C |
253 |
178 |
0 |
100% |
26.0% |
ERR33-C |
690 |
290 |
62 |
82.4% |
25.9% |
EXP34-C |
197 |
259 |
126 |
67.5% |
37.4% |
INT31-C |
401 |
284 |
287 |
49.7% |
21.7% |
MEM31-C |
78 |
1,204 |
1,443 |
45.5% |
13.0% |
ENV03-C/STR02-C taint |
476 |
140 |
161 |
46.5% |
29.0% |
EXP34-C |
190 |
650 |
820 |
44.2% |
12.9% |
INT30-C/INT32-C |
194 |
447 |
626 |
41.7% |
27.0% |
INT31-C (new in v0.3.18) |
195 |
406 |
588 |
40.8% |
24.8% |
INT31-C (new in v0.3.18) |
121 |
854 |
1,317 |
39.3% |
12.8% |
STR31-C/ARR30-C |
Perfect precision (100% TP): CWE-244, CWE-252, CWE-253, CWE-338, CWE-467, CWE-481, CWE-587, CWE-590, CWE-591, CWE-681 (10 CWEs).
Zero-detection CWEs (rules mapped but 0 violations): CWE-114, CWE-188, CWE-226, CWE-259, CWE-272, CWE-273, CWE-327, CWE-367, CWE-459, CWE-464, CWE-468, CWE-469, CWE-666, CWE-672, CWE-676, CWE-761, CWE-762, CWE-789, CWE-843 (19 CWEs).
Duration: ~8 min wall time (12 jobs, 4-core laptop), 68 CWEs scanned. Fast mode is ~10x faster than full suite for CWE-focused analysis.
v0.3.17 — CWE-78 Macro Alias Resolution + CWE-253 Incorrect Return Check
Two new detection capabilities: P6 (CWE-78 macro alias resolution) and P7 (CWE-253 incorrect comparison validation).
P6: CWE-78 macro alias resolution (commit d8587d80): Added
collect_macro_aliases() to resolve #define SYSTEM system patterns.
ENV33-C, ENV03-C, STR02-C now detect dangerous function calls through macro
indirection. Also added Windows exec/spawn variants (_execl, _execv,
_spawnl, etc.) to ENV33-C and STR02-C.
P7: CWE-253 ERR33-C comparison validation (commit 83a71e54): Functions
classified by error return kind (NullPointer, NegativeInt, Eof, NonZero,
Count). When a direct call appears in a binary_expression, the comparison
operator/value is validated against function semantics. Detects:
fgets() < 0 (pointer with ordered op), fprintf() == 0 (error is < 0),
putc() == 0 (error is EOF), remove() == 0 (0 = success),
fwrite() < 0 (size_t unsigned). Includes wchar_t variants and macro alias
resolution.
Juliet impact (v0.3.14 → v0.3.17): Overall TP 126,106→128,038 (+1,932), FP 158,036→160,496 (+2,460), TP rate 44.4% → 44.4% (unchanged) — net increase from new detections (macro-resolved calls + CWE-253 patterns).
CWE-aware highlights (first run with CWE-aware scoring):
CWE |
CWE-matched TP |
CWE-matched FP |
Precision |
Per-file |
Notes |
|---|---|---|---|---|---|
253 |
178 |
0 |
100% |
26.0% |
New — P7 detection |
252 |
179 |
0 |
100% |
16.5% |
Existing ERR33-C |
690 |
290 |
62 |
82.4% |
25.9% |
EXP34-C null deref from return |
78 |
1,282 |
1,773 |
42.0% |
13.0% |
P6 macro aliases |
590 |
94 |
0 |
100% |
10.4% |
MEM31-C free-not-on-heap |
467 |
20 |
0 |
100% |
37.0% |
ARR01-C sizeof-on-pointer |
481 |
12 |
0 |
100% |
66.7% |
EXP45-C assign-vs-compare |
391 |
36 |
22 |
62.1% |
37.0% |
ERR33-C unchecked error |
CWE-aware aggregate (68 CWEs with mapped rules): CWE-matched TP rate 44.3%, per-file detection 12.6%, flaw-hit rate 4.6%, noise ratio 93.3%.
Duration: 1h 47m 15s (54,484 files, 118 CWEs), 24-core workstation via MCP benchmark server.
v0.3.14 — EXP33-C, INT30-C, EXP34-C FP Fixes (Full Suite)
First full-suite benchmark since v0.3.5 (v0.3.8 and v0.3.9 used a 12-CWE subset only). Includes all changes from v0.3.6–v0.3.14, of which v0.3.8 and v0.3.9 are documented below.
Additional fixes in v0.3.10–v0.3.14:
EXP34-C: Parameter null propagation — seed callee params with null state inferred from call-site arguments.
INT30-C: Subtraction guard — detect
a - bpatterns where a preceding comparison provesa >= band suppress the unsigned wrap violation.EXP33-C: For-loop init recognized as dominating assignment —
for (int i = 0; ...)loop variable initializer now counted as initialization before first use. Also fixed a double-push incollect_init_func_calls_for_varthat caused position deduplication failures.
Juliet impact (v0.3.5 → v0.3.14): Overall TP 130,004→126,106 (-3,898), FP 161,510→158,036 (-3,474), TP rate 44.6% → 44.4% (-0.2pp). CWE categories with data: 106→103. Categories >50% TP: 19→17. v0.3.8/v0.3.9 12-CWE subset had reported 44.3%; full-suite is 44.4% — consistent with subset measurement.
Top CWEs by FP count (v0.3.14 full suite):
CWE |
TP |
FP |
TP% |
|---|---|---|---|
CWE78 OS Command Injection |
17,350 |
5,592 |
75.6% |
CWE197 Numeric Truncation Error |
1,148 |
227 |
83.5% |
CWE506 Embedded Malicious Code |
666 |
120 |
84.7% |
CWE617 Reachable Assertion |
312 |
48 |
86.7% |
CWE464 Addition of Data Structure Sentinel |
48 |
5 |
90.6% |
CWE457 Use of Uninitialized Variable |
704 |
2,549 |
21.6% (worst) |
CWE563 Unused Variable |
54 |
250 |
17.8% |
CWE366 Race Condition Within Thread |
1 |
12 |
7.7% |
Duration: 1h 14m 1s (54,484 files, 118 CWEs), 24-core workstation via MCP benchmark server.
v0.3.9 — P3214 Real-World FP Fixes (12-CWE Subset)
Targeted at 5 high-frequency FP patterns from P3214 embedded firmware (CMS80F752x/8051 MCU). 12-CWE subset: TP=61,349, FP=77,245, rate=44.3% (-450 TP / -581 FP vs v0.3.8).
DCL08-C: Skip pair-offset “relationship” check when all 3+ enumerators have explicit values. Protocol/wire-format/register enums assign all values deliberately — no implicit relationship to encode. 2-member enums (canonical
{IN_STR_LEN=18, OUT_STR_LEN=20}) still checked. Eliminates ~205 FPs per protocol-heavy file.EXP36-C: Added
uint8_t *andint8_t *to alignment map with value 1. Previouslyends_with("*")fallback assigned them alignment 4, causing(uint8_t *)&struct_varto be flagged as increasing alignment strictness (it decreases it). Eliminates 4 FPs per memcpy/memset site.INT36-C: Guard
check_pointer_to_integer_castwith!is_pointer_type(type_text).is_integer_type("uint8_t *")returned true because “uint8_t” contains “int”. Pointer casts like(uint8_t *)&xwere incorrectly flagged as pointer-to-integer conversions. Eliminates 14 FPs per file.INT01-C: Skip
check_param_listwhen param usesuint8_t/uint16_t/int8_t/int16_t. On 8/16-bit MCUs these are correct types for size parameters;size_twould waste scarce registers. Eliminates 12 FPs.EXP33-C: When a variable is initialized in 2+ distinct conditional branches (if/else-if without final else), use
ConditionallyInitializedinstead ofUninitialized. Multi-branch chains indicate the programmer covers all cases (e.g., exhaustive enum dispatch); sqc cannot prove this statically. Single-branch if-without-else still flags. Also fixed position deduplication incollect_init_func_calls_for_var(identifier args were pushed twice). Eliminates 31 FPs.
Juliet impact (v0.3.8 → v0.3.9): TP 61,799→61,349 (-450), FP 77,826→77,245 (-581), rate 44.3%→44.3%. TP loss primarily from DCL08-C no longer flagging all-explicit-value enums (low-confidence detections on protocol enum patterns).
v0.3.8 — STR31-C, API00-C, EXP34-C, EXP33-C FP Fixes (12-CWE Subset)
Scope: 12-CWE subset (25,860 files); not comparable to the full-suite counts in the round table above (see the v0.3.8/v0.3.9 rows there for the subset numbers).
STR31-C: Gate literal-source suppression in
check_strcpy_safetyandcheck_strcat_safetyon!is_function_parameter(dest). Recovers TPs in CWE124/127 (not in this 12-CWE set, so no measurable change here).API00-C: 4 new validation patterns recognized: return-statement null check, if/else chain, helper-fn early-return, unsigned char arithmetic exclusion. Reduces FPs in real-world code.
EXP34-C: Compound null guard fix —
parse_all_null_conditionscollects ALL variables from||conditions.if (a == NULL || b == NULL) return;now marks bothaandbNotNull on the false branch.EXP33-C: Field/subscript write on uninitialized struct no longer treated as a read (
myUnion.field = x,arr[i].field = xdon’t read the base variable);process_assignmentnow marks the base variable as Initialized on any field write (stack structs and malloc arrays).
EXP33-C delta: TP 2,524→2,225 (-299), FP 3,022→2,446 (-576). FP rate 54.5%→52.4%. Overall: TP 62,098→61,799 (-299), FP 78,402→77,826 (-576), TP rate 44.2%→44.3% (+0.1pp).
v0.3.5 — Struct Field Type Resolution, Real-World FP Fixes
Struct field type resolution for INT32-C/INT30-C, plus DCL13-C/DCL30-C/ EXP33-C targeted FP fixes.
Struct field type resolution: Prescan now collects struct definitions
into struct_field_types: HashMap<String, HashMap<String, String>>.
INT32-C and INT30-C infer_type() resolves field_expression nodes
(e.g., s->count) via two-level lookup: variable type → struct name →
field type. Previously returned not_applicable/unknown for all struct
field accesses.
Real-world FP fixes: DCL13-C (removed FILE-modifying functions from
READ_ONLY_FUNCTIONS), DCL30-C (added conditional_expression handling to
is_alloc_expression()), EXP33-C (array output parameter recognition for
unknown functions). Cleared 3 inline suppressions.
Juliet impact (0.2.25 → 0.3.5): Overall TP 130,199→130,004 (-195), FP 161,965→161,510 (-455), TP rate 44.6% → 44.6% (unchanged). Minimal Juliet impact expected — Juliet uses explicit local variables, not struct member access patterns; real-world impact expected on struct-heavy codebases (curl, mosquitto, etc.).
Files changed: prescan.rs, context.rs, ast_utils.rs,
int32_c.rs, int30_c.rs, dcl13_c.rs, dcl30_c.rs,
exp33_c.rs.
v0.2.25 — ARR32-C Tightening, Rule Removals, Value-Range FP Fixes
Mixed release: ARR32-C refinement (pre-existing), INT18-C/EXP05-C rule removal, and real-world value-range FP fixes.
ARR32-C tightening (dominant effect): Refined array size validation to reduce false positives. -1,201 TP/-926 FP — trades some TPs for cleaner results. Largest CWE impacts: CWE190 -584 FP, CWE191 -391 FP, CWE194 -148 TP (no FP change), CWE195 -132 TP (no FP change).
INT18-C removal: Rule removed entirely (-232 TP/-621 FP). Was generating 2.7:1 FP:TP ratio — not worth keeping.
EXP05-C removal: Rule removed entirely (-3 TP/-12 FP). Negligible detection with poor ratio.
INT30-C pointer type detection: Fixed infer_type() to check for
pointer types (*) before is_unsigned_type(). unsigned char * was
incorrectly classified as “unsigned” instead of “not_applicable”. Also fixed
extract_type_and_name() to include * in type_map for pointer
declarators, and pointer_expression dereference to strip one * level.
(-26 TP/-62 FP)
INT32-C field_expression skip: check_memory_function_overflow() now
skips field_expression nodes (e.g., server_host->h_length) —
contains_arithmetic() was matching -> as subtraction.
INT32-C/INT30-C small increment suppression: New
is_small_increment_of_opaque() suppresses call_expression + small_literal
and call_initialized_var + small_literal patterns where const_eval can’t
evaluate function return values.
INT34-C const_eval integration: Converted from unit struct to stateful
struct with MacroConstantMap. Shift amount range evaluation via
try_evaluate_range() + loop-bounds validation as fallback.
Juliet impact (0.2.23 → 0.2.25): Overall TP 131,661→130,199 (-1,462), FP 163,585→161,965 (-1,620), TP rate 44.6% → 44.6% (unchanged). Per-rule: ARR32-C -1,201 TP/-926 FP, INT18-C -232 TP/-621 FP, INT30-C -26 TP/-62 FP, EXP05-C -3 TP/-12 FP. Only regression: FIO05-C +1 FP (noise), CWE773 +2 FP (noise). CWE194 -148 TP/0 FP and CWE195 -132 TP/0 FP from ARR32-C changes (TP loss, no FP benefit).
Real-world impact: 7 target FPs eliminated (64 → 61 violations). All from value-range fixes (INT30-C pointer type, INT32-C field_expr/small_increment, INT30-C small_increment, INT34-C const_eval).
Files changed: int30_c.rs, int32_c.rs, int34_c.rs, mod.rs,
arr32_c.rs, exp05_c.rs, int18_c.rs, str04_c.rs.
v0.2.23 — INT32-C Const-Eval for Allocation/Memory/Abs + INT30-C Built-in Macros
Major const_eval enhancement: built-in C standard limit macros, sizeof resolution, and AST-based allocation overflow checks.
Built-in limit macros: Added ~35 C standard macros (INT_MAX, UINT_MAX,
CHAR_MAX, etc.) as defaults in collect_macro_constants(). Since
tree-sitter doesn’t process #include <limits.h>, const_eval previously
couldn’t resolve expressions involving these macros. Now
INT_MAX/2 + INT_MAX/2, UINT_MAX - 50, and similar are correctly
evaluated.
sizeof resolution: Maps ~20 C types to LP64 sizes (char``→1,
``int``→4, ``long``→8, ``size_t``→8, pointers→8). Enables const-folding of
``N * sizeof(T) allocation expressions.
INT32-C allocation overflow: Refactored check_allocation_overflow()
and check_memory_function_overflow() from text-level
contains_arithmetic() (just expr.contains('*')) to AST node traversal
with expression_fits_in_signed() (64-bit width for size_t).
malloc(100 * sizeof(char)) now resolves to 400 and is suppressed.
INT32-C abs() suppression: Two new checks in check_abs_overflow():
widening cast (abs((long)data) can’t overflow because long range
contains all int values) and comparison condition (if (abs(x) <= limit)
— the abs() IS the bounds check, not a violation).
INT30-C uint64_t subtraction skip: uint64_t subtraction wrapping is
practically impossible (2^64 ns = ~584 years). Skip unsigned subtraction
checks when both operands are 64-bit.
Juliet impact (0.2.22 → 0.2.23): Overall TP 137,921→131,661 (-6,260), FP 175,673→163,585 (-12,088), TP rate 44.0% → 44.6% (+0.6pp). Per-rule: INT32-C -2,394 TP/-5,238 FP (2.2:1 ratio), INT30-C -3,866 TP/-6,848 FP (1.8:1 ratio). Zero rule regressions; only CWE194 +28 FP and CWE195 +25 FP (noise from const_eval enabling new evaluations). Top CWE improvements: CWE197 +14.8pp TP rate (69.4%→84.2%), CWE590 +3.1pp, CWE226 +3.5pp, CWE122 -2,809 FP.
Files changed: const_eval.rs, int32_c.rs, int30_c.rs,
function_summary.rs.
v0.2.16 — EXP34-C: Call-Site Null Propagation (Phase 2)
Two complementary mechanisms for cross-file null pointer analysis:
A. Call-site flagging (new TPs): Re-enabled check_callsite_null_args
— flags DefinitelyNull arguments passed to callees that don’t null-check
them. Removed the old dereferences_params gate (too aggressive, killed
all TPs). Added is_null_safe_function() guard for free,
printLine, etc. Only fires when callee has a prescan summary (guards
against unknown library functions).
B. Callee param seeding (FP reduction): Prescan second pass collects
argument null states at every call site via AST-level inference
(infer_arg_null_state). Aggregates per-callee per-param with lattice
join. Seeds callee params with call-site-derived states instead of blanket
PossiblyNull. Header-declared functions get an implicit Unknown caller to
prevent false NotNull seeding.
Juliet impact (0.2.15 → 0.2.16): Net +11 FP (+0.006%), +19 TP, TP rate 44.2% → 44.2% (unchanged at 1dp). Only CWE-476 affected: +19 TP / +17 FP (TP rate 35.9% → 36.6%, +0.7pp). EXP34-C in CWE-476: TP 80 → 99 (+19), FP 94 → 111 (+17). Approach A delivered +19 TPs (call-site flagging working as intended for Juliet variants 51-54). Approach B had limited impact: most good-source functions pass identifiers (Unknown from prescan AST inference, not trackable without dataflow), so param seeding couldn’t distinguish good vs bad callers. Relay chains (variants 52-54) correctly produce Unknown → PossiblyNull (safe fallback). No impact on other CWEs.
Files changed: exp34_c.rs, function_summary.rs, prescan.rs,
null_state.rs.
v0.2.21 — Const-Eval Value-Range Analysis + Real-World Round 6 FP Fixes
New src/analyze/const_eval.rs module (~550 lines) implements lightweight
constant evaluation: macro constant collection from #define nodes,
ValueRange interval arithmetic, recursive AST constant folder, loop-bound
extraction from enclosing for/while/do statements, and local
variable range resolution. Integrated into INT32-C and INT30-C as
early-return suppression when
expression_fits_in_signed()/expression_fits_in_unsigned() proves
safety.
Additional real-world FP fixes (also included): ARR02-C skip
string-literal-initialized arrays, POS02-C removed socket/setsockopt
from privileged operation list, PRE31-C strip string literals before
side-effect analysis, MEM05-C ALL_CAPS macro constant VLA suppression +
word-boundary recursion matching.
Juliet impact (0.2.20 → 0.2.21): Overall TP 144,278→137,921 (-6,357), FP
181,924→175,667 (-6,257), TP rate 44.2% → 44.0% (-0.2pp). Zero CWE/rule
regressions. Top rule changes: INT32-C -2,849 FP/-2,147 TP (const_eval),
POS02-C -1,660 FP/-2,398 TP (socket/setsockopt), MEM05-C -1,454 FP/-1,638 TP
(ALL_CAPS VLA), ARR02-C -157 FP/-88 TP, INT30-C -136 FP/-86 TP. POS02-C
concern: 0.69:1 FP:TP ratio — loses more TPs than FPs; Juliet patterns use
socket()/setsockopt() in good/bad function pairs, so removing the
check suppresses violations in surrounding code (real-world impact much
smaller, -167 across curl+hostap). CWE190 (Integer Overflow): -1,269 FP
(biggest CWE improvement); CWE191: -848 FP.
Real-world results: INT32-C 10→8 (-2 macro×literal FPs suppressed). INT30-C unchanged.
Files changed: const_eval.rs (NEW), context.rs, prescan.rs,
mod.rs, int32_c.rs, int30_c.rs, arr02_c.rs, pos02_c.rs,
pre31_c.rs, mem05_c.rs.
v0.2.20 — Real-World FP Fixes (API00-C, INT01-C, EXP37-C, EXP34-C)
Fixes driven by real-world false positive analysis. Four rounds of targeted fixes (transitive includes, snprintf arg count, K&R declaration, static function skip, DCL13-C alias tracking, INT01-C dedup, stack array NotNull).
API00-C static function skip:
check_function_parameter_validation()returns early forstaticfunctions andSTATICmacro prefixes. API00-C is about public API contracts — static functions are internal.INT01-C duplicate firing fix:
check_size_params()double-visitedfunction_declarator/parameter_listnodes via both explicit child iteration and general recursion. Fix: skip already-handled node kinds in general recursion.EXP37-C init_declarator skip: K&R-style declaration check now skips
declarationnodes withinit_declaratorchild (variable declarations with initialization, not function prototypes).EXP34-C stack array NotNull:
collect_assignments_recursive()in prescan now detectsarray_declaratorchildren and marks them NotNull (stack arrays can never be null).FIO47-C snprintf arg count:
count_arguments()subtracts 3 forsnprintf/vsnprintf(buffer + size + format).DCL13-C address-of-member + alias: Detects
&(param->field)in function args andT *local = param;alias patterns as modification through the parameter.
Juliet impact (0.2.19 → 0.2.20): Overall TP 145,639→144,278 (-1,361), FP 184,644→181,924 (-2,720), TP rate 44.1% → 44.2% (+0.1pp). No CWE regressions. Top rule changes: API00-C -1,917 FP (static skip), INT01-C -231 FP (dedup), EXP34-C -221 FP (array NotNull), DCL30-C -201 FP, FIO47-C -88 FP. No rule regressions — previously reported POS02-C/ERR05-C/MEM06-C “regressions” were a measurement artifact (see the per-rule data accuracy note below).
Files changed: api00_c.rs, int01_c.rs, exp37_c.rs,
fio47_c.rs, dcl13_c.rs, prescan.rs.
v0.2.19 — Real-World FP Reduction (INT30-C Loop Guards, Prescan Null Guards, ARR00-C Fix)
Three changes targeting real-world false positives (not Juliet-specific patterns).
INT30-C loop-bounded increment: Added is_add_one_bounded_by_loop()
and is_bounded_by_loop_condition() — walk AST ancestors for enclosing
while/for with var < limit condition. Suppresses var + 1,
var += 1, var++ when bounded by loop condition (proves no unsigned
wrap).
Prescan early-return null guards: Added
collect_early_return_null_guards() to detect if (p == NULL) return;
patterns at function entry. Marks guarded parameters as NotNull in
local_states for downstream rules (EXP34-C, API00-C caller-aware
suppression).
ARR00-C crash fix: array_size - 1 panicked when array_size was 0
(unsigned underflow). Changed to saturating_sub(1).
Juliet impact (0.2.18 → 0.2.19): Overall TP 145,639→145,639 (0), FP 184,645→184,644 (-1), TP rate 44.1% → 44.1% (unchanged) — essentially neutral, changes target real-world patterns not present in Juliet. Rule-level churn: FIO06-C -169 FP (prescan improvement) offset by FIO03-C +169 FP, ARR00-C -74 FP offset by EXP10-C +74 FP. No CWE-level regressions.
Files changed: int30_c.rs, prescan.rs, arr00_c.rs.
v0.2.18 — Quick Wins FP Reduction (INT31-C, ARR36-C, API00-C, INT30-C)
Four targeted fixes addressing false positives identified from real-world codebases.
INT31-C pointer cast skip: Added early return when either target or source type contains
*. Pointer reinterpretation casts like(uint8_t *)bufare not integer value conversions and should not trigger INT31-C.ARR36-C type filtering: Only track pointer/array declarators (
pointer_declaratororarray_declarator) inprocess_declaration(). Scalar variables initialized from array subscripts (e.g.,int a = arr[0]) are not pointers — their subtraction/comparison is integer arithmetic, not pointer arithmetic.API00-C void-cast detection: Recognize
(void)paramcasts andUNUSED(param)macros as intentional suppression patterns. Parameters explicitly cast to void are acknowledged as unused by design and should not trigger API00-C’s “validate before use” check.INT30-C guard pattern expansion: Rewrote
is_guarded_by_gt_zero()to walk AST ancestors looking for enclosingif,while, andforconditions. Addedcondition_implies_positive()with compound condition support (handles&&/||viacontains()). Recognizesvar > expr(any lower bound, not just zero) andexpr < var(with<</<=exclusion). Addedis_subtract_one_guarded()for both binary subtraction and compound-= 1.
Juliet impact (0.2.17 → 0.2.18): Overall TP 146,913→145,639 (-1,274), FP 185,591→184,645 (-946), TP rate 44.2% → 44.1% (-0.1pp). INT30-C dominant: -1,292 TP / -935 FP (guard expansion removes both TPs and FPs). CWE191 (Integer Underflow): -1 TP / -45 FP (clean win). WIN03-C: +16 TP / -102 FP (unexpected improvement). ARR38-C: 0 TP / -36 FP (clean win). Only 1 trivial regression: CWE675 +1 FP. INT30-C FP:TP ratio ~0.7:1 (loses more TPs than FPs — guard patterns are slightly too aggressive).
Files changed: int31_c.rs, arr36_c.rs, api00_c.rs, int30_c.rs.
v0.2.17 — Phase 3: CWE-476 FP Reduction (MEM10-C, API00-C, API02-C, Prescan)
Targeted CWE-476 false positive reduction via rule narrowing and enhanced inter-procedural analysis.
MEM10-C positive guard suppression: Suppress violations when the condition is a positive null guard (
!= NULLor bare truthiness) and the parameter is only used inside the guarded block. This pattern (if (data != NULL) { use(data); }) is the prescribed fix per EXP34-C — MEM10-C was penalizing correct code.API02-C ``const wchar_t *`` exclusion: Extended existing
const char *skip to wide strings. Wide char pointers follow the same null-terminated string convention. Original plan to skip all mutablechar *was too aggressive —char *as destination buffer correctly requires a size parameter.API00-C caller-aware suppression: Converted from unit struct to stateful with
function_summaries. Addedset_project_context()to receive summaries from prescan. Before flagging a pointer parameter, checkscallsite_param_null_states: if all callers pass NotNull → suppress violation.Prescan local variable tracking: Enhanced
collect_callsite_args_from_treewithcollect_local_var_states()— scans function bodies for simple assignments (var = NULL→ DefinitelyNull,var = "str"→ NotNull,var = malloc(...)→ PossiblyNull). Resolves identifier arguments via local state lookup instead of returning Unknown.Variant 45 global tracking: Verified working correctly —
badSink()flagged (reads DefinitelyNull global),goodG2BSink()andgoodB2GSink()correctly suppressed. No code changes needed.
Juliet impact (0.2.16 → 0.2.17): Overall +180 TP, +81 FP, TP rate 44.2% → 44.2% (unchanged). CWE-476: TP 313→320 (+7), FP 542→512 (-30), TP rate 36.6% → 38.5% (+1.9pp). CWE-690: TP 3711→3747 (+36), FP 4474→4411 (-63), TP rate +0.6pp. Per-rule: MEM10-C -38 FP/0 TP (clean elimination), API00-C -3 FP/0 TP. Side benefits from prescan: FIO03-C -169 FP, ERR05-C -105 FP, FIO20-C -102 FP. Regressions: EXP34-C +76 FP, FIO06-C +169 FP (enhanced prescan provides more inter-procedural data to these rules).
Files changed: mem10_c.rs, api00_c.rs, api02_c.rs,
prescan.rs, mod.rs.
v0.2.15 — Real-World FP Cleanup (17 Patterns)
Addressed 17 FP patterns (~51 violations) identified from real-world codebases across two commits. Targeted real-world precision over Juliet benchmark score.
Commit 1 (d31a6c3) — Batch 1 (10 rule fixes, ~36 FPs): FIO46-C
source-order stream tracking (store start_byte(), only flag after
fclose); INT32-C returns not_applicable for field_expression nodes;
FLP03-C precise scientific notation regex + per-operand float check in
division; EXP40-C checks parent declaration for const type
qualifier; EXP12-C checks node.parent() — skip when return value is
captured; INT01-C skips sizeof(...) * N binary expressions in allocation
args; INT10-C collect_variable_types() + operand_has_unsigned_type()
for struct fields; ARR39-C skips ALL_CAPS-only arithmetic (macro/enum
constants); EXP05-C doesn’t recurse into function_definition nodes for
const scanning; EXP02-C exempts NULL_CHECK && FUNCTION_CALL guard
pattern.
Commit 2 (0a45c9f) — 6 remaining violations: INT32-C (x3) propagate
unsigned type through binary_expression chains; INT10-C (x1) skip
field_expression operands in modulo sign check; EXP05-C (x1) skip
field_expression in const-qualification check; ARR39-C (x1) recursive
is_all_caps_arithmetic() for nested expressions.
Juliet impact (0.2.13 → 0.2.15): Net -10,678 FP (-5.4%), -11,689 TP, TP rate 44.7% → 44.2% (-0.5pp) — decline accepted since fixes are semantically correct for real-world code. Top rule deltas: EXP12-C -5,477 FP/-7,530 TP (parent check reduces over-flagging), INT01-C -2,714 FP/-740 TP, INT32-C -181 FP/-1,122 TP. FIO46-C and EXP02-C eliminated entirely (0 detections — correct, these rules had very low signal).
v0.2.13 — INT31-C Implicit Narrowing + Real-World FP Fixes
INT31-C: Implemented
check_assignment_conversion()for implicit narrowing detection (uint8_t tag = (uint16_t)(expr)). Conservative: only flags when both LHS and RHS have known integer types. FP suppressions: literal-fits, safe-mask (& 0xFF), bounds-checked blocks, double-flag prevention. Juliet CWE197 impact: -9 TP / -9 FP (unchanged — Juliet uses explicit casts). Real-world: +229 new findings across curl/hostap/sqlite/mosquitto.INT32-C: Skip unsigned operands in binary overflow checks (FP-004). -8,390 FP, -7,068 TP.
DCL07-C/DCL31-C: Skip indirect calls (function pointers) + preproc -guarded calls (FP-009). -2,529/-2,429 FP.
DCL15-C: Skip functions declared in header files (public API). -663 FP (curl).
Net: -13,961 FP (-6.6%), -10,758 TP, TP rate 44.6% → 44.7% (+0.1pp). Zero CWE regressions. Top CWE improvements: CWE90 (-2,737 FP, +8.2pp), CWE78 (-1,704 FP, +2.7pp). Rule regressions under investigation at the time: ARR00-C (+905 FP), WIN03-C (+233 FP), MEM01-C (+225 FP).
Real-world benchmark (0.2.11 → 0.2.13, 4 codebases):
Codebase |
0.2.11 |
0.2.13 |
Delta |
|---|---|---|---|
curl |
93,576 |
73,816 |
-19,760 (-21.1%) |
hostap |
234,421 |
206,906 |
-27,515 (-11.7%) |
sqlite |
177,983 |
147,091 |
-30,892 (-17.4%) |
mosquitto |
39,177 |
33,638 |
-5,539 (-14.1%) |
Total |
545,157 |
461,451 |
-83,706 (-15.4%) |
v0.2.12 — DCL13-C Pointer Modification + INT01-C sizeof Skip
DCL13-C: Comprehensive pointer modification detection — tracks
*ptr =,ptr[i] =,ptr->field =, increment/decrement, and function-call mutations. Rule went from 4,713 FP → 486 FP (-4,227 FP, -3,163 TP). The large TP drop is expected: many “const-qualify” violations in Juliet’s bad code were technically correct flags but low-signal.INT01-C: Skip
sizeof(...)expressions in allocation argument checks.malloc(sizeof(int) * n)no longer flagssizeof(int)as an implicit conversion. -988 FP, -1,251 TP.Side effects: DCL13-C’s reduction exposed previously-masked violations from other rules: EXP33-C (+1,022 FP), API02-C (+628 FP), ERR05-C (+431 FP), EXP12-C (+279 FP). Net still strongly positive.
Net: -5,531 FP (-2.6%), -3,619 TP, TP rate 44.5% → 44.6% (+0.1pp). Only 1 CWE regressed: CWE773 (+3 FP, 0 TP change).
v0.2.7 — INT36-C TP Restore + INT31-C FP Fix
INT36-C: Re-allowed
->field access in pointer-to-int detection (+955 TP, +149 FP — restoring TPs from earlier over-aggressive filtering).INT31-C: Added shift-narrowing detection for
(uint8_t)(value >> N)patterns (-138 FP, -9 TP; CWE197 TP rate 71.8% → 75.7%).
Overall: +72 TP, -1 FP.
Round 16 — DCL13-C main() Exemption + MEM10-C Parameter-Only Null Check
DCL13-C:
main()parameters defined by C standard — not flagged for const-qualification.MEM10-C: Inline null-check detection restricted to function parameters only. Good functions in CWE-476 add
if (data != NULL)guards; MEM10-C was penalizing this correct pattern. CWE-476 MEM10-C FPs: 134 → 28 (-106).
Round 15 — EXP34-C: if/else Branch Merge
collect_null_variables now merges state from both if/else branches
(union of potentially_null_vars). Fixes variant 12
(globalReturnsTrueOrFalse) where if-branch sets ptr=NULL and else-branch
sets ptr=non-null.
Round 14 — EXP34-C: deref_after_check Pattern
Fixed null_check_positions to store end_byte so derefs inside the
null branch (if (ptr == NULL) { *ptr; }) are still flagged. +18 TPs, 0
new FPs.
Round 13 — STR31-C: L-prefix and Literal-Source Fixes
Strip L-prefix before measuring wide string literals (
L"*.*"→ 3 chars, not 4).Literal source + unknown dest → safe (suppresses FPs in CWE134 good functions).
Net: -4,125 FP (-1.7%), -934 TP, TP rate +0.3pp.
Round 12 — INT07-C, INT32-C, EXP10-C, EXP34-C, INT30-C, MEM10-C, STR31-C, Windows API
INT07-C: Removed comparison operators from numeric-use detection.
INT32-C:
infer_type()returns “not_applicable” for non-integer types.EXP10-C:
is_pure_function()whitelist (~50 functions).EXP34-C: Gates nullable-function-call taint on
declared_pointer_vars.INT30-C: Not-applicable guards for pointer arithmetic.
MEM10-C: Removed
== 0/!= 0from null-check detection.STR31-C: Short literal suppression (≤3 chars).
std_functions: +~100 Windows API functions. DCL31-C/DCL07-C: 21K/20K → 2.5K/2.4K FP.
Net: -28,933 FP (-10.6%), TP rate +0.6pp.
Round 9 — CFG, Data-Flow, Inter-Procedural Analysis
CFG construction, reaching definitions, inter-procedural function summaries. Minimal Juliet impact (-73 FP) — targets multi-file real-world codebases.
Round 8 — DCL40-C, FLP32-C, ERR33-C
DCL40-C: Removed 31-char prefix collision check (was O(n²) FPs). FP ~12K → ~0, 0 TP loss.
FLP32-C: Windowed error checking (5 stmts) instead of entire scope.
ERR33-C: Argument-list detection for nested calls.
Net: -5,060 FP (-1.7%), TP rate +0.4pp.
Round 7 — EXP36-C, EXP34-C, ARR37-C
EXP36-C: Only check pointer-to-pointer casts; skip integer casts and unknown source types.
EXP34-C: Removed
_tsuffix heuristic; field null propagation now conditional on base.ARR37-C: Stop flagging Unknown pointers; all pointer params now ambiguous.
Net: -25,716 FP (-7.9%), TP rate +0.3pp.
Round 6 — Cross-File Analysis (-d)
--directories CLI option pre-scans for function definitions. DCL31-C/
DCL07-C eliminated FPs from Juliet helper functions. Net: -148,622 FP
(-31.2%), TP rate +1.4pp.
Round 5 — FLP02-C, DCL06-C, INT30-C
FLP02-C: AST-node-kind checks instead of text heuristics.
DCL06-C: Expanded acceptable literal values to 0–10.
INT30-C:
collect_variable_types()pattern; removed name heuristics.
Net: -16,835 FP (-3.4%), TP rate -0.7pp (DCL06-C is ~50/50).
Round 4 — EXP12-C, FLP03-C, INT32-C
EXP12-C: Removed ~30 side-effect functions from “important return value” whitelist.
FLP03-C: Removed assignment_expression arm.
INT32-C:
collect_variable_types()HashMap; default “unknown” for unmapped variables.
Net: -44,941 FP (-8.4%).
Round 3 — DCL31-C, DCL07-C, FLP34-C
Shared std_functions.rs database (~270 functions). -198,974 FP. FLP34-C:
type-aware checking.
Round 2 — EXP33-C, SIG31-C, ARR01-C, DCL30-C, DCL02-C
Fixed preprocessor-block visibility bug (functions inside #ifdef
invisible). DCL02-C similar-identifier check. -15,859 FP; CWE-457 TP rate
12.2% → 22.6%.
Round 1 — INT08-C, CON08-C, DCL20-C, ARR38-C
INT08-C: Removed
intfrom “narrow type” definition.CON08-C: Only flag multiple atomic functions without mutex.
DCL20-C: Only flag declarations/prototypes, not definitions.
ARR38-C: Removed duplicate strcpy/strcat flagging.
Net: -86,919 FP (-10.4%), TP rate +1.2pp.
Performance by CWE Category (Historical Snapshot)
Note
These tiers reflect an older full-suite run (pre-fast-mode). See
JULIET_RESULTS.md for current per-CWE precision via
python -m bench get-cwe-detail / the MCP get_cwe_detail tool.
Tier 1: Strong Detection (TP > 50%) — 18 categories
CWE |
Category |
TP Rate |
Files |
|---|---|---|---|
464 |
Data Structure Sentinel Addition |
89.1% |
56 |
617 |
Reachable Assertion |
86.7% |
354 |
506 |
Embedded Malicious Code |
85.7% |
158 |
587 |
Fixed Address to Pointer |
100% |
18 |
526 |
Info Exposure via Env Variables |
100% |
18 |
78 |
OS Command Injection |
76.1% |
5,600 |
114 |
Process Control |
73.6% |
672 |
427 |
Uncontrolled Search Path Element |
72.0% |
560 |
510 |
Trapdoor |
70.0% |
70 |
197 |
Numeric Truncation Error |
84.2% |
1,008 |
15 |
External Control of System/Config |
66.9% |
56 |
620 |
Unverified Password Change |
64.4% |
36 |
194 |
Unexpected Sign Extension |
59.9% |
1,344 |
188 |
Reliance on Data/Memory Layout |
59.5% |
36 |
123 |
Write-What-Where Condition |
58.6% |
168 |
90 |
LDAP Injection |
57.9% |
560 |
195 |
Signed-to-Unsigned Conversion |
57.9% |
1,344 |
835 |
Infinite Loop |
50.0% |
18 |
Tier 2: Moderate Detection (35–50%) — 68 categories
The bulk of categories (64%) cluster here. Includes buffer overflows (CWE-121 ~43%, CWE-122 ~42%), format strings (CWE-134 ~37%), and resource management.
Tier 3: Below Average (25–35%) — 16 categories
Includes integer overflow/underflow (CWE-190 ~33%, CWE-191 ~35%), memory management (CWE-401 ~34%, CWE-415 ~34%), NULL pointer dereference (CWE-476 ~39%).
Tier 4: Weak Detection (<25%) — 4 categories
CWE |
Category |
TP Rate |
Root Cause |
|---|---|---|---|
256 |
Plaintext Password Storage |
~15% |
No credential-storage rules |
338 |
Weak PRNG |
~23% |
No PRNG-quality rules |
457 |
Use of Uninitialized Variable |
~24% |
Improved from 12.2% after fixes |
319 |
Cleartext Transmission |
~25% |
Limited cleartext detection |
Full Per-CWE Results (Round 1 Baseline)
Note
This table reflects Round 1 (42.3% TP rate). Current performance is much higher. Relative ordering remains representative of rule difficulty.
CWE |
Vulnerability Type |
Files |
TP |
FP |
TP Rate |
|---|---|---|---|---|---|
506 |
Embedded Malicious Code |
158 |
3,421 |
552 |
86.1% |
15 |
External Control of System/Config |
56 |
1,255 |
422 |
74.8% |
427 |
Uncontrolled Search Path Element |
560 |
7,656 |
2,798 |
73.2% |
78 |
OS Command Injection |
5,600 |
79,292 |
30,203 |
72.4% |
617 |
Reachable Assertion |
354 |
2,685 |
1,192 |
69.3% |
197 |
Numeric Truncation Error |
1,008 |
7,899 |
3,733 |
67.9% |
123 |
Write-What-Where Condition |
168 |
2,239 |
1,213 |
64.9% |
114 |
Process Control |
672 |
8,839 |
4,973 |
64.0% |
194 |
Unexpected Sign Extension |
1,344 |
18,260 |
12,440 |
59.5% |
510 |
Trapdoor |
70 |
1,450 |
1,037 |
58.3% |
195 |
Signed-to-Unsigned Conversion |
1,344 |
16,087 |
11,865 |
57.6% |
90 |
LDAP Injection |
560 |
12,600 |
10,252 |
55.1% |
464 |
Data Structure Sentinel Addition |
56 |
334 |
280 |
54.4% |
526 |
Info Exposure via Env Variables |
18 |
69 |
58 |
54.3% |
587 |
Fixed Address to Pointer |
18 |
36 |
31 |
53.7% |
680 |
Integer Overflow to Buffer Overflow |
336 |
5,381 |
4,715 |
53.3% |
188 |
Reliance on Data/Memory Layout |
36 |
286 |
275 |
51.0% |
843 |
Type Confusion |
100 |
279 |
340 |
45.1% |
481 |
Assigning Instead of Comparing |
18 |
195 |
239 |
44.9% |
480 |
Use of Incorrect Operator |
18 |
79 |
97 |
44.9% |
121 |
Stack-Based Buffer Overflow |
5,906 |
50,353 |
66,007 |
43.3% |
122 |
Heap-Based Buffer Overflow |
3,656 |
42,202 |
58,891 |
41.7% |
134 |
Uncontrolled Format String |
3,360 |
52,276 |
90,251 |
36.7% |
476 |
NULL Pointer Dereference |
372 |
1,222 |
2,475 |
33.1% |
190 |
Integer Overflow |
5,040 |
26,103 |
54,636 |
32.3% |
191 |
Integer Underflow |
3,864 |
19,849 |
40,831 |
32.7% |
401 |
Memory Leak |
1,228 |
10,976 |
23,198 |
32.1% |
416 |
Use After Free |
150 |
1,787 |
4,698 |
27.6% |
457 |
Use of Uninitialized Variable |
616 |
5,045 |
36,338 |
12.2% |
TOTALS (106 categories): 54,484 files, TP 552,645, FP 752,422, TP rate 42.3%.
12 categories had no C test data (Java/C++ only): CWE-23, CWE-36, CWE-396, CWE-397, CWE-440, CWE-500, CWE-561, CWE-562, CWE-672, CWE-674, CWE-676, CWE-762.
Per-Rule Data Accuracy Note (2026-03-03)
Prior to this date, the analysis script output only the top 10 rules per CWE for TP and FP. The MCP benchmark server aggregated per-rule totals from these top-10 lists, producing lossy data. This caused:
Phantom regressions: Rules appearing in the top-10 window when other rules dropped out (e.g., v0.2.20’s reported POS02-C/ERR05-C/MEM06-C “regressions” were entirely phantom — zero actual change).
Inaccurate per-rule deltas: Numbers could be undercounted (rule only in top-10 for some CWEs) or overcounted (rule entering top-10 due to another rule’s reduction).
Total TP/FP and TP rates were always correct — these were computed from full violation data, not per-rule aggregation.
The analysis script now outputs all rules, and all runs since have been reanalyzed with full per-rule data. Historical per-rule numbers in the “Per-Round Fix Details” section above predate this fix and may have varying accuracy. The overall direction of changes was generally correct.
Competitor Comparison
Tool |
Detection Rate |
FP Rate |
Analysis Depth |
Juliet Data |
CERT C |
Price |
|---|---|---|---|---|---|---|
SqC |
44.6% |
55.4% |
AST + CFG + inter-procedural + call-site null + local var tracking + const_eval |
Full (118 CWEs) |
283 rules |
– |
Semgrep CE |
44–48% |
Very low |
AST (tree-sitter) |
No |
Community |
Free |
Semgrep Pro |
72–75% |
Very low |
AST + taint + inter-file |
No |
Community |
Commercial |
Infer |
~55% |
~45% |
Separation logic |
Partial (4 CWEs) |
No |
Free |
Flawfinder |
~40% |
High |
Lexical scanning |
Indirect |
No |
Free |
CodeQL |
~29% |
Moderate |
Data-flow, taint |
Indirect |
Partial |
Free/Commercial |
Cppcheck |
Low |
Very low |
Data-flow |
Indirect |
Partial |
Free |
Coverity |
Best-in-class |
~15–20% |
Inter-procedural, path-sensitive |
Not public |
Partial |
Enterprise |
Commercial “Tool C” |
~73% |
~7% |
Inter-procedural |
Yes (22 CWEs) |
– |
Commercial |
The SqC row above is from the 44.6% full-suite era (v0.2.23); current fast-mode
TP rate is 83.8% (v0.4.116, see JULIET_RESULTS.md) but is not directly
comparable since the competitor figures below were not re-measured
fast-mode/CWE-matched.
“Commercial Tool C” is anonymized from Goseva-Popstojanova & Perhinschi 2015, tested on 22 CWEs only.
Key context from literature:
Tools on average find ~20% of weaknesses in basic Juliet test cases (ISSTA 2022).
Even commercial tools miss 27% of C/C++ vulnerabilities (Goseva 2015).
FP rates range from 6.5% to 76%+ depending on rule set.
Industry target for developer adoption is 10–20% FP rate.
No single tool is comprehensive; academic consensus recommends tool combination.
Sources: ISSTA 2022 | Goseva 2015 | JKU 2014 | Semgrep Blog 2025
Version History (v0.2.1 – v0.3.17)
Full-suite runs only (pre-fast-mode). See JULIET_RESULTS.md for
fast-mode versions from v0.3.20 onward, or query data/benchmarks.db
directly for every run.
Version |
TP Rate |
FP |
TP |
Runtime |
Machine |
Notes |
|---|---|---|---|---|---|---|
v0.2.1 (baseline) |
41.1% |
839,341 |
~584K |
– |
24-core workstation |
Original |
v0.2.4 |
43.8% |
243,849 |
189,950 |
– |
24-core workstation |
Windows API + multiple rule fixes |
v0.2.6 |
44.5% |
215,672 |
172,708 |
– |
24-core workstation |
CFG null state + bounds-check detection |
v0.2.7 |
44.5% |
215,671 |
172,780 |
– |
24-core workstation |
INT36-C TP restore + INT31-C FP fix |
v0.2.12 |
44.6% |
210,138 |
169,161 |
– |
24-core workstation |
DCL13-C pointer modification + INT01-C sizeof skip |
v0.2.13 |
44.7% |
196,177 |
158,403 |
– |
24-core workstation |
INT31-C implicit narrowing + real-world FP fixes |
v0.2.15 |
44.2% |
185,499 |
146,714 |
– |
24-core workstation |
Real-world FP cleanup (17 patterns) |
v0.2.16 |
44.2% |
185,510 |
146,733 |
– |
24-core workstation |
EXP34-C call-site null propagation (Phase 2) |
v0.2.17 |
44.2% |
185,591 |
146,913 |
– |
24-core workstation |
Phase 3: MEM10-C, API00-C, API02-C, prescan (CWE-476 38.5%) |
v0.2.18 |
44.1% |
184,645 |
145,639 |
– |
24-core workstation |
INT31-C pointer cast, ARR36-C, API00-C void-cast, INT30-C guards |
v0.2.19 |
44.1% |
184,644 |
145,639 |
– |
24-core workstation |
INT30-C loop guards, prescan null guards, ARR00-C fix |
v0.2.20 |
44.2% |
181,924 |
144,278 |
– |
24-core workstation |
Real-world FP fixes: API00-C, INT01-C, EXP37-C, EXP34-C |
v0.2.21 |
44.0% |
175,667 |
137,921 |
– |
24-core workstation |
const_eval value-range analysis + real-world Round 6 FP fixes |
v0.2.22 |
44.0% |
175,673 |
137,921 |
– |
24-core workstation |
INT30-C: extend upper-bound guard to if_statement |
v0.2.23 |
44.6% |
163,585 |
131,661 |
– |
24-core workstation |
INT32-C const_eval alloc/memory/abs + INT30-C uint64_t + built-in macros |
v0.2.25 |
44.6% |
161,965 |
130,199 |
– |
24-core workstation |
ARR32-C tightening, INT18-C/EXP05-C removal, value-range FP fixes |
v0.3.5 |
44.6% |
161,510 |
130,004 |
– |
24-core workstation |
Struct field type resolution (INT32-C/INT30-C), DCL13-C/DCL30-C/EXP33-C fixes |
v0.3.8 |
44.3% |
77,826 |
61,799 |
– |
4-core laptop |
STR31-C, API00-C, EXP34-C, EXP33-C fixes — 12-CWE subset, not full suite (see FP Reduction History table above) |
v0.3.14 |
44.4% |
158,036 |
126,106 |
1h 14m |
24-core workstation |
EXP33-C, INT30-C, EXP34-C FP fixes (full suite) |
v0.3.17 |
44.4% |
160,496 |
128,038 |
1h 47m |
24-core workstation |
CWE-78 macro alias + CWE-253 incorrect return check |
Scripts and Data Locations
bench/ Python benchmark module (python -m bench juliet)
~/data/benchmarks/juliet-test-suite-c/
testcases/ 118 CWE categories, 54,484 .c files
testcasesupport/ Shared helper functions
/tmp/juliet_results/ Per-run output (MCP benchmark server, legacy pre-SQLite)
data/benchmarks.db Canonical store from v0.3.20 onward (SQLite)