Hardening pass: critical/high/memory/patch-bank fixes + VCF buffer overflow #2

Merged
jens merged 7 commits from hardening into master 2026-07-27 20:44:34 +02:00
2 changed files with 6 additions and 1 deletions
Showing only changes of commit d713ca50bb - Show all commits
+1
View File
@@ -11,6 +11,7 @@ Findings from a code-review pass over `src/plug` and `src/synth` (2026-07-27, br
- [x] **Host block sizes larger than `SYNTH_MAX_BUFSIZE` (8192) caused a stack buffer overflow or a permanent deadlock.** `processBlock` (`src/plug/PluginProcessor.cpp:547`) now chunks rendering into `SYNTH_MAX_BUFSIZE`-sized passes instead of writing the host's full block size into a fixed 8192-sample stack buffer; `JaySynth::renderNextBlock` (`src/plug/JaySynth.cpp:977`) clamps the same way and correctly holds a pending MIDI event over between passes.
- [x] **Unchecked file stream could be null, crashing on plugin construction.** `JaySynth::JaySynth` (`src/plug/JaySynth.cpp:55-57`) now guards the wavetable file read against `createInputStream()` returning null.
- [x] **Parameter-changing calls raced with real-time audio rendering — no lock protected them.** `JaySynth::setParameter`/`setControl`/`setPerVoiceControl`/`setParam`/`ClearControls` (`src/plug/JaySynth.cpp`) now take `ScopedLock sl(lock)`, matching `renderNextBlock` and the note/controller handlers.
- [x] **Found while building `tools/testhost`'s regression suite for this list: the block-size fix above wasn't sufficient on its own.** `VCF_CalcCoeff_LPF`/`_HPF`/`_BPF` (`src/synth/vcf.c`) advance the coefficient pointer once per *(sample, filter-section)* pair - a 4th-order filter uses `FILTER_MAX_ORDER/2` = 2 cascaded sections per sample - but `VCF_SetBufsize` only allocated `pCoef` with `bufsize` entries, not `bufsize * sections`. Any host block between 4097 and 8192 samples (inclusive of the now-correctly-chunked range from the fix above) with a 4th-order filter selected wrote past the end of that buffer, corrupting adjacent heap memory (confirmed with AddressSanitizer: `heap-buffer-overflow`, `WRITE of size 8`, exactly 8 bytes past a 393216-byte/8192-`vcf_coef_t` allocation). Fixed by sizing the allocation for the worst case, `bufsize*(FILTER_MAX_ORDER/2)`. Verified with ASan across the full range (4097, 8192, 16384, 20000 samples) after the fix - zero errors.
## High — real-time audio-thread safety (fixed)
+5 -1
View File
@@ -142,7 +142,11 @@ void VCF_SetBufsize(vcf_t *pObj, UINT32 size)
return;
pObj->pOut = (synth_float_t*)SynthCheckAlloc(malloc(pObj->bufsize*sizeof(synth_float_t)));
pObj->pCoef = (vcf_coef_t*)SynthCheckAlloc(malloc(pObj->bufsize*sizeof(vcf_coef_t)));
// VCF_CalcCoeff_*() advances the coefficient pointer once per
// (sample, filter-section) pair - a 4th-order filter uses
// FILTER_MAX_ORDER/2 cascaded 2nd-order sections per sample, so the
// buffer needs that many times bufsize entries, not just bufsize.
pObj->pCoef = (vcf_coef_t*)SynthCheckAlloc(malloc(pObj->bufsize*(FILTER_MAX_ORDER/2)*sizeof(vcf_coef_t)));
pObj->pCoeff_last = pObj->pCoef;
VCF_Coeff_update(pObj);