import csv, json, os, sys
HERE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(HERE, "libcrc_cfg_0.4.24.json")
OUT = os.path.join(HERE, "adjudication_libcrc_0.4.24.csv")
PREFIX = "/home/brandon/toolchain/libcrc/"
def rel(f):
return f.replace(PREFIX, "")
FP_REASON = {
"FIO47-C": "<inttypes.h> PRIxNN/PRIuNN macros in the concatenated format literal are not expanded by sqc's format parser; '%\"' is a macro-built specifier boundary, not an invalid one",
"PRE32-C": "PRIxNN are object-like macros from <inttypes.h>; PRE32-C targets #if/#include directives passed as macro arguments, not standard format-string macros",
"INT36-C": "'(uintNN_t) *ptr' dereferences ptr to a byte then widens; it is not a pointer-to-integer conversion (sqc confuses *ptr with ptr)",
"API00-C": "input_str validated indirectly: 'ptr = input_str; if (ptr != NULL)' guards every deref (argv guarded by the argc check); sqc misses the alias",
"MEM30-C": "target 'crc_tabXX' is a file-scope static array; init writes a scalar to a global element, no stack object escapes",
"INT31-C": "value is bounded: ch in [0,255] after the !=EOF guard (fgetc contract) so '(unsigned char)ch' is lossless; nibble<<4 fits unsigned char",
"INT32-C": "counter bounded far below INT_MAX (test-failure count / argv index / byte value 0-255); no feasible overflow",
"INT30-C": "unsigned-shift wrap is well-defined and intended in the CRC algorithm; result is masked",
"INT16-C": "the '&' operand is the unsigned uint64_t element crc_tab_precalc[a]; 'a' is only the index, not the masked value",
"INT14-C": "'crc' is unsigned (uint16_t/uint64_t), not signed; the shift is part of the CRC algorithm",
"INT17-C": "0xffffffffL fits both 32- and 64-bit long and is masked into a uint32_t; no portability defect",
"INT01-C": "'filename' is a path string, not a size/count; size_t is inapplicable",
"ARR00-C": "loop variable 'a' is initialized in the for-init (a=0); not uninitialized",
"ARR02-C": "standard C idiom ('char *argv[]' / 'extern T x[]'); the array size is defined elsewhere",
"API02-C": "main(int, char*[]) is the standard entry-point signature; a size parameter is inapplicable",
"ENV01-C": "no getenv()/environment-variable use in the flagged code",
"STR31-C": "argc is validated before argv use (argc<2 / argc!=3 -> exit)",
"STR03-C": "snprintf(result, 3, ...) writes exactly 2 hex + NUL into the documented >=3-byte buffer; bounded by design",
"FIO34-C": "'ch' is int; '(ch=fgetc())!=EOF' is the correct EOF-detection idiom",
"FIO23-C": "stdout is flushed at normal program exit; no data loss",
"FIO02-C": "demo CLI: the argv path is the intended user interface; no privilege boundary to canonicalize across",
"EXP14-C": "implicit promotion is benign and intended in the CRC bit math; values fit int and are masked back",
"EXP19-C": "missing braces around a single-statement body; cosmetic, no defect",
"EXP20-C": "'if(!strcmp(...))' is a standard, well-understood idiom",
"DCL06-C": "256/0xFF/0x0001 are intrinsic CRC constants; a symbolic name adds no safety",
"DCL16-C": "lowercase suffix follows hex digits/'u' (e.g. 0x..ull); negligible 'l'/'1' confusion risk",
"DCL04-C": "two related ints in one declaration; cosmetic",
"ERR33-C": "fgetc result is checked against EOF; an fclose failure on a read-only stream is inconsequential",
}
def adjudicate(v):
rid, m = v["rule_id"], v["message"]
f = rel(v["file"])
if rid == "FIO40-C":
return "TP", "input_string is used in the do_ascii/do_hex paths after fgets() failure (lines 102-105 only perror, no reset) -> indeterminate read"
if rid == "FIO20-C":
return "TP", "fgets(MAX-1) truncation is never detected; long input is silently truncated"
if rid == "DCL00-C":
return "TP", "sht75_crc_table is initialized once and never modified; const-qualifying it is correct and implementable"
if rid == "PRE06-C":
return "TP", "header genuinely lacks an include guard (no #ifndef/#define)"
if rid == "DCL15-C":
return "TP", "update_crc_64 has external linkage but is not declared in checksum.h nor used cross-file; should be static or given a prototype"
if rid == "EXP33-C":
if "input_string" in m:
return "TP", "input_string may be read uninitialized after fgets() failure (same defect as FIO40-C)"
return "FP", "CRC table is lazily initialized via 'if(!_init) init_tab()' before any read; fully populated when read"
if rid == "CON03-C":
if "_init" in m:
return "TP", "lazy table init (init flag + table write) is an unsynchronized data race if crc_* is called from multiple threads (known libcrc thread-safety issue)"
return "FP", "single-threaded test counter / read-only const table; no concurrent access"
if rid in ("EXP12-C", "ERR00-C") and f == "precalc/precalc.c" and "fclose" in m:
return "TP", "fclose() on a written stream is unchecked; a close/flush failure (e.g. disk full) silently loses the generated table"
if rid == "EXP12-C":
return "FP", "ignoring the printf/fprintf/snprintf return value is idiomatic here; no error-handling consequence"
if rid == "ERR00-C":
return "FP", "fopen/fgetc return IS checked (if(fp..) / !=EOF); fclose on a read stream is benign"
if rid == "ARR30-C":
if "size 256" in m or "0x00FF" in m or "0x00ff" in m:
return "FP", "table index is masked '& 0x00FF' to 0-255 against a 256-entry table; provably in-bounds"
return "FP", "NUL/sentinel-terminated string scan; the loop terminates, no buffer overflow"
return "FP", FP_REASON.get(rid, "advisory finding with no security or correctness merit on this code")
def main():
data = json.load(open(SRC))
rows = []
for i, v in enumerate(sorted(data, key=lambda x: (x["rule_id"], rel(x["file"]), x["line"]))):
verdict, reason = adjudicate(v)
rows.append({
"rule": v["rule_id"], "idx": i, "project": "libcrc",
"file": rel(v["file"]), "line": v["line"],
"verdict": verdict, "reason": reason,
})
with open(OUT, "w", newline="") as fh:
w = csv.DictWriter(fh, fieldnames=["rule", "idx", "project", "file", "line", "verdict", "reason"])
w.writeheader()
w.writerows(rows)
tp = sum(r["verdict"] == "TP" for r in rows)
fp = sum(r["verdict"] == "FP" for r in rows)
print(f"wrote {OUT}: {len(rows)} findings ({tp} TP, {fp} FP, precision {tp/len(rows)*100:.1f}%)")
if __name__ == "__main__":
main()