Repository navigation
Conversation
lusoris
force-pushed
the
fix/adm-tiny-frame-crash
branch
from
October 2, 2026 05:23
dfc7a12 to
11e7e7a
Compare
integer ADM crashes on any frame whose width or height is 16 or below. Two defects are behind it. adm_cm() converts ceil(log2(w) - 4), which is negative for a scale 0 band of 8 samples or fewer, to uint32_t, so every `>> shift_xhcub` shifts by 4294967295. And dwt2_src_indices_filt() loops to `h_half - 2` on an unsigned h_half, which wraps when scale 3 gets an input of 2 samples, the case for a 16 pixel dimension. float_adm does not define a score for these frames either: it reads out of bounds up to 8x8 and returns adm2 scores above 1 from 9x9 to 16x16. So refuse them in init() with -EINVAL and a message, instead of making the integer path compute a number the float reference does not have. The limit is 17 because scale 3 needs a DWT input of 3 samples. Frames of 17 and above are unchanged. Add a test_feature_extractor case: init fails for 1x1, 8x8, 16x16, 64x16 and 16x64 and succeeds for 17x17, 64x17, 17x64 and 64x64. Co-Authored-By: Claude Opus 5.5 <[email protected]>
adm_cuda computes the same ceil(log2(w) - 4) shift counts on the host (integer_adm_cuda.c) and is picked for the same frames when a CUDA context is present, so it must refuse what the CPU extractor refuses. Share ADM_MIN_DIM through integer_adm.h and check it at the top of init_fex_cuda(), before any CUDA object is created. Built with CUDA 13.4 and run on an RTX 4090: 16x16, 64x16 fail with "adm_cuda: invalid size" and exit 234, 24x24 scores as before. Co-Authored-By: Claude Opus 5.5 <[email protected]>
lusoris
force-pushed
the
fix/adm-tiny-frame-crash
branch
from
October 2, 2026 18:44
11e7e7a to
bee03b9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Integer ADM crashes on any frame whose width or height is 16 or below. This makes
init()refuse those frames with-EINVALand a message that names the limit. Fixes #1607.Why refuse instead of compute
#1607 asked whether these sizes should work or be refused, and nobody has answered. The history points at refusing: #1035 reported this crash in 2022, #1046 fixed it by rejecting
w <= 32 || h <= 32ininit()("ADM probably doesn't make sense as a metric at these very small patch sizes"), and 966be8d (#1485) dropped that check.float_admis the reference definition, and it does not define a score for these frames either. Same synthetic 4:2:0 content (3 frames, pseudo-random luma/chroma with a distortion of up to +-12),float_admat frame index 2:float_admheap-buffer-overflow READ of size 4inadm_dwt2_s(adm_tools.c:1091, called fromcompute_adm,adm.c:197; at 8x8 it is 32 bytes before a 2560-byte region); also 64x1, 64x2, 64x8, 1x64, 2x64, 8x64VMAF_feature_adm2_score1.383, 1.297, 1.397, 0.995, 1.402, 1.327, 1.224, 1.424 (9 to 16),adm_scale3up to 3.25ADM is a ratio of retained to original detail, so a score of 1.4 (and 3.1 for scale 3 at 16x16) is not a measurement. Making the integer path return numbers at these sizes would give it values its own reference does not have, so it refuses them.
Cause
Two defects behind the crash, both from #1607 and re-checked on master
8e7a1ac4:adm_cm()computes(uint32_t)ceil(log2(w) - 4)forshift_xhcubandshift_xvcub(- 3forshift_xdcub). For a scale 0 band of 8 samples or fewer, that is a frame dimension of 16 or less, the double is negative and converting it touint32_tis undefined; every>> shift_xhcubafter it shifts by 4294967295. The same expression is inadm_avx2.c(adm_cm_avx2),adm_avx512.c(adm_cm_avx512) and in the CUDA host code (integer_adm_cuda.c).dwt2_src_indices_filt()loopsfor (i = 1; i < h_half - 2; ++i)with anunsigned h_half. A 16-pixel dimension reaches scale 3 with a 2-sample input (16 to 8 to 4 to 2), soh_half == 1, the bound wraps to 4294967295 and the loop writes past the index table. Same forw_half.Where the boundary is: a dimension d reaches scale 3 as a DWT input of
ceil(d / 8)samples and the transform needs 3, which is d >= 17. That is also exactly whereceil(log2(band) - 4)stops being negative.Reproducer
Master
9e48141b, release build, x86-64, 16x16 4:2:0 clips cut from thesrc01pair (3 frames):libvmaf ERROR adm: invalid size (16x16), width and height must be at least 17, exit 234With
--model version=vmaf_v0.6.1instead of--feature adm, this branch refuses the 16x16 clip with the same message and exit 234.Fix
init()returns-EINVALand logs the size and the limit whenworhis belowADM_MIN_DIM(17, defined ininteger_adm.hwith the derivation). Frames of 17 and above take the same path as before. A second commit applies the same check at the top ofinit_fex_cuda(), becauseadm_cudacarries the same host expression and is picked for the same frames when a CUDA context exists; drop that commit if you would rather keep this CPU-only.The process exit status for the refused run changes from 139 (SIGSEGV) to 234 (
-EINVALas an unsigned byte).What this does not touch
git merge-tree).checkasm.post_dwt_sizesincheck_adm.chas a 16x16 entry, andcheck_adm_cmpasses half of it, an 8-sample band, toadm_cm()and its SIMD twins directly, not throughinit(). A UBSan build ofcheckasm --test=admwith-fsanitize=float-cast-overflowstill reports-1 is outside the range of representable values of type 'unsigned int'atinteger_adm.c:1567and the matching lines inadm_avx2.candadm_avx512.c(153 reports with this branch and feature/adm: fix out-of-bounds read at scale 3 for frame dimensions 17 to 32 #1599 and feature/adm: fix undefined rounding constant in adm_cm for frame widths 17 to 32 #1600 merged). Removing the sub-17 entries from that table, or clamping the shift in the helpers, is a separate decision; this change does not make either.Tests
New
test_adm_minimum_dimensioninlibvmaf/test/test_feature_extractor.c:init()fails with-EINVALfor 1x1, 8x8, 16x16, 64x16 and 16x64 and succeeds for 17x17, 64x17, 17x64 and 64x64. With master'sinteger_adm.cand the new test, the case fails ("adm init should reject a frame below 17 pixels in either dimension") in a release build, no sanitizer needed; with this changetest_feature_extractorpasses 5/5.Validation
x86-64 Linux (AVX-512 host), GCC 16.2.1, on master
9e48141b. The sanitizer builds use-Db_sanitize=address,undefined -Db_lto=falseand add-fsanitize=float-cast-overflow, which GCC'sundefinedgroup does not include.Rebased on master
9e48141b(2026-10-02). The size sweep and the CUDA run below were taken on8e7a1ac4and not repeated (integer_adm.c,adm_avx2.c,adm_avx512.candinteger_adm_cuda.care unchanged between the two, andinteger_adm.cdiffers only by one NEON dispatch line ininit(), after the lines this change touches); the reproducer table, themeson testcounts and the Netflix pair comparison were re-run on9e48141b. On the CUDA build,meson testis 24 of 25 on master and here (test_cuda_pic_preallocationfails on both), and the CLI output with--gpumask 0on the three Netflix pairs is identical to master.--cpumask 16and--cpumask -1: 174 runs per build.-Db_sanitize=address,undefinedplus-fsanitize=float-cast-overflow: 17 runs finish without a report, 157 report.64x17at--cpumask 16,shift exponent -5 is negativeinget_best15_from32()(adm_avx2.c:1350), the known AVX2 defect of feature/adm: keep get_best15_from32() inside its defined range on AVX2 #1635, content dependent and not size related.meson test(-Denable_float=true -Denable_checkasm=true): 25/25 on master, 25/25 here.-Db_sanitize=address,undefined -Db_lto=falsebuild: 22 pass and 3 fail on master, 22 pass and 3 fail here;test_predictandtest_pic_preallocationfail on LeakSanitizer reports andcheckasmaborts on aheap-buffer-overflowinadm_dwt2_16(integer_adm.c:2603on master), on both, and this change does not touch them. With feature/adm: fix out-of-bounds read at scale 3 for frame dimensions 17 to 32 #1599 and feature/adm: fix undefined rounding constant in adm_cm for frame widths 17 to 32 #1600 also merged (measured on8e7a1ac4, withoutcheckasm, not repeated): 25/25 release, 23/25 sanitizer, same two aborts.--feature psnr --feature float_ssim, give identical per-frame and pooled output on master and on this branch with the default CPU dispatch and with SIMD masked off (also against the three-way merge). No golden assertion changes.--feature adm_cuda: 16x16 and 64x16 are refused withadm_cuda: invalid size ...and exit 234, 24x24 scores. The behaviour ofadm_cudabelow 17 on master was not measured, and it was not compared with the CPU at any size.The workflow run on this PR needs a maintainer's approval.