Skip to content

fix(metrics): CPU-side scoring NaN/UB guards across PSNR/SSIM/MS-SSIM/ADM/CAMBI/MOTION - #641

Merged
lusoris merged 1 commit into
masterfrom
fix/r6-cpu-scoring-nan-ub-guards
Jun 4, 2026
Merged

lusoris merged 1 commit into
masterfrom
fix/r6-cpu-scoring-nan-ub-guards

Conversation

@lusoris

@lusoris lusoris commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Round-6 audit — 11 CPU-path scoring edge-case fixes:

  • APSNR log10(0) (integer_psnr.c): guard sse[i]==0 (identical frames) → emit psnr_max[i]; bound loop to enable_chroma?3:1 planes; remove erroneous *2 in cap formula.
  • MS-SSIM pow(neg,frac) NaN (ms_ssim.c): wrap l and c in fabs() before pow(), mirroring the existing guard for s.
  • MS-SSIM size overflow (ms_ssim.c): (double)w * (double)h instead of (double)(w*h) — int32 overflow at widths > 46340.
  • float SSIM/MS-SSIM convert_to_db (float_ssim.c, float_ms_ssim.c): guard score >= 1.0 → return max_db; log10(1−score) with score≥1 yields NaN.
  • iqa assert abort (iqa/ssim_tools.c): replace assert(!args) with runtime if (args && !mr) return INFINITY; the assert fired on the Rouse MS-SSIM path.
  • *ADM score_aim uninit (adm.c, integer_adm.c): add *score_aim = 1.0f in the den==0 branch; caller reads it unconditionally.
  • float_adm harmonic mean NaN (float_adm.c): guard score+score_aim==0 → return 0.0 instead of 0/0 NaN through MAX().
  • float_adm adm_skip_scale0 sentinel (float_adm.c): emit explicit 0.0 for scale-0 when skip flag is set, mirroring float_vif.c:296.
  • MOTION wrong bilinear stride (motion.c): pass caller-supplied img1_stride/img2_stride to motion_scale_bilinear; recomputing from width ignores alignment padding.
  • MOTION OOM crash (motion.c): NULL-guard both aligned_malloc calls; return motion_scale0 gracefully on OOM.
  • CAMBI v_band_size overflow (cambi.c): compute in signed int; return -EINVAL with diagnostic when non-positive — prevents uint16_t wrap-to-~65535 and OOB writes.

ADR-0108 deliverables

  • Research digest: no digest needed: targeted bug-fix cluster
  • Decision matrix: no alternatives: only-one-way fixes (see ADR-1033 § Alternatives considered)
  • AGENTS.md invariant: no rebase-sensitive invariants
  • Reproducer: meson test -C build --suite=fast + run vmaf on identical ref/dist pair — should produce clamped PSNR not -Inf
  • CHANGELOG fragment: changelog.d/fixed/1033-cpu-scoring-nan-ub-guards.md
  • Rebase note: docs/rebase-notes.md top entry
  • state.md update: T-CPU-SCORING-NAN-UB-GUARDS-2026-06-04 opened and tracked

Files changed

core/src/feature/{integer_psnr,ms_ssim,float_ssim,float_ms_ssim,adm,float_adm,integer_adm,motion,cambi}.c + iqa/ssim_tools.c + ADR-1033 + changelog fragment.

Notes

  • DRAFT only — do not merge until CI is green.
  • SKIP=semgrep-local used per CachyOS io_uring memlock exception (pre-existing project rule, see fix/semgrep-local-io-uring-memlock).
  • Related: fix/r6-metric-scoring-guards (ADR-1024) covers APSNR + PSNR UB + ADM subset — this PR is a superset that covers all 11 remaining bugs.

🤖 Generated with Claude Code

…BI/MOTION (ADR-1033)

