Skip to content

Commit 3e1ff74

Browse files
AliceLRsezero
authored andcommitted
Fix DMF loader crash/hang/slow load bugs found by libFuzzer.
* DMF: fix faulty bounds checks for INFO, SEQU, and SMPI chunks. * DMF: add numerous missing bounds checks for patterns and track data. * DMF: fix out-of-bounds reads caused by missing sample bounds check. * DMF: fix hangs caused by duplicate PATT chunks. * DMF: fix sample leaks caused by duplicate SMPD chunks. * DMF: fix slow loads caused by missing EOF check in DMFUnpack. * DMF: constify pointers derived from lpStream. Konstanty#58
1 parent ac308c0 commit 3e1ff74

File tree

1 file changed

+51
-20
lines changed

1 file changed

+51
-20
lines changed

src/load_dmf.cpp

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,12 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
8787
//---------------------------------------------------------------
8888
{
8989
const DMFHEADER *pfh = (DMFHEADER *)lpStream;
90-
DMFINFO *psi;
91-
DMFSEQU *sequ;
90+
const DMFINFO *psi;
91+
const DMFPATT *patt;
92+
const DMFSEQU *sequ;
9293
DWORD dwMemPos;
9394
BYTE infobyte[32];
94-
BYTE smplflags[MAX_SAMPLES], hasSMPI = 0;
95+
BYTE smplflags[MAX_SAMPLES], hasSMPI = 0, hasSMPD = 0;
9596

9697
if ((!lpStream) || (dwMemLength < 1024)) return FALSE;
9798
if ((pfh->id != 0x464d4444) || (!pfh->version) || (pfh->version & 0xF0)) return FALSE;
@@ -115,7 +116,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
115116
case 0x47534d43:
116117
psi = (DMFINFO *)(lpStream+dwMemPos);
117118
if (id == 0x47534d43) dwMemPos++;
118-
if ((psi->infosize > dwMemLength) || (psi->infosize + dwMemPos + 8 > dwMemLength)) goto dmfexit;
119+
if ((psi->infosize > dwMemLength) || (dwMemPos + 8 > dwMemLength - psi->infosize)) goto dmfexit;
119120
if ((psi->infosize >= 8) && (!m_lpszSongComments))
120121
{
121122
m_lpszSongComments = new char[psi->infosize]; // changed from CHAR
@@ -138,9 +139,10 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
138139
// "SEQU"
139140
case 0x55514553:
140141
sequ = (DMFSEQU *)(lpStream+dwMemPos);
141-
if ((sequ->seqsize >= dwMemLength) || (dwMemPos + sequ->seqsize + 12 > dwMemLength)) goto dmfexit;
142+
if ((sequ->seqsize >= dwMemLength) || (dwMemPos + 8 > dwMemLength - sequ->seqsize)) goto dmfexit;
143+
if (sequ->seqsize >= 4)
142144
{
143-
UINT nseq = sequ->seqsize >> 1;
145+
UINT nseq = (sequ->seqsize - 4) >> 1;
144146
if (nseq >= MAX_ORDERS-1) nseq = MAX_ORDERS-1;
145147
if (sequ->loopstart < nseq) m_nRestartPos = sequ->loopstart;
146148
for (UINT i=0; i<nseq; i++) Order[i] = (BYTE)sequ->sequ[i];
@@ -150,12 +152,12 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
150152

151153
// "PATT"
152154
case 0x54544150:
153-
if (!m_nChannels)
155+
patt = (DMFPATT *)(lpStream+dwMemPos);
156+
if ((patt->patsize >= dwMemLength) || (dwMemPos + 8 > dwMemLength - patt->patsize)) goto dmfexit;
157+
if (patt->patsize >= 4 && !m_nChannels)
154158
{
155-
DMFPATT *patt = (DMFPATT *)(lpStream+dwMemPos);
156159
UINT numpat;
157160
DWORD dwPos = dwMemPos + 11;
158-
if ((patt->patsize >= dwMemLength) || (dwMemPos + patt->patsize + 8 > dwMemLength)) goto dmfexit;
159161
numpat = patt->numpat;
160162
if (numpat > MAX_PATTERNS) numpat = MAX_PATTERNS;
161163
m_nChannels = patt->tracks;
@@ -164,7 +166,8 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
164166
if (m_nChannels < 4) m_nChannels = 4;
165167
for (UINT npat=0; npat<numpat; npat++)
166168
{
167-
DMFTRACK *pt = (DMFTRACK *)(lpStream+dwPos);
169+
const DMFTRACK *pt = (DMFTRACK *)(lpStream+dwPos);
170+
if (dwPos + 8 >= dwMemLength) break;
168171
#ifdef DMFLOG
169172
Log("Pattern #%d: %d tracks, %d rows\n", npat, pt->tracks, pt->ticks);
170173
#endif
@@ -174,7 +177,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
174177
if (ticks > 256) ticks = 256;
175178
if (ticks < 16) ticks = 16;
176179
dwPos += 8;
177-
if ((pt->jmpsize >= dwMemLength) || (dwPos + pt->jmpsize + 4 >= dwMemLength)) break;
180+
if ((pt->jmpsize >= dwMemLength) || (dwPos + 4 > dwMemLength - pt->jmpsize)) break;
178181
PatternSize[npat] = (WORD)ticks;
179182
MODCOMMAND *m = AllocatePattern(PatternSize[npat], m_nChannels);
180183
if (!m) goto dmfexit;
@@ -193,6 +196,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
193196
// Parse track global effects
194197
if (!glbinfobyte)
195198
{
199+
if (d+1 > dwPos) break;
196200
BYTE info = lpStream[d++];
197201
BYTE infoval = 0;
198202
if ((info & 0x80) && (d < dwPos)) glbinfobyte = lpStream[d++];
@@ -214,30 +218,40 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
214218
// Parse channels
215219
for (UINT i=0; i<tracks; i++) if (!infobyte[i])
216220
{
221+
if (d+1 > dwPos) break;
217222
MODCOMMAND cmd = {0,0,0,0,0,0};
218223
BYTE info = lpStream[d++];
219-
if (info & 0x80) infobyte[i] = lpStream[d++];
224+
if (info & 0x80)
225+
{
226+
if (d+1 > dwPos) break;
227+
infobyte[i] = lpStream[d++];
228+
}
220229
// Instrument
221230
if (info & 0x40)
222231
{
232+
if (d+1 > dwPos) break;
223233
cmd.instr = lpStream[d++];
224234
}
225235
// Note
226236
if (info & 0x20)
227237
{
238+
if (d+1 > dwPos) break;
228239
cmd.note = lpStream[d++];
229240
if ((cmd.note) && (cmd.note < 0xfe)) cmd.note &= 0x7f;
230241
if ((cmd.note) && (cmd.note < 128)) cmd.note += 24;
231242
}
232243
// Volume
233244
if (info & 0x10)
234245
{
246+
if (d+1 > dwPos) break;
235247
cmd.volcmd = VOLCMD_VOLUME;
236248
cmd.vol = (lpStream[d++]+3)>>2;
237249
}
238250
// Effect 1
239251
if (info & 0x08)
240252
{
253+
if (d+2 > dwPos) break;
254+
241255
BYTE efx = lpStream[d++];
242256
BYTE eval = lpStream[d++];
243257
switch(efx)
@@ -259,6 +273,8 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
259273
// Effect 2
260274
if (info & 0x04)
261275
{
276+
if (d+2 > dwPos) break;
277+
262278
BYTE efx = lpStream[d++];
263279
BYTE eval = lpStream[d++];
264280
switch(efx)
@@ -289,6 +305,8 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
289305
// Effect 3
290306
if (info & 0x02)
291307
{
308+
if (d+2 > dwPos) break;
309+
292310
BYTE efx = lpStream[d++];
293311
BYTE eval = lpStream[d++];
294312
switch(efx)
@@ -372,22 +390,24 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
372390
#endif
373391
if (dwPos + 8 >= dwMemLength) break;
374392
}
375-
dwMemPos += patt->patsize + 8;
376393
}
394+
dwMemPos += patt->patsize + 8;
377395
break;
378396

379397
// "SMPI": Sample Info
380398
case 0x49504d53:
381399
{
382400
hasSMPI = 1;
383-
DMFSMPI *pds = (DMFSMPI *)(lpStream+dwMemPos);
384-
if (pds->size <= dwMemLength - dwMemPos)
401+
const DMFSMPI *pds = (DMFSMPI *)(lpStream+dwMemPos);
402+
if ((pds->size >= dwMemLength) || (dwMemPos + 8 > dwMemLength - pds->size)) goto dmfexit;
403+
if (pds->size >= 1)
385404
{
386405
DWORD dwPos = dwMemPos + 9;
387406
m_nSamples = pds->samples;
388407
if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1;
389408
for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++)
390409
{
410+
if (dwPos >= dwMemPos + pds->size + 8) break;
391411
UINT namelen = lpStream[dwPos];
392412
smplflags[iSmp] = 0;
393413
if (dwPos+namelen+1+sizeof(DMFSAMPLE) > dwMemPos+pds->size+8) break;
@@ -398,7 +418,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
398418
m_szNames[iSmp][rlen] = 0;
399419
}
400420
dwPos += namelen + 1;
401-
DMFSAMPLE *psh = (DMFSAMPLE *)(lpStream+dwPos);
421+
const DMFSAMPLE *psh = (DMFSAMPLE *)(lpStream+dwPos);
402422
MODINSTRUMENT *psmp = &Ins[iSmp];
403423
psmp->nLength = psh->len;
404424
psmp->nLoopStart = psh->loopstart;
@@ -425,7 +445,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
425445
{
426446
DWORD dwPos = dwMemPos + 8;
427447
UINT ismpd = 0;
428-
for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++)
448+
for (UINT iSmp=1; iSmp<=m_nSamples && !hasSMPD; iSmp++)
429449
{
430450
ismpd++;
431451
DWORD pksize;
@@ -458,6 +478,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
458478
}
459479
dwPos += pksize;
460480
}
481+
hasSMPD = 1;
461482
dwMemPos = dwPos;
462483
}
463484
break;
@@ -520,8 +541,8 @@ static BYTE DMFReadBits(DMF_HTREE *tree, UINT nbits)
520541
{
521542
tree->bitnum--;
522543
} else
523-
{
524-
tree->bitbuf = (tree->ibuf < tree->ibufmax) ? *(tree->ibuf++) : 0;
544+
if (tree->ibuf < tree->ibufmax) {
545+
tree->bitbuf = *(tree->ibuf++);
525546
tree->bitnum = 7;
526547
}
527548
if (tree->bitbuf & 1) x |= bitv;
@@ -576,14 +597,24 @@ int DMFUnpack(LPBYTE psample, LPBYTE ibuf, LPBYTE ibufmax, UINT maxlen)
576597
DMF_HTREE tree;
577598
UINT actnode;
578599
BYTE value, sign, delta = 0;
579-
600+
580601
memset(&tree, 0, sizeof(tree));
581602
tree.ibuf = ibuf;
582603
tree.ibufmax = ibufmax;
583604
DMFNewNode(&tree);
584605
value = 0;
606+
607+
if (tree.ibuf >= ibufmax) return tree.ibuf - ibuf;
608+
585609
for (UINT i=0; i<maxlen; i++)
586610
{
611+
if ((tree.ibuf >= tree.ibufmax) && (!tree.bitnum))
612+
{
613+
#ifdef DMFLOG
614+
Log("DMFUnpack: unexpected EOF at output byte %d / %d\n", i, maxlen);
615+
#endif
616+
break;
617+
}
587618
actnode = 0;
588619
sign = DMFReadBits(&tree, 1);
589620
do

0 commit comments

Comments
 (0)