Skip to content

Commit

Permalink
Add missing checks when loading and playing .mkj files
Browse files Browse the repository at this point in the history
Fix the following issues in src/mkj.cpp:
* Check number of channels before loading instruments data.
  This fixes a heap-based buffer overflow in CmkjPlayer::load()
  (issue #87).
* Check number of notes befor calculating size of song data
  to avoid interger overflows as well as out-of-bounds reads
  later in update(). (Size of song data vs. used data is really
  hilarious, but that's the way it is.)
* Fail loading if there was an error while reading file data.
* Also in update(), end the song if invalid data is encountered.
  That avoids integer overflows or out-of-range OPL writes.

This commit fixes CVE-2019-14692.

Fixes: #87
  • Loading branch information
miller-alex authored and Malvineous committed May 11, 2020
1 parent b48ac59 commit b5fb32c
Showing 1 changed file with 26 additions and 0 deletions.
26 changes: 26 additions & 0 deletions src/mkj.cpp
Expand Up @@ -45,18 +45,35 @@ bool CmkjPlayer::load(const std::string &filename, const CFileProvider &fp)

// load
maxchannel = f->readInt(2);
if (maxchannel > 9 || maxchannel < 0) {
fp.close(f);
return false;
}
// load and store the channel instruments.
for(i = 0; i < maxchannel; i++) {
for (j = 0; j < 8; j++) {
inst[i].value[j] = f->readInt(2);
}
}
maxnotes = f->readInt(2);
if (maxnotes < 1 ||
// Larger values would need code changes to avoid integer overflows:
maxnotes > 0x7fff / (maxchannel + 1) ||
// That's what gets actually accessed in update(): [yeah, it's weird]
maxnotes - 1 + 3 * maxchannel > (maxchannel + 1) * maxnotes) {
fp.close(f);
return false;
}
delete[] songbuf;
songbuf = new short [(maxchannel+1)*maxnotes];
for(i = 0; i < maxchannel; i++) channel[i].defined = f->readInt(2);
for(i = 0; i < (maxchannel + 1) * maxnotes; i++)
songbuf[i] = f->readInt(2);

if (f->error()) {
fp.close(f);
return false;
}
AdPlug_LogWrite("CmkjPlayer::load(\"%s\"): loaded file ver %.2f, %d channels,"
" %d notes/channel.\n", filename.c_str(), ver, maxchannel,
maxnotes);
Expand Down Expand Up @@ -102,25 +119,34 @@ bool CmkjPlayer::update()
case 15: opl->write(0xa0 + c,0x63); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break;
case 255: // delay
channel[c].songptr += maxchannel;
if (songbuf[channel[c].songptr] < 0)
goto bad_data; // avoid integer overflow
channel[c].pstat = songbuf[channel[c].songptr];
break;
case 254: // set octave
channel[c].songptr += maxchannel;
if ((songbuf[channel[c].songptr] | 7) != 7)
goto bad_data; // value out of range
channel[c].octave = songbuf[channel[c].songptr];
break;
case 253: // set speed
channel[c].songptr += maxchannel;
if (songbuf[channel[c].songptr] < 0)
goto bad_data; // avoid integer overflow
channel[c].speed = songbuf[channel[c].songptr];
break;
case 252: // set waveform
channel[c].songptr += maxchannel;
if ((songbuf[channel[c].songptr] - 300 | 0xff) != 0xff)
goto bad_data; // value out of range
channel[c].waveform = songbuf[channel[c].songptr] - 300;
if(c > 2)
opl->write(0xe0 + c + (c+6),channel[c].waveform);
else
opl->write(0xe0 + c,channel[c].waveform);
break;
case 251: // song end
bad_data:
for(i = 0; i < maxchannel; i++) channel[i].songptr = i;
songend = true;
return false;
Expand Down

0 comments on commit b5fb32c

Please sign in to comment.