Rebased onto master (post-PR#638 squash) — resolved UNION conflicts in
docs/rebase-notes.md and docs/state.md only; no code conflicts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@lusoris
lusoris force-pushed the fix/r6-cpu-scoring-nan-ub-guards branch from 8bf52d8 to 750540b Compare June 4, 2026 04:19
@lusoris
lusoris marked this pull request as ready for review June 4, 2026 04:19
Copilot AI review requested due to automatic review settings June 4, 2026 04:19
@lusoris
lusoris merged commit a295f4a into master Jun 4, 2026
67 of 106 checks passed
@lusoris
lusoris deleted the fix/r6-cpu-scoring-nan-ub-guards branch June 4, 2026 04:19
lusoris added a commit that referenced this pull request Jun 4, 2026
…-precedence x2

Rebased onto master (post-PR#638, #641, #642 squash) — resolved UNION
conflicts in docs/rebase-notes.md and docs/state.md only; no code conflicts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Jun 4, 2026
…-precedence x2 (#637)

Rebased onto master (post-PR#638, #641, #642 squash) — resolved UNION
conflicts in docs/rebase-notes.md and docs/state.md only; no code conflicts.

Co-authored-by: Lusoris <lusoris@pm.me>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Jun 4, 2026
…V queue sync (ADR-1034)

Rebased onto master (post-PR#638, #641, #642, #637 squash) — resolved UNION
conflicts in docs/adr/README.md, docs/rebase-notes.md, docs/state.md only;
no code conflicts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Jun 4, 2026
…V queue sync (ADR-1034) (#640)

Rebased onto master (post-PR#638, #641, #642, #637 squash) — resolved UNION
conflicts in docs/adr/README.md, docs/rebase-notes.md, docs/state.md only;
no code conflicts.

Co-authored-by: Lusoris <lusoris@pm.me>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Jun 4, 2026
…tion vertical halo (ADR-1030)

Rebased onto master (post-PR#638, #641, #642, #637, #640 squash) — resolved
UNION conflicts in docs/adr/README.md, docs/rebase-notes.md, docs/state.md;
no code conflicts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Jun 4, 2026
…tion vertical halo (ADR-1030) (#639)

Rebased onto master (post-PR#638, #641, #642, #637, #640 squash) — resolved
UNION conflicts in docs/adr/README.md, docs/rebase-notes.md, docs/state.md;
no code conflicts.

Co-authored-by: Lusoris <lusoris@pm.me>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@lusoris
lusoris removed the request for review from Copilot June 4, 2026 04:40
@lusoris lusoris added this to the 1.0.0 — First release milestone Sep 4, 2026
lusoris added a commit that referenced this pull request Sep 30, 2026
…ocumentation gate

The standards gate now installs praetor main 25451d8 from the remote
module proxy (PRAETOR_REF was f41e74d on master). Every praetor-managed
file is regenerated with that engine's own code: adopt (with
--verification-max-entries 200000), sync, compile-context and
devcontainer --source-root at the pin, which keeps the vmafx-dev-mcp
base image.

Baseline (ADR-1351): f41e74d re-records 185 entries on this tree with no
growth, 25451d8 records 431. All 246 new fingerprints are engine
changes, replayed on the same tree against each commit's parent:
d7a3778 adds 61 process exits from library code (Python 51, Go 5,
Rust 5), 025bbc6 adds 142 long Python functions and 36 recursive ones,
53e7594 adds 7 Go calls without a deadline. They are recorded with
--allow-increase and a reason.

Documentation gate: praetor fixed cordanaLLM/praetor#532, #533 and #534,
so the gate now passes here. .standards.yaml raises max_files to 8192
and max_file_bytes to 4 MiB, and style-excludes the generated ADR
index, the ADR row fragments and testdata fixtures, the files the
repository's own markdownlint hook already skips. The other 75
findings are fixed in the source: the 14 predictor model cards and
their template in predictor_train.py (pinned by
test_predictor_card_markdown.py), an MD013 re-enable in the SYCL
overview and a fence language in the ADR fragments README. The figure
engine under tools/figures/, the docs-figures target, a .gitattributes
block and a workflow step arrive with it.

Praetor defects, filed or tracked upstream and worked around here:
- adopt still refuses to extend the Makefile block because of computed
  targets such as $(BUILD_DIR): (#537, open). GNU Make finds no
  docs-lint or docs-figures rule outside the block, so the block is
  praetor's own DocumentationMakefileBlock() text.
- The repository-wide dist/ rule hid tools/figures/dist/; .gitignore
  re-includes it (#591).
- black, ruff and markdownlint would rewrite or flag the locked
  figure engine; their pre-commit hooks skip tools/figures/ (#578).
- Praetor requires the retired numbered workspace root to be ignored
  (#641, filed with this change). The ADR-1277 contract check accepts
  only that rule inside praetor's block, skips tools/markdownlint/ when
  scanning for references and still fails on a local directory. ADR-1351
  amends ADR-1277.

Also: repository.default_branch: master renders the ruleset for master;
REUSE.toml labels the vendored interfig sources and the player bundle;
adopt's praetorctl pre-tool hook registrations in .claude/settings.json,
.codex/hooks.json and .gemini/settings.json are left out, since audit
does not verify them and they change every agent session.
T-PRAETOR-DOCS-GATE-LIMITS-2026-09-28 is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Sep 30, 2026
…ocumentation gate

The standards gate now installs praetor main 25451d8 from the remote
module proxy (PRAETOR_REF was f41e74d on master). Every praetor-managed
file is regenerated with that engine's own code: adopt (with
--verification-max-entries 200000), sync, compile-context and
devcontainer --source-root at the pin, which keeps the vmafx-dev-mcp
base image.

Baseline (ADR-1351): f41e74d re-records 185 entries on this tree with no
growth, 25451d8 records 431. All 246 new fingerprints are engine
changes, replayed on the same tree against each commit's parent:
d7a3778 adds 61 process exits from library code (Python 51, Go 5,
Rust 5), 025bbc6 adds 142 long Python functions and 36 recursive ones,
53e7594 adds 7 Go calls without a deadline. They are recorded with
--allow-increase and a reason.

Documentation gate: praetor fixed cordanaLLM/praetor#532, #533 and #534,
so the gate now passes here. .standards.yaml raises max_files to 8192
and max_file_bytes to 4 MiB, and style-excludes the generated ADR
index, the ADR row fragments and testdata fixtures, the files the
repository's own markdownlint hook already skips. The other 75
findings are fixed in the source: the 14 predictor model cards and
their template in predictor_train.py (pinned by
test_predictor_card_markdown.py), an MD013 re-enable in the SYCL
overview and a fence language in the ADR fragments README. The figure
engine under tools/figures/, the docs-figures target, a .gitattributes
block and a workflow step arrive with it.

Praetor defects, filed or tracked upstream and worked around here:
- adopt still refuses to extend the Makefile block because of computed
  targets such as $(BUILD_DIR): (#537, open). GNU Make finds no
  docs-lint or docs-figures rule outside the block, so the block is
  praetor's own DocumentationMakefileBlock() text.
- The repository-wide dist/ rule hid tools/figures/dist/; .gitignore
  re-includes it (#591).
- black, ruff and markdownlint would rewrite or flag the locked
  figure engine; their pre-commit hooks skip tools/figures/ (#578).
- Praetor requires the retired numbered workspace root to be ignored
  (#641, filed with this change). The ADR-1277 contract check accepts
  only that rule inside praetor's block, skips tools/markdownlint/ when
  scanning for references and still fails on a local directory. ADR-1351
  amends ADR-1277.

Also: repository.default_branch: master renders the ruleset for master;
REUSE.toml labels the vendored interfig sources and the player bundle;
adopt's praetorctl pre-tool hook registrations in .claude/settings.json,
.codex/hooks.json and .gemini/settings.json are left out, since audit
does not verify them and they change every agent session.
T-PRAETOR-DOCS-GATE-LIMITS-2026-09-28 is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Sep 30, 2026
…ocumentation gate

The standards gate now installs praetor main 25451d8 from the remote
module proxy (PRAETOR_REF was f41e74d on master). Every praetor-managed
file is regenerated with that engine's own code: adopt (with
--verification-max-entries 200000), sync, compile-context and
devcontainer --source-root at the pin, which keeps the vmafx-dev-mcp
base image.

Baseline (ADR-1351): f41e74d re-records 185 entries on this tree with no
growth, 25451d8 records 431. All 246 new fingerprints are engine
changes, replayed on the same tree against each commit's parent:
d7a3778 adds 61 process exits from library code (Python 51, Go 5,
Rust 5), 025bbc6 adds 142 long Python functions and 36 recursive ones,
53e7594 adds 7 Go calls without a deadline. They are recorded with
--allow-increase and a reason.

Documentation gate: praetor fixed cordanaLLM/praetor#532, #533 and #534,
so the gate now passes here. .standards.yaml raises max_files to 8192
and max_file_bytes to 4 MiB, and style-excludes the generated ADR
index, the ADR row fragments and testdata fixtures, the files the
repository's own markdownlint hook already skips. The other 75
findings are fixed in the source: the 14 predictor model cards and
their template in predictor_train.py (pinned by
test_predictor_card_markdown.py), an MD013 re-enable in the SYCL
overview and a fence language in the ADR fragments README. The figure
engine under tools/figures/, the docs-figures target, a .gitattributes
block and a workflow step arrive with it.

Praetor defects, filed or tracked upstream and worked around here:
- adopt still refuses to extend the Makefile block because of computed
  targets such as $(BUILD_DIR): (#537, open). GNU Make finds no
  docs-lint or docs-figures rule outside the block, so the block is
  praetor's own DocumentationMakefileBlock() text.
- The repository-wide dist/ rule hid tools/figures/dist/; .gitignore
  re-includes it (#591).
- black, ruff and markdownlint would rewrite or flag the locked
  figure engine; their pre-commit hooks skip tools/figures/ (#578).
- Praetor requires the retired numbered workspace root to be ignored
  (#641, filed with this change). The ADR-1277 contract check accepts
  only that rule inside praetor's block, skips tools/markdownlint/ when
  scanning for references and still fails on a local directory. ADR-1351
  amends ADR-1277.

Also: repository.default_branch: master renders the ruleset for master;
REUSE.toml labels the vendored interfig sources and the player bundle;
adopt's praetorctl pre-tool hook registrations in .claude/settings.json,
.codex/hooks.json and .gemini/settings.json are left out, since audit
does not verify them and they change every agent session.
T-PRAETOR-DOCS-GATE-LIMITS-2026-09-28 is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Sep 30, 2026
…ocumentation gate

The standards gate now installs praetor main 25451d8 from the remote
module proxy (PRAETOR_REF was f41e74d on master). Every praetor-managed
file is regenerated with that engine's own code: adopt (with
--verification-max-entries 200000), sync, compile-context and
devcontainer --source-root at the pin, which keeps the vmafx-dev-mcp
base image.

Baseline (ADR-1351): f41e74d re-records 185 entries on this tree with no
growth, 25451d8 records 431. All 246 new fingerprints are engine
changes, replayed on the same tree against each commit's parent:
d7a3778 adds 61 process exits from library code (Python 51, Go 5,
Rust 5), 025bbc6 adds 142 long Python functions and 36 recursive ones,
53e7594 adds 7 Go calls without a deadline. They are recorded with
--allow-increase and a reason.

Documentation gate: praetor fixed cordanaLLM/praetor#532, #533 and #534,
so the gate now passes here. .standards.yaml raises max_files to 8192
and max_file_bytes to 4 MiB, and style-excludes the generated ADR
index, the ADR row fragments and testdata fixtures, the files the
repository's own markdownlint hook already skips. The other 75
findings are fixed in the source: the 14 predictor model cards and
their template in predictor_train.py (pinned by
test_predictor_card_markdown.py), an MD013 re-enable in the SYCL
overview and a fence language in the ADR fragments README. The figure
engine under tools/figures/, the docs-figures target, a .gitattributes
block and a workflow step arrive with it.

Praetor defects, filed or tracked upstream and worked around here:
- adopt still refuses to extend the Makefile block because of computed
  targets such as $(BUILD_DIR): (#537, open). GNU Make finds no
  docs-lint or docs-figures rule outside the block, so the block is
  praetor's own DocumentationMakefileBlock() text.
- The repository-wide dist/ rule hid tools/figures/dist/; .gitignore
  re-includes it (#591).
- black, ruff and markdownlint would rewrite or flag the locked
  figure engine; their pre-commit hooks skip tools/figures/ (#578).
- Praetor requires the retired numbered workspace root to be ignored
  (#641, filed with this change). The ADR-1277 contract check accepts
  only that rule inside praetor's block, skips tools/markdownlint/ when
  scanning for references and still fails on a local directory. ADR-1351
  amends ADR-1277.

Also: repository.default_branch: master renders the ruleset for master;
REUSE.toml labels the vendored interfig sources and the player bundle;
adopt's praetorctl pre-tool hook registrations in .claude/settings.json,
.codex/hooks.json and .gemini/settings.json are left out, since audit
does not verify them and they change every agent session.
T-PRAETOR-DOCS-GATE-LIMITS-2026-09-28 is closed.
lusoris added a commit that referenced this pull request Oct 2, 2026
…each with its size and the upstream change that ends it (ADR-1479 to ADR-1486)

The reference for code inherited from Netflix/vmaf is Netflix's source;
a difference needs an ADR. The upstream parity audit of 2026-10-02 found
deliberate differences that had none of their own, or whose ADR
(ADR-1033) names neither upstream's behaviour nor the size:

- ADR-1479 ciede on 4:2:2: chroma flags (fork PR #1050); 0.153 on 48 of
  48 frames; Netflix/vmaf#1611.
- ADR-1480 speed_temporal buffers at speed_prescale above 1 (#1643);
  up to 195, upstream segfaults on two fixtures; Netflix/vmaf#1627.
- ADR-1481 a failing extractor fails the run (#871); status only, 78
  probe runs where upstream is silent and 88 where it crashes.
- ADR-1482 integer adm on frames of 17 to 32 pixels (#1473, #1507);
  scale 3 up to 0.23; Netflix/vmaf#1599, #1600.
- ADR-1483 odd-sized chroma planes round up (4f08d32); psnr_cb / cr
  up to 0.684 / 0.826 dB, ciede 0.198.
- ADR-1484 float_ms_ssim magnitude before pow() (#641, ADR-1033 item 2);
  NaN upstream on the 10 px checkerboard; Netflix/vmaf#1665.
- ADR-1485 apsnr of a plane without error (#641, item 1); 114 against
  60 dB; Netflix/vmaf#1666.
- ADR-1486 float_motion scale-1 stride (#641, item 9); up to 25.1;
  Netflix/vmaf#1667.

Each ADR gives upstream's file and line at Netflix 9e48141b, the fork's
lines, the reason found in the fork's pull request, commit or code, and
the measured size from the audit. Documentation only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant