Repository navigation
fix(svm): check every allocation in the bundled predictor, and move the Cppcheck lane - #1498
Merged
Merged
Conversation
cppcheck 2.19 reports 90 findings in this file that 2.13 never looked for: 88 nullPointerOutOfMemory, where a malloc result is dereferenced without a test, and the Cache class owning raw storage with neither a copy constructor nor an assignment operator. The analyser version comes from the CI runner image, so they appeared when the matrix moved to ubuntu-26.04. They are real, and ADR-1142 gives vendored code no exemption, so they are fixed rather than suppressed and the Cppcheck lane moves to 26.04 with the rest of the matrix. The Malloc macro now goes through a helper that reports and aborts on failure, which is the policy this file already follows at its realloc sites: OOM in a scoring path has no recovery model, and the callers here cannot unwind a half-built model. That covers 74 of the 88 in one place. On success the helper returns exactly what malloc returned. Cache's copy operations are deleted, as the Kernel class in the same file already does. Touching the file brought the whole of it into scope for the size limit, which 14 blocks exceeded. Each is split along the seams its own comments already marked: the solver into setup, pair selection, the two alpha-update branches, the gradient update and the finish; both working-set selections into their two scans; the trainer into its regression branch, weighted C, permuted inputs, the pairwise loop, the model head, the coefficient layout and the workspace free; the sigmoid trainer into initial point, gradient and line search; and the model parser's methods out of its class body. Two class bodies shrank by moving small accessors out. Every expression is the one it replaced, in the same order. Verified: cppcheck 2.19 with CI's exact flags goes from 90 findings to 0; no block exceeds 60 lines; the governance audit passes; the fast suite is 139 of 139 and test_svm_api passes. Scores are byte-identical: all 48 frames of the Netflix pair at --precision max match the pre-change binary exactly, and both checkerboard pairs give 35.0686714193046 and 7.985899011514694.
Replaces the open row #1494 added with the closed one, and adds the changelog fragment plus the rebase note that tells the next re-vendor what it would undo.
This was referenced Sep 19, 2026
lusoris
added a commit
that referenced
this pull request
Sep 19, 2026
…n the stale baseline (#1503) standards-gate.yml installed praetor at e4b35cb3c7fe (2026-09-18); main is at 846da5908d15. Before moving the pin, both engines and a CI-form go-install of 846da59 recorded the baseline on the same origin/master tree: all three produce byte-identical infraction sets, 1414 entries with the same fingerprints, so the bump changes no finding. The committed baseline said 1433 while the tree measures 1414. The 19 are two files cleaned in merged PRs without re-recording: core/src/svm.cpp 14 -> 0 (#1498) and core/test/test_ciede_neon.c 5 -> 0. A too-loose baseline fails nothing, so those entries were headroom for 19 new findings. Reported upstream as cordanaLLM/praetor#349.
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.
Summary
Moving CI to the newer runner image moved the analyser with it:
ubuntu-24.04carries cppcheck 2.13 andubuntu-26.04carries 2.19, with nothing older in its archive. 2.19 reported 90 findings incore/src/svm.cppthat 2.13 never looked for, and #1494 parked the Cppcheck lane on the old image rather than suppress them. This fixes them and moves the lane.nullPointerOutOfMemorymallocresult dereferenced without a test; 74 of them through theMallocmacronoCopyConstructor/noOperatorEqCacheclass owns raw heap storage and allowed itself to be copiedThe allocation fix follows the policy this file already had.
Mallocgoes through a helper that reports and aborts on failure, which is exactly what the same file does at itsreallocsites, where the comment reads "OOM in a hot scoring path — no recovery model". The callers here,predict.candbrisque.c, have no way to unwind a half-built model. On success the helper returns exactly whatmallocreturned, so no score can move.Cache's copy operations are deleted, as theKernelclass in the same file already does; a copy would double-free the head array and every cached column.Why the diff is large: the touched-file rule
Editing the file brought all of it into scope, and 14 blocks exceeded the 60-line limit. Each is split along the seams its own comments already marked:
Solver::Solve(237 lines)Solver::select_working_set,Solver_NU::select_working_setsvm_train(227 lines)sigmoid_trainsvm_cross_validation,svm_save_model,svm_check_parameter,svm_predict_values,svm_group_classesSVMModelParser(235-line template body),Solver,SVR_QTwo
realloc-growth blocks that were duplicated betweensvm_group_classesand the parameter check now share one helper. Every expression is the one it replaced, in the same order.Verification
ubuntu:26.04containerpraetorctl auditmeson test --suite=fasttest_svm_api(the fork's own libsvm API test)--precision max, every frame vs the pre-change binaryThe score check is the one that matters, and it is a comparison against a binary built from
master's source, not against remembered numbers.no docs needed: no user-discoverable surface changes. The contributor-facing consequence, that a future libsvm re-vendor would undo all of this, is in
docs/rebase-notes.md.Type
fix— bug fixrefactor— no behavior changeChecklist
make format && make lintis green locally.meson test -C build(139 of 139 fast).assertAlmostEqual(...)score in the Netflix golden Python tests.docs/state.md: the row ci: finish the ubuntu-26.04 move, give the Docs job room, and keep Cppcheck's analyser #1494 opened is replaced with the closed one.Deep-dive deliverables (ADR-0108)
.cppcheck-suppressions.txtstates there is no vendored tier, so suppressing was not an option, and the size limit is not optional either.AGENTS.mdinvariant note — carried indocs/rebase-notes.md, the cross-package invariant index the harness imports.changelog.d/fixed/svm-checked-allocation.md.docs/rebase-notes.md.Reproducer
To check the scores yourself, build
masterand this branch, score the Netflix pair with--precision max --jsonfrom each, and compare theframesarrays.Known follow-ups
svm_train,svm_cross_validation,sigmoid_train,svm_save_model) is reachable only fromcore/test/test_svm_api.c; scoring callssvm_predict. Whether the fork should keep compiling it is worth its own decision.