--- stsoundlib/Ymload.cpp.orig 2016-07-26 16:04:42 UTC +++ stsoundlib/Ymload.cpp @@ -51,61 +51,98 @@ static void signeSample(ymu8 *ptr,yms32 } } -char *mstrdup(char *in) +void myFree(void **pPtr) +{ + if (*pPtr) free(*pPtr); + *pPtr = NULL; +} + +char *mstrdup(const char *in) { char *out = (char*)malloc(strlen(in)+1); if (out) strcpy(out,in); return out; } -ymu32 readMotorolaDword(ymu8 **ptr) +ymu32 readMotorolaDword(ymu8 **ptr, ymint *ptr_size) { -ymu32 n; +ymu32 n = 0; ymu8 *p = *ptr; - - n = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; - p+=4; - *ptr = p; + if (*ptr_size>=4) + { + n = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; + p+=4; + *ptr = p; + } + (*ptr_size)+=4; return n; } -ymu16 readMotorolaWord(ymu8 **ptr) +ymu16 readMotorolaWord(ymu8 **ptr, ymint *ptr_size) { -ymu16 n; +ymu16 n = 0; ymu8 *p = *ptr; - - n = (p[0]<<8)|p[1]; - p+=2; - *ptr = p; + if (*ptr_size>=2) + { + n = (p[0]<<8)|p[1]; + p+=2; + *ptr = p; + } + (*ptr_size)+=2; return n; } -ymchar *readNtString(ymchar **ptr) +ymchar *readNtString(ymchar **ptr, ymint *ptr_size) { ymchar *p; +ymint len = 0; - p = mstrdup(*ptr); - (*ptr) += strlen(*ptr)+1; + if (*ptr_size<=0) + { + (*ptr_size)-=1; + return mstrdup(""); + } + p=*ptr; + while(!*p) + { + p++; + ptr_size--; + len++; + if (*ptr_size==0) + { + (*ptr_size)-=1; + return mstrdup(""); + } + } + + p = mstrdup(*ptr); + (*ptr) += len+1; return p; } -yms32 ReadLittleEndian32(ymu8 *pLittle) +yms32 ReadLittleEndian32(ymu8 *pLittle, ymint ptr_size) { - yms32 v = ( (pLittle[0]<<0) | + yms32 v = 0; + if (ptr_size>=4) + { + v = ( (pLittle[0]<<0) | (pLittle[1]<<8) | (pLittle[2]<<16) | (pLittle[3]<<24)); - + } return v; } -yms32 ReadBigEndian32(ymu8 *pBig) +yms32 ReadBigEndian32(ymu8 *pBig, ymint ptr_size) { - yms32 v = ( (pBig[0]<<24) | + yms32 v = 0; + if (ptr_size>=4) + { + v = ( (pBig[0]<<24) | (pBig[1]<<16) | (pBig[2]<<8) | - (pBig[3]<<0)); - + (pBig[3]<<0)); + } return v; } @@ -114,6 +151,13 @@ unsigned char *CYmMusic::depackFile(void lzhHeader_t *pHeader; ymu8 *pNew; ymu8 *pSrc; + ymint ptr_left = fileSize; + ymint dummy; + + if (ptr_left < (ymint)sizeof(lzhHeader_t)) + { + return pBigMalloc; + } pHeader = (lzhHeader_t*)pBigMalloc; @@ -123,8 +167,6 @@ unsigned char *CYmMusic::depackFile(void return pBigMalloc; } - fileSize = (ymu32)-1; - if (pHeader->level != 0) // NOTE: Endianness works because value is 0 { // Compression LH5, header !=0 : Error. free(pBigMalloc); @@ -133,7 +175,8 @@ unsigned char *CYmMusic::depackFile(void return NULL; } - fileSize = ReadLittleEndian32((ymu8*)&pHeader->original); + dummy = 4; + fileSize = ReadLittleEndian32((ymu8*)&pHeader->original, dummy); pNew = (ymu8*)malloc(fileSize); if (!pNew) { @@ -144,10 +187,20 @@ unsigned char *CYmMusic::depackFile(void } pSrc = pBigMalloc+sizeof(lzhHeader_t)+pHeader->name_lenght; // NOTE: Endianness works because name_lenght is a byte + ptr_left -= sizeof(lzhHeader_t)+pHeader->name_lenght; pSrc += 2; // skip CRC16 + ptr_left -= 2; - const int packedSize = ReadLittleEndian32((ymu8*)&pHeader->packed); + dummy = 4; + const int packedSize = ReadLittleEndian32((ymu8*)&pHeader->packed, dummy); + + if (packedSize > ptr_left) + { + setLastError("File too small"); + free(pNew); + return pBigMalloc; + } // alloc space for depacker and depack data CLzhDepacker *pDepacker = new CLzhDepacker; @@ -229,19 +282,29 @@ ymbool CYmMusic::ymDecode(void) { ymu8 *pUD; ymu8 *ptr; + ymint ptr_size = fileSize; ymint skip; ymint i; ymu32 sampleSize; yms32 tmp; ymu32 id; - - id = ReadBigEndian32((unsigned char*)pBigMalloc); + if (ptr_size < 4) + { + setLastError("File too small"); + return YMFALSE; + } + id = ReadBigEndian32((unsigned char*)pBigMalloc, ptr_size); switch (id) { - case 'YM2!': // MADMAX specific. + case 0x594d3221 /*'YM2!'*/: // MADMAX specific. songType = YM_V2; nbFrame = (fileSize-4)/14; + if (nbFrame == 0) + { + setLastError("No frames in file"); + return YMFALSE; + } loopFrame = 0; ymChip.setClock(ATARI_CLOCK); setPlayerRate(50); @@ -256,9 +319,14 @@ ymbool CYmMusic::ymDecode(void) pSongPlayer = mstrdup("YM-Chip driver."); break; - case 'YM3!': // Standart YM-Atari format. + case 0x594d3321 /*'YM3!'*/: // Standart YM-Atari format. songType = YM_V3; nbFrame = (fileSize-4)/14; + if (nbFrame == 0) + { + setLastError("No frames in file"); + return YMFALSE; + } loopFrame = 0; ymChip.setClock(ATARI_CLOCK); setPlayerRate(50); @@ -273,11 +341,24 @@ ymbool CYmMusic::ymDecode(void) pSongPlayer = mstrdup("YM-Chip driver."); break; - case 'YM3b': // Standart YM-Atari format + Loop info. + case 0x594d3362 /*'YM3b'*/: // Standart YM-Atari format + Loop info. + if (ptr_size < 4) + { + setLastError("File too small"); + return YMFALSE; + } pUD = (ymu8*)(pBigMalloc+fileSize-4); songType = YM_V3; - nbFrame = (fileSize-4)/14; - loopFrame = ReadLittleEndian32(pUD); + nbFrame = (fileSize-8)/14; + if (nbFrame == 0) + { + setLastError("No frames in file"); + return YMFALSE; + } + { + ymint dummy = 4; + loopFrame = ReadLittleEndian32(pUD, dummy); + } ymChip.setClock(ATARI_CLOCK); setPlayerRate(50); pDataStream = pBigMalloc+4; @@ -291,35 +372,62 @@ ymbool CYmMusic::ymDecode(void) pSongPlayer = mstrdup("YM-Chip driver."); break; - case 'YM4!': // Extended ATARI format. + case 0x594d3421 /*'YM4!'*/: // Extended ATARI format. setLastError("No more YM4! support. Use YM5! format."); return YMFALSE; break; - case 'YM5!': // Extended YM2149 format, all machines. - case 'YM6!': // Extended YM2149 format, all machines. + case 0x594d3521 /*'YM5!'*/: // Extended YM2149 format, all machines. + case 0x594d3621 /*'YM6!'*/: // Extended YM2149 format, all machines. + if (ptr_size < 12) + { + setLastError("File too small"); + return YMFALSE; + } if (strncmp((const char*)(pBigMalloc+4),"LeOnArD!",8)) { setLastError("Not a valid YM format !"); return YMFALSE; } ptr = pBigMalloc+12; - nbFrame = readMotorolaDword(&ptr); - setAttrib(readMotorolaDword(&ptr)); - nbDrum = readMotorolaWord(&ptr); - ymChip.setClock(readMotorolaDword(&ptr)); - setPlayerRate(readMotorolaWord(&ptr)); - loopFrame = readMotorolaDword(&ptr); - skip = readMotorolaWord(&ptr); + ptr_size -= 12; + nbFrame = readMotorolaDword(&ptr, &ptr_size); + setAttrib(readMotorolaDword(&ptr, &ptr_size)); + nbDrum = readMotorolaWord(&ptr, &ptr_size); + ymChip.setClock(readMotorolaDword(&ptr, &ptr_size)); + setPlayerRate(readMotorolaWord(&ptr, &ptr_size)); + loopFrame = readMotorolaDword(&ptr, &ptr_size); + skip = readMotorolaWord(&ptr, &ptr_size); ptr += skip; + ptr_size -= skip; + if (ptr_size <= 0) + { + setLastError("File too small"); + return YMFALSE; + } if (nbDrum>0) { - pDrumTab=(digiDrum_t*)malloc(nbDrum*sizeof(digiDrum_t)); + pDrumTab=(digiDrum_t*)calloc(nbDrum, sizeof(digiDrum_t)); for (i=0;i= 0x80000000) + { + setLastError("To big drumtab"); + goto error_out; + } + if (ptr_size<(ymint)pDrumTab[i].size) + { + setLastError("File too small"); + goto error_out; + } pDrumTab[i].pData = (ymu8*)malloc(pDrumTab[i].size); memcpy(pDrumTab[i].pData,ptr,pDrumTab[i].size); if (attrib&A_DRUM4BITS) @@ -328,23 +436,26 @@ ymbool CYmMusic::ymDecode(void) ymu8 *pw = pDrumTab[i].pData; for (j=0;j>7; + *pw = ymVolumeTable[(*pw)&15]>>7; + pw++; } } ptr += pDrumTab[i].size; - } - else - { - pDrumTab[i].pData = NULL; + ptr_size -= pDrumTab[i].size; } } attrib &= (~A_DRUM4BITS); } - pSongName = readNtString((char**)&ptr); - pSongAuthor = readNtString((char**)&ptr); - pSongComment = readNtString((char**)&ptr); + pSongName = readNtString((char**)&ptr, &ptr_size); + pSongAuthor = readNtString((char**)&ptr, &ptr_size); + pSongComment = readNtString((char**)&ptr, &ptr_size); + if (ptr_size <= 0) + { + setLastError("File too small"); + goto error_out; + } songType = YM_V5; - if (id=='YM6!') + if (id==0x594d3621/*'YM6!'*/) { songType = YM_V6; pSongType = mstrdup("YM 6"); @@ -353,13 +464,28 @@ ymbool CYmMusic::ymDecode(void) { pSongType = mstrdup("YM 5"); } + if ((nbFrame >= 0x08000000) || (nbFrame < 0)) + { + setLastError("Too many frames"); + goto error_out; + } + if (ptr_size < (ymint)(nbFrame * 16)) + { + setLastError("File too small"); + goto error_out; + } pDataStream = ptr; streamInc = 16; setAttrib(A_STREAMINTERLEAVED|A_TIMECONTROL); pSongPlayer = mstrdup("YM-Chip driver."); break; - case 'MIX1': // ATARI Remix digit format. + case 0x4d495831 /*'MIX1'*/: // ATARI Remix digit format. + if (ptr_size < 12) + { + setLastError("File too small"); + return YMFALSE; + } if (strncmp((const char*)(pBigMalloc+4),"LeOnArD!",8)) { @@ -367,23 +493,50 @@ ymbool CYmMusic::ymDecode(void) return YMFALSE; } ptr = pBigMalloc+12; + ptr_size -= 12; songType = YM_MIX1; - tmp = readMotorolaDword(&ptr); + tmp = readMotorolaDword(&ptr, &ptr_size); setAttrib(0); if (tmp&1) setAttrib(A_DRUMSIGNED); - sampleSize = readMotorolaDword(&ptr); - nbMixBlock = readMotorolaDword(&ptr); + sampleSize = readMotorolaDword(&ptr, &ptr_size); + nbMixBlock = readMotorolaDword(&ptr, &ptr_size); + if (ptr_size <= 0) + { + setLastError("File too small"); + goto error_out; + } + if (sampleSize <= 0) + { + setLastError("Invalid sampleSize"); + goto error_out; + } + if (nbMixBlock <= 0) + { + setLastError("Invalid number of mixblocks"); + goto error_out; + } pMixBlock = (mixBlock_t*)malloc(nbMixBlock*sizeof(mixBlock_t)); for (i=0;i=0x80000000) + { + setLastError("Invalid sampleSize"); + goto error_out; + } + if (ptr_size < (ymint)sampleSize) + { + setLastError("File too small"); + goto error_out; } - pSongName = readNtString((char**)&ptr); - pSongAuthor = readNtString((char**)&ptr); - pSongComment = readNtString((char**)&ptr); pBigSampleBuffer = (unsigned char*)malloc(sampleSize); memcpy(pBigSampleBuffer,ptr,sampleSize); @@ -400,8 +553,8 @@ ymbool CYmMusic::ymDecode(void) break; - case 'YMT1': // YM-Tracker - case 'YMT2': // YM-Tracker + case 0x594d5431 /*'YMT1'*/: // YM-Tracker + case 0x594d5432 /*'YMT2'*/: // YM-Tracker /*; ; Format du YM-Tracker-1 ; @@ -418,33 +571,55 @@ ymbool CYmMusic::ymDecode(void) ; NT Music comment ; nb digi * */ + if (ptr_size < 12) + { + setLastError("File too small"); + return YMFALSE; + } + if (strncmp((const char*)(pBigMalloc+4),"LeOnArD!",8)) { setLastError("Not a valid YM format !"); return YMFALSE; } ptr = pBigMalloc+12; + ptr_size -= 12; songType = YM_TRACKER1; - nbVoice = readMotorolaWord(&ptr); - setPlayerRate(readMotorolaWord(&ptr)); - nbFrame= readMotorolaDword(&ptr); - loopFrame = readMotorolaDword(&ptr); - nbDrum = readMotorolaWord(&ptr); - attrib = readMotorolaDword(&ptr); - pSongName = readNtString((char**)&ptr); - pSongAuthor = readNtString((char**)&ptr); - pSongComment = readNtString((char**)&ptr); + nbVoice = readMotorolaWord(&ptr, &ptr_size); + setPlayerRate(readMotorolaWord(&ptr, &ptr_size)); + nbFrame= readMotorolaDword(&ptr, &ptr_size); + loopFrame = readMotorolaDword(&ptr, &ptr_size); + nbDrum = readMotorolaWord(&ptr, &ptr_size); + attrib = readMotorolaDword(&ptr, &ptr_size); + pSongName = readNtString((char**)&ptr, &ptr_size); + pSongAuthor = readNtString((char**)&ptr, &ptr_size); + pSongComment = readNtString((char**)&ptr, &ptr_size); + if (ptr_size < 0) + { + setLastError("File too small"); + return YMFALSE; + } if (nbDrum>0) { - pDrumTab=(digiDrum_t*)malloc(nbDrum*sizeof(digiDrum_t)); + pDrumTab=(digiDrum_t*)calloc(nbDrum, sizeof(digiDrum_t)); for (i=0;i<(ymint)nbDrum;i++) { - pDrumTab[i].size = readMotorolaWord(&ptr); + pDrumTab[i].size = readMotorolaWord(&ptr, &ptr_size); + if (ptr_size < 0) + { + setLastError("File too small"); + goto error_out; + } pDrumTab[i].repLen = pDrumTab[i].size; - if ('YMT2' == id) + if (0x594d5432/*'YMT2'*/ == id) { - pDrumTab[i].repLen = readMotorolaWord(&ptr); // repLen - readMotorolaWord(&ptr); // flag + pDrumTab[i].repLen = readMotorolaWord(&ptr, &ptr_size); // repLen + readMotorolaWord(&ptr, &ptr_size); // flag + if (ptr_size < 0) + { + setLastError("File too small"); + goto error_out; + } } if (pDrumTab[i].repLen>pDrumTab[i].size) { @@ -453,19 +628,27 @@ ymbool CYmMusic::ymDecode(void) if (pDrumTab[i].size) { + if (pDrumTab[i].size >= 0x80000000) + { + setLastError("Drumtab to big"); + goto error_out; + } + if (ptr_size<(ymint)pDrumTab[i].size) + { + setLastError("File too small"); + goto error_out; + } + pDrumTab[i].pData = (ymu8*)malloc(pDrumTab[i].size); memcpy(pDrumTab[i].pData,ptr,pDrumTab[i].size); ptr += pDrumTab[i].size; - } - else - { - pDrumTab[i].pData = NULL; + ptr_size -= pDrumTab[i].size; } } } ymTrackerFreqShift = 0; - if ('YMT2' == id) + if (0x594d5432/*'YMT2'*/ == id) { ymTrackerFreqShift = (attrib>>28)&15; attrib &= 0x0fffffff; @@ -476,18 +659,33 @@ ymbool CYmMusic::ymDecode(void) pSongType = mstrdup("YM-T1"); } + if ((nbVoice > MAX_VOICE) || (nbVoice < 0)) + { + setLastError("Too many voices"); + goto error_out; + } + if ((nbFrame >= (ymint)(0x80000000 / (MAX_VOICE * (sizeof(ymTrackerLine_t))))) || (nbFrame < 0)) /* ymTrackerLine_t has a 2^N size */ + { + setLastError("Too many frames"); + goto error_out; + } + if (ptr_size < (ymint)(sizeof(ymTrackerLine_t) * nbVoice * nbFrame)) + { + setLastError("File too small"); + goto error_out; + } pDataStream = ptr; ymChip.setClock(ATARI_CLOCK); ymTrackerInit(100); // 80% de volume maxi. - streamInc = 16; + streamInc = 16; /* not needed, since this is only used for YMx formats */ setTimeControl(YMTRUE); pSongPlayer = mstrdup("Universal Tracker"); break; default: - setLastError("Unknow YM format !"); + setLastError("Unknown YM format !"); return YMFALSE; break; } @@ -498,6 +696,25 @@ ymbool CYmMusic::ymDecode(void) } return YMTRUE; +error_out: + for (i=0;i0) + { + myFree((void **)&pDrumTab); + nbDrum=0; + } + myFree((void **)&pSongName); + myFree((void **)&pSongAuthor); + myFree((void **)&pSongComment); + myFree((void **)&pSongType); /* <- never needed, but we keep it for purity */ + myFree((void **)&pSongPlayer); /* <- never needed, but we keep it for purity */ + myFree((void **)&pMixBlock); + myFree((void **)&pBigSampleBuffer); /* <- never needed, but we keep it for purity */ + return YMFALSE; } @@ -645,12 +862,6 @@ ymbool CYmMusic::loadMemory(void *pBlock return YMTRUE; } -void myFree(void **pPtr) -{ - if (*pPtr) free(*pPtr); - *pPtr = NULL; -} - void CYmMusic::unLoad(void) {