Research-0876: printf-format portability audit (CERT FIO47-C / MISRA 21.6)¶
Date: 2026-05-30 Branch: fix/printf-format-portability Scope: every fork-added .c / .cpp / .h under core/src/, core/tools/, core/test/, cmd/, mcp-server/ — search for non-portable length-modifier specifiers (%lu, %ld, %lx, %lld, %llu, %llx).
1. Method¶
grep -rnE '%(lld|llu|llx|ld|lu|lx)\b' core/ cmd/ mcp-server/ \
--include='*.c' --include='*.cpp' --include='*.h' --include='*.cu' --include='*.cc' \
| grep -vE 'third_party|iqa|3rdparty|libsvm|cJSON|pdjson'
Each hit was triaged against the type of the formatted value (via git blame + struct declaration lookup), the cast (if any) applied at the call site, and the target platform's data model.
2. Findings¶
Class A — silent truncation on Windows LLP64 (BUG)¶
uint64_t formatted with %lu + (unsigned long) cast.
core/src/sycl/common.cpp:600—frame_counter(declareduint64_tatcommon.cpp:117) printed with%lu+(unsigned long)cast. On Windows LLP64unsigned longis 32 bits; values> 2^32 - 1truncate silently. Fixed:%" PRIu64 "+ drop cast.core/src/sycl/common.cpp:1060—timing_frames(declareduint64_tatcommon.cpp:111), same pattern. Fixed identically.
Linux LP64 happens to satisfy unsigned long == uint64_t so the bug is invisible there; the audit is forward-looking per memory project_windows_ci_sdk_pin.
Class B — portable but non-idiomatic for fixed-width types¶
int64_t / uint64_t formatted with %lld / %llu / %llx + explicit (long long) / (unsigned long long) / (unsigned long long) cast. C99 guarantees long long >= 64 bits and the matching format specifiers exist, so these are portable — but they hide the intent (future readers can't distinguish a load-bearing cast from a forgotten one).
core/src/libvmaf.c:923, 931, 942, 988, 999, 1040— six tiny-model loader log sites printingint64_t in_shape[]ONNX shape dims. Cast(long long). Converted toPRId64.core/src/sycl/dmabuf_import.cpp:338, 516— twouint64_t modifier(DRM format modifier) log sites. Cast(unsigned long long). Converted toPRIx64.core/test/test_motion_v2_simd.c:264— twouint64_tSAD values in divergence log. Cast(unsigned long long). Converted toPRIu64.
Class C — verified not-bugs, left as-is¶
core/tools/yuv_input.c:124, 133—off_t file_szprinted with(long long)+%lld.off_tis a POSIX non-fixed-width type; CERT FIO47-C recommends exactly this cast pattern (or(intmax_t)+%jd). No PRI macro applies. Leave.core/test/test_public_api_score.c:201— WindowsGetCurrentProcessId()(returnsDWORD) printed with(unsigned long)+%lu.DWORDis exactlyunsigned longon Windows. Format matches the type's exact spelling. Leave.core/src/feature/x86/adm_avx512.c:74-75—print_128_64debug macro in upstream Netflix code (under#if 0debug guard). Per fork rule 6, upstream-mirror files are reserved for the next upstream sync. Leave.
Class D — verified portable, not in scope¶
%zu for size_t is C99 standard and matches the type exactly. No issues found; ten+ such uses across the tree are correct.
3. Coverage summary¶
| Sweep target | Hits | Class A | Class B | Class C | Class D |
|---|---|---|---|---|---|
core/src/ (fork-added) | 17 | 2 | 9 | 1 | 5 |
core/tools/ (post-ADR-0700 fork edits) | 4 | 0 | 0 | 2 | 2 |
core/test/ (fork-added) | 5 | 0 | 2 | 1 | 2 |
cmd/, mcp-server/ | 0 | 0 | 0 | 0 | 0 |
Total fix candidates: 11 (Class A + B). All fixed in this PR.
4. Validation¶
meson setup build-cpu core -Denable_cuda=false -Denable_sycl=false— clean configure.ninja -C build-cpu— clean build, 726 targets.meson test -C build-cpu --suite=fast— 49/49 pass.grep -nE '%(lld|llu|llx|ld|lu|lx)\b' core/src/sycl/ core/src/libvmaf.c core/test/test_motion_v2_simd.c— empty.
SYCL / Windows builds are not exercised locally (no SYCL toolchain on this host, no Windows runner); the fix is type-correct via <cinttypes> / <inttypes.h> and follows the CERT-mandated portable form, so the audit's correctness claim is independent of the local validation surface.
5. Out of scope¶
- Upstream-mirror
print_128_64macro inadm_avx512.c— wait for next upstream sync. printf-vs-vmaf_logrouting audit — covered by ADR-0871 / commit4579a439ae.snprintfreturn-value-check audit — separate sweep; FIO34-C territory.
6. References¶
- CERT FIO47-C: "Use valid format strings".
- MISRA C:2012 Rule 21.6: "The Standard Library input/output functions shall not be used".
- C99 §7.8.1 —
<inttypes.h>PRI macros. - ISO C:
unsigned longis at least 32 bits;long longis at least 64 bits. - Windows LLP64 data model —
long/unsigned longare 32 bits;long long/unsigned long longare 64 bits;size_tis 64 bits. - Memory
project_windows_ci_sdk_pin— Windows CI rolled to 24H2 SDK; portability audit is forward-looking.