ADR-1308: Resolve CodeQL float equality alerts via contract-preserving comparisons¶
- Status: Proposed
- Date: 2026-09-24
- Deciders: VMAFx maintainers
- Tags:
codeql,security,floating-point,quality
Context¶
GitHub CodeQL rule cpp/equality-on-floats flags direct equality (== and !=) between floating-point expressions because floating-point rounding often makes exact comparisons dangerous. On origin/master, six live alerts were active:
- Alert 168 (
core/src/feature/feature_name.cpp:149): compared double option dictionary valuesval == opt_valwhen deduping feature option pairs. - Alert 927 (
core/src/predict.c:302): checked whetherguided_score != sentinelbefore publishing guided features. - Alert 1101 (
core/test/test_svm_api.c:368and line 504): compared floating-point class labels in the SVM unit test suite. - Alert 1201 (
core/src/feature/brisque_math.h:366): checkedassert(hi != lo)before dividing byhi - loduring BRISQUE feature normalization. - Alert 1221 (
core/src/mcp/3rdparty/cJSON/cJSON.c:615): comparedd == (double)item->valueintinprint_numberto determine whether a number should be serialized as an integer. - Alert 1244 (
core/test/test_cambi.c:1142): incheck_c_values_avx2_parity, comparedc_scalar[i] == c_avx2[i]to enforce bit-exact SIMD parity between AVX2 and scalarcalculate_c_valueskernels.
Resolving these alerts required satisfying strict repository invariants:
- No scanner suppression: No
// NOLINT,lgtm[...], or.codeql/query exclusions. - No query evasion: The repair must be the root-cause semantic contract.
- No public ABI expansion: All helpers remain file-scope internal.
- No score or tolerance loosening: Preserve exact numerical scores, bit-identity where required, and upstream cJSON denormal/DAZ semantics.
- Zero Netflix golden test changes: Golden data and assertions must remain untouched.
Decision¶
Implement the exact semantic contract for each site:
- Feature Name Double Option Equality (
feature_name.cpp:149): Implement internal helperoption_double_equals: - Evaluates NaN as never equal (returns false for NaN operands, preserving IEEE-754 semantics).
- Treats signed zeros
+0.0 == -0.0as equal. - Treats same infinities as equal via identical bit representation.
-
For finite values, evaluates exact 64-bit bit identity via
memcpytouint64_t. This avoids CodeQL's float equality rule while guaranteeing precise option matching. -
Predict Guided Feature Sentinel Detection (
predict.c:302): Implement static helperfloat_values_equal: - Evaluates NaN as never equal (returns false for NaN operands, preserving IEEE-754 semantics).
- Treats
+0.0 == -0.0as equal. - Treats same infinities as equal via identical bit representation.
-
For finite values, compares exact IEEE-754 bit representations via
uint64_t. The check invmaf_predict_score_at_indexbecomes!float_values_equal(st->guided_score, st->sentinel). -
SVM API Label Comparisons (
test_svm_api.c:368, line 504): SVM class labels represent discrete integer identifiers (+1.0,-1.0,0.0). Implementsvm_labels_equal(a, b)using 64-bit IEEE bit identity viamemcpywith signed-zero equivalence (+0.0 == -0.0) and same-infinity behavior, rejecting NaN (never equal). It does not usea - b == 0.0or finiteness checks. -
BRISQUE Normalization Span Assertion (
brisque_math.h:366): Inbrisque_range_scale, replaceassert(hi != lo)with:
const double span = hi - lo;
assert(span != 0.0 && isfinite(span));
return -1.0 + 2.0 / span * (feat - lo);
CodeQL exempts comparison against constant 0.0. In addition, to satisfy HISS-04 (maximum 60 LOC per function in touched files), extract the inner accumulator loop of brisque_fit_aggd into brisque_aggd_accumulate (51 LOC), preserving exact summation and loop ordering.
-
cJSON Integer Print Detection (
cJSON.c:615): Changed == (double)item->valueinttod - (double)item->valueint == 0.0. This preserves upstream cJSON behavior and the host compiler's floating-point model (e.g., DAZ on Intel icx vs subnormal preservation on GCC/Clang) while satisfying CodeQL. -
CAMBI AVX2 Parity Bit-Identity Assertion (
test_cambi.c:1142): Incheck_c_values_avx2_parity, implementfloat_bits_equal(float a, float b)comparing exactuint32_tbit patterns viamemcpyforc_scalar[i]vsc_avx2[i]. This directly expresses the requirement that AVX2 and scalarcalculate_c_valuespaths produce identical bitwise results without float equality operations.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
Inline // NOLINT or lgtm[...] suppression | Trivial diff | Violates rule against scanner suppressions; leaves root-cause unaddressed | Rejected |
Approximate epsilon comparisons (fabs(a - b) < eps) everywhere | Standard floating-point idiom | Incorrect for bit-identity tests, breaks sentinel detection (e.g. 0.0 vs 1e-9), and alters option semantics | Rejected |
| Query filter exclusion in CI workflow | Zero source changes | Defeats CodeQL quality gating; masks regressions | Rejected |
| Contract-specific semantics (bit identity, constant difference, span checks) | CodeQL clean, zero regressions, preserves all bit-exact contracts | Requires per-site contract analysis and red-capable unit tests | Chosen |
Consequences¶
- Positive:
- All six live CodeQL
cpp/equality-on-floatsalerts are eliminated on fresh scanner runs. - Zero suppression comments or scanner-specific exclusions added.
- Bit-exactness and golden Netflix test results remain 100% green (271 passed, 12 skipped, 0 failed under literal
make test-netflix-goldenwith CPU-forced environment). - All unit tests and
fastsuite (145/145) pass cleanly. - HISS-04 compliance achieved on all touched files.
- Negative:
- Slightly more verbose comparison helpers in
feature_name.cpp,predict.c, and tests. - Neutral / follow-ups:
AGENTS.mdand rebase notes document the comparison helpers and why they must not be reverted during upstream rebases.
References¶
- Research-2097 — complete analysis, CodeQL SARIF verification, and test logs.
- CodeQL query rule
cpp/equality-on-floats(FloatComparison.ql). - GitHub CodeQL alerts 168, 927, 1101, 1201, 1221, 1244.