Summary
Multiple SF2 hydra table indices read from file data are used without bounds validation in tsf_load_presets(), causing heap out-of-bounds reads when loading a crafted .sf2 file.
Vulnerability Details
The function tsf_load_presets (tsf.h, starting at line 708) uses several 16-bit index values from SF2 sub-chunk records to traverse the hydra data structures. While the instrument index is properly validated at line 789 (if (whichInst >= hydra->instNum) continue;), five other index accesses lack equivalent bounds checks:
1. SampleID index into shdrs array (line 824) — Most critical
pshdr = &hydra->shdrs[pigen->genAmount.wordAmount];
pigen->genAmount.wordAmount can be 0–65535 (from file), but is not checked against hydra->shdrNum. Compare with the instrument check at line 789 which correctly validates the same pattern. This reads a 46-byte tsf_hydra_shdr structure from out-of-bounds heap memory. The garbage values for start, end, startLoop, endLoop, sampleRate are then used to configure audio regions.
2. presetBagNdx into pbags array (line 741)
ppbag = hydra->pbags + pphdr->presetBagNdx;
ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx;
presetBagNdx (tsf_u16) is not checked against hydra->pbagNum.
3. genNdx into pgens array (line 745)
ppgen = hydra->pgens + ppbag->genNdx;
ppgenEnd = hydra->pgens + ppbag[1].genNdx;
genNdx (tsf_u16) is not checked against hydra->pgenNum.
4. instBagNdx into ibags array (line 752/793)
pibag = hydra->ibags + pinst->instBagNdx;
pibagEnd = hydra->ibags + pinst[1].instBagNdx;
instBagNdx (tsf_u16) is not checked against hydra->ibagNum.
5. instGenNdx into igens array (line 755/798)
pigen = hydra->igens + pibag->instGenNdx;
pigenEnd = hydra->igens + pibag[1].instGenNdx;
instGenNdx (tsf_u16) is not checked against hydra->igenNum.
Impact
- Heap OOB read during file loading: A crafted
.sf2 file triggers OOB reads across multiple heap allocations when loaded via tsf_load(), tsf_load_memory(), or tsf_load_filename().
- Crash (denial of service): If OOB reads hit unmapped memory, the process crashes with SIGSEGV/access violation.
- Information disclosure: On hardened platforms, OOB-read data from the heap flows into audio region parameters which influence rendered audio output, potentially leaking heap contents through a side channel.
- Secondary OOB during playback: Corrupted
offset/loop_start values from OOB shdr reads are not clamped (only end and loop_end are clamped at lines 830–835), so voice->sourceSamplePosition (line 1634) can be initialized to an attacker-influenced value, causing further OOB reads from the fontSamples buffer during audio rendering (line 1295).
Suggested Fix
Add bounds checks before each index access, consistent with the existing instrument check at line 789:
// Line 824: Add SampleID bounds check (matching the instrument check at line 789)
if (pigen->genAmount.wordAmount >= hydra->shdrNum) continue;
pshdr = &hydra->shdrs[pigen->genAmount.wordAmount];
// Line 741: Add presetBagNdx bounds check
if (pphdr->presetBagNdx >= hydra->pbagNum || pphdr[1].presetBagNdx > hydra->pbagNum) continue;
// Line 745: Add genNdx bounds check
if (ppbag->genNdx >= hydra->pgenNum || ppbag[1].genNdx > hydra->pgenNum) continue;
// Line 752/793: Add instBagNdx bounds check
if (pinst->instBagNdx >= hydra->ibagNum || pinst[1].instBagNdx > hydra->ibagNum) continue;
// Line 755/798: Add instGenNdx bounds check
if (pibag->instGenNdx >= hydra->igenNum || pibag[1].instGenNdx > hydra->igenNum) continue;
Additionally, clamp zoneRegion.offset and zoneRegion.loop_start to fontSampleCount (similar to the existing clamps for end and loop_end at lines 830–835) to prevent secondary OOB during playback.
Severity
Medium-High (CVSS ~7.1). Network-reachable if the application loads user-supplied SF2 files (e.g., web-based MIDI players, game engines with modding support).
References
- CWE-125: Out-of-bounds Read
- The library is used in RetroArch/DOSBox Pure and various embedded/game projects
Summary
Multiple SF2 hydra table indices read from file data are used without bounds validation in
tsf_load_presets(), causing heap out-of-bounds reads when loading a crafted.sf2file.Vulnerability Details
The function
tsf_load_presets(tsf.h, starting at line 708) uses several 16-bit index values from SF2 sub-chunk records to traverse the hydra data structures. While the instrument index is properly validated at line 789 (if (whichInst >= hydra->instNum) continue;), five other index accesses lack equivalent bounds checks:1. SampleID index into
shdrsarray (line 824) — Most criticalpigen->genAmount.wordAmountcan be 0–65535 (from file), but is not checked againsthydra->shdrNum. Compare with the instrument check at line 789 which correctly validates the same pattern. This reads a 46-bytetsf_hydra_shdrstructure from out-of-bounds heap memory. The garbage values forstart,end,startLoop,endLoop,sampleRateare then used to configure audio regions.2.
presetBagNdxintopbagsarray (line 741)presetBagNdx(tsf_u16) is not checked againsthydra->pbagNum.3.
genNdxintopgensarray (line 745)genNdx(tsf_u16) is not checked againsthydra->pgenNum.4.
instBagNdxintoibagsarray (line 752/793)instBagNdx(tsf_u16) is not checked againsthydra->ibagNum.5.
instGenNdxintoigensarray (line 755/798)instGenNdx(tsf_u16) is not checked againsthydra->igenNum.Impact
.sf2file triggers OOB reads across multiple heap allocations when loaded viatsf_load(),tsf_load_memory(), ortsf_load_filename().offset/loop_startvalues from OOBshdrreads are not clamped (onlyendandloop_endare clamped at lines 830–835), sovoice->sourceSamplePosition(line 1634) can be initialized to an attacker-influenced value, causing further OOB reads from thefontSamplesbuffer during audio rendering (line 1295).Suggested Fix
Add bounds checks before each index access, consistent with the existing instrument check at line 789:
Additionally, clamp
zoneRegion.offsetandzoneRegion.loop_starttofontSampleCount(similar to the existing clamps forendandloop_endat lines 830–835) to prevent secondary OOB during playback.Severity
Medium-High (CVSS ~7.1). Network-reachable if the application loads user-supplied SF2 files (e.g., web-based MIDI players, game engines with modding support).
References