SDL_mixer: libmodplug: Fix misc. loader crashes and leaks found by libFuzzer:

From ac3bd35e5a9877861ca3c701ae9cdf9147eb7b33 Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Wed, 23 Jun 2021 07:50:02 +0300
Subject: [PATCH] libmodplug: Fix misc. loader crashes and leaks found by
 libFuzzer:

Patchset by Alice Rowan: https://github.com/Konstanty/libmodplug/pull/58

* AMF (DSMI): fix out-of-bounds reads caused by missing order list
       bounds checks.
* DBM: fix leaks caused by duplicate instrument chunks being loaded.
* FAR: fix out-of-bounds reads due to not correctly bounding the
       maximum pattern read size.
* IT:  fix out-of-bounds reads in the IT sample decompressors caused
       by allowing ITReadBits to read past the end of the buffer.
* MED: fix out-of-bounds reads due to a faulty MMD2PLAYSEQ bounds check.
* MED: fix out-of-bounds reads due to bad sample bounding.
* MED: fix out-of-bounds reads due to bad block name bounding (and
       potential missing nul terminators).
* OKT: fix out-of-bounds reads due to incorrect OKTSAMPLE bounding.
* OKT: fix out-of-bounds reads due to bad chunk header and order
       list bounding.
* OKT: fix playback errors caused by skipping the first two orders in
       the order list.
* S3M: fix out-of-bounds reads due to missing order list bounds check.
* S3M: fix out-of-bounds reads due to missing offset list bounds check.
* S3M: fix out-of-bounds reads due to missing panning table check.
* STM: fix pattern leaks and pattern size corruption caused by missing
       MAX_PATTERNS check.
* ULT: fix out-of-bounds reads due to incorrect event bounding.
* WAV: fix out-of-bounds reads due to not bounds checking the fmt chunk.
* WAV: fix hangs caused by missing chunk length bounds check.
* WAV: constify pointers derived from lpStream.
* XM:  fix out-of-bounds reads due to broken XMSAMPLEHEADER check.
* XM:  fix out-of-bounds reads due to missing pattern data checks.
* XM:  fix slow loads caused by bad bounding in instrument/sample
       loops, add other various missing bounds checks.

- Fix AMS loader crash and slow load bugs found by libFuzzer:

* AMS: fix AMS out-of-bounds reads due to missing song comments checks.
* AMS: fix AMS out-of-bounds reads due to missing order list check.
* AMS: fix AMS out-of-bounds reads due to missing pattern/track checks.
* AMS: fix AMS2 out-of-bounds reads due to missing/broken instrument
       and envelope bounds checks.
* AMS: fix AMS2 out-of-bounds reads due to missing sample bounds checks.
* AMS: fix ReadSample out-of-bounds reads due to overflow in packed
       size bounds check.
* AMS: fix AMSUnpack out-of-bounds reads due to missing RLE unpacking
       bounds checks.
* AMS: reduce AMSUnpack slow loads by rejecting samples with truncated
       or invalid RLE.
* AMS: reduce AMSUnpack slow loads by shrinking samples if their packed
       size couldn't possibly store them.
* AMS: constify pointers derived from lpStream.

- 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.

- Fix MDL loader crash bugs found by libFuzzer:

* MDL: fix out-of-bounds reads due to missing info chunk bounds check.
* MDL: fix out-of-bounds reads due to a missing bounds check when
       loading instruments.
* MDL: fix out-of-bounds reads and other bugs due to bad envelope
       bounding and missing duplicate envelope chunk checks.
* MDL: fix out-of-bounds reads due to broken track bounds checks.

- Fix MT2 loader crashes and hangs found by libFuzzer:

* MT2: fix out-of-bounds reads due to missing nDrumDataLen check.
* MT2: fix out-of-bounds reads due to missing pattern/track checks.
* MT2: fix out-of-bounds reads due to broken/nonsensical instrument
       bounds checks.
* MT2: fix out-of-bounds reads due to missing sample data length
       bounds check.
* MT2: fix out-of-bounds reads due to bad checks on group structs.
* MT2: fix hangs caused by overflows preventing the data chunk size
       bounds check from working.
* MT2: constify pattern data pointer derived from lpStream.

- Fix PSM loader crash bugs found by libFuzzer:

* PSM: fix out-of-bounds reads due to dereferencing lpStream before
       any bounds checks.
* PSM: fix out-of-bounds reads due to reading pPsmPat.data from the
       stack instead of the input buffer.
* PSM: fix out-of-bounds reads due to invalid samples in patterns.
* PSM: fix missing pattern length byte-swapping.
* PSM: constify pattern data pointer derived from lpStream.
---
 external/libmodplug-0.8.9.0/src/load_amf.cpp |  4 +
 external/libmodplug-0.8.9.0/src/load_ams.cpp | 77 +++++++++++++---
 external/libmodplug-0.8.9.0/src/load_dbm.cpp |  6 ++
 external/libmodplug-0.8.9.0/src/load_dmf.cpp | 71 ++++++++++----
 external/libmodplug-0.8.9.0/src/load_far.cpp | 17 ++--
 external/libmodplug-0.8.9.0/src/load_it.cpp  | 21 +++--
 external/libmodplug-0.8.9.0/src/load_mdl.cpp | 20 ++--
 external/libmodplug-0.8.9.0/src/load_med.cpp | 19 ++--
 external/libmodplug-0.8.9.0/src/load_mid.cpp | 97 +++++++++++++-------
 external/libmodplug-0.8.9.0/src/load_mt2.cpp | 37 +++++---
 external/libmodplug-0.8.9.0/src/load_okt.cpp | 13 ++-
 external/libmodplug-0.8.9.0/src/load_pat.cpp |  2 +-
 external/libmodplug-0.8.9.0/src/load_psm.cpp | 13 ++-
 external/libmodplug-0.8.9.0/src/load_s3m.cpp |  5 +
 external/libmodplug-0.8.9.0/src/load_stm.cpp |  1 +
 external/libmodplug-0.8.9.0/src/load_ult.cpp |  3 +-
 external/libmodplug-0.8.9.0/src/load_wav.cpp |  9 +-
 external/libmodplug-0.8.9.0/src/load_xm.cpp  | 30 +++---
 external/libmodplug-0.8.9.0/src/mmcmp.cpp    | 39 ++++----
 external/libmodplug-0.8.9.0/src/sndfile.cpp  |  7 +-
 20 files changed, 325 insertions(+), 166 deletions(-)

diff --git a/external/libmodplug-0.8.9.0/src/load_amf.cpp b/external/libmodplug-0.8.9.0/src/load_amf.cpp
index 82ea867..0b9356c 100644
--- a/external/libmodplug-0.8.9.0/src/load_amf.cpp
+++ b/external/libmodplug-0.8.9.0/src/load_amf.cpp
@@ -315,8 +315,12 @@ BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, const DWORD dwMemLength)
 			PatternSize[iOrd] = 64;
 			if (pfh->version >= 14)
 			{
+				if (dwMemPos + m_nChannels * sizeof(USHORT) + 2 > dwMemLength) return FALSE;
 				PatternSize[iOrd] = bswapLE16(*(USHORT *)(lpStream+dwMemPos));
 				dwMemPos += 2;
+			} else
+			{
+				if (dwMemPos + m_nChannels * sizeof(USHORT) > dwMemLength) return FALSE;
 			}
 			ptracks[iOrd] = (USHORT *)(lpStream+dwMemPos);
 			dwMemPos += m_nChannels * sizeof(USHORT);
diff --git a/external/libmodplug-0.8.9.0/src/load_ams.cpp b/external/libmodplug-0.8.9.0/src/load_ams.cpp
index 1448f46..37eeae6 100644
--- a/external/libmodplug-0.8.9.0/src/load_ams.cpp
+++ b/external/libmodplug-0.8.9.0/src/load_ams.cpp
@@ -39,15 +39,16 @@ typedef struct AMSSAMPLEHEADER
 
 #pragma pack()
 
+static BOOL AMSUnpackCheck(const BYTE *lpStream, DWORD dwMemLength, MODINSTRUMENT *ins);
 
 BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 //-----------------------------------------------------------
 {
 //	BYTE pkinf[MAX_SAMPLES];
-	AMSFILEHEADER *pfh = (AMSFILEHEADER *)lpStream;
+	const AMSFILEHEADER *pfh = (AMSFILEHEADER *)lpStream;
 	DWORD dwMemPos;
 	UINT tmp, tmp2;
-	
+
 	if ((!lpStream) || (dwMemLength < 1024)) return FALSE;
 	if ((pfh->verhi != 0x01) || (strncmp(pfh->szHeader, "Extreme", 7))
 	 || (!pfh->patterns) || (!pfh->orders) || (!pfh->samples) || (pfh->samples >= MAX_SAMPLES)
@@ -63,7 +64,7 @@ BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 	m_nSamples = pfh->samples;
 	for (UINT nSmp=1; nSmp<=m_nSamples; nSmp++, dwMemPos += sizeof(AMSSAMPLEHEADER))
 	{
-		AMSSAMPLEHEADER *psh = (AMSSAMPLEHEADER *)(lpStream + dwMemPos);
+		const AMSSAMPLEHEADER *psh = (AMSSAMPLEHEADER *)(lpStream + dwMemPos);
 		MODINSTRUMENT *pins = &Ins[nSmp];
 		pins->nLength = psh->length;
 		pins->nLoopStart = psh->loopstart;
@@ -115,9 +116,10 @@ BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 		dwMemPos += tmp;
 	}
 	// Read Song Comments
+	if (dwMemPos + 2 > dwMemLength) return TRUE;
 	tmp = *((WORD *)(lpStream+dwMemPos));
 	dwMemPos += 2;
-	if (dwMemPos + tmp >= dwMemLength) return TRUE;
+	if (tmp >= dwMemLength || dwMemPos > dwMemLength - tmp) return TRUE;
 	if (tmp)
 	{
 		m_lpszSongComments = new char[tmp+1];  // changed from CHAR
@@ -127,6 +129,7 @@ BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 		dwMemPos += tmp;
 	}
 	// Read Order List
+	if (2*pfh->orders >= dwMemLength || dwMemPos > dwMemLength - 2*pfh->orders) return TRUE;
 	for (UINT iOrd=0; iOrd<pfh->orders; iOrd++, dwMemPos += 2)
 	{
 		UINT n = *((WORD *)(lpStream+dwMemPos));
@@ -138,7 +141,7 @@ BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 		if (dwMemPos + 4 >= dwMemLength) return TRUE;
 		UINT len = *((DWORD *)(lpStream + dwMemPos));
 		dwMemPos += 4;
-		if ((len >= dwMemLength) || (dwMemPos + len > dwMemLength)) return TRUE;
+		if ((len >= dwMemLength) || (dwMemPos > dwMemLength - len)) return TRUE;
 		PatternSize[iPat] = 64;
 		MODCOMMAND *m = AllocatePattern(PatternSize[iPat], m_nChannels);
 		if (!m) return TRUE;
@@ -154,6 +157,7 @@ BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 			// Note+Instr
 			if (!(b0 & 0x40))
 			{
+				if (i+1 > len) break;
 				b2 = p[i++];
 				if (ch < m_nChannels)
 				{
@@ -162,6 +166,7 @@ BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 				}
 				if (b1 & 0x80)
 				{
+					if (i+1 > len) break;
 					b0 |= 0x40;
 					b1 = p[i++];
 				}
@@ -179,6 +184,7 @@ BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 					}
 				} else
 				{
+					if (i+1 > len) break;
 					b2 = p[i++];
 					if (ch < m_nChannels)
 					{
@@ -221,6 +227,7 @@ BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 				}
 				if (b1 & 0x80)
 				{
+					if (i+1 > len) break;
 					b1 = p[i++];
 					if (i <= len) goto anothercommand;
 				}
@@ -238,7 +245,8 @@ BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
 	{
 		if (dwMemPos >= dwMemLength - 9) return TRUE;
 		UINT flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8;
-		dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos);
+		if (!AMSUnpackCheck(lpStream+dwMemPos, dwMemLength-dwMemPos, &Ins[iSmp])) break;
+		dwMemPos += ReadSample(&Ins[iSmp], flags, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos);
 	}
 	return TRUE;
 }
@@ -301,7 +309,6 @@ typedef struct AMS2SAMPLE
 	BYTE flags;
 } AMS2SAMPLE;
 
-
 #pragma pack()
 
 
@@ -309,7 +316,7 @@ BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength)
 //------------------------------------------------------------
 {
 	const AMS2FILEHEADER *pfh = (AMS2FILEHEADER *)lpStream;
-	AMS2SONGHEADER *psh;
+	const AMS2SONGHEADER *psh;
 	DWORD dwMemPos;
 	BYTE smpmap[16];
 	BYTE packedsamples[MAX_SAMPLES];
@@ -335,19 +342,23 @@ BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength)
 	if (psh->flags & 0x40) m_dwSongFlags |= SONG_LINEARSLIDES;
 	for (UINT nIns=1; nIns<=m_nInstruments; nIns++)
 	{
+		if (dwMemPos >= dwMemLength) return TRUE;
 		UINT insnamelen = lpStream[dwMemPos];
-		CHAR *pinsname = (CHAR *)(lpStream+dwMemPos+1);
+		const CHAR *pinsname = (CHAR *)(lpStream+dwMemPos+1);
 		dwMemPos += insnamelen + 1;
-		AMS2INSTRUMENT *pins = (AMS2INSTRUMENT *)(lpStream + dwMemPos);
+		const AMS2INSTRUMENT *pins = (AMS2INSTRUMENT *)(lpStream + dwMemPos);
 		dwMemPos += sizeof(AMS2INSTRUMENT);
-		if (dwMemPos + 1024 >= dwMemLength) return TRUE;
-		AMS2ENVELOPE *volenv, *panenv, *pitchenv;
+		const AMS2ENVELOPE *volenv, *panenv, *pitchenv;
+		if (dwMemPos + sizeof(AMS2ENVELOPE) > dwMemLength) return TRUE;
 		volenv = (AMS2ENVELOPE *)(lpStream+dwMemPos);
 		dwMemPos += 5 + volenv->points*3;
+		if (dwMemPos + sizeof(AMS2ENVELOPE) > dwMemLength) return TRUE;
 		panenv = (AMS2ENVELOPE *)(lpStream+dwMemPos);
 		dwMemPos += 5 + panenv->points*3;
+		if (dwMemPos + sizeof(AMS2ENVELOPE) > dwMemLength) return TRUE;
 		pitchenv = (AMS2ENVELOPE *)(lpStream+dwMemPos);
 		dwMemPos += 5 + pitchenv->points*3;
+		if (dwMemPos >= dwMemLength) return TRUE;
 		INSTRUMENTHEADER *penv = new INSTRUMENTHEADER;
 		if (!penv) return TRUE;
 		memset(smpmap, 0, sizeof(smpmap));
@@ -387,6 +398,7 @@ BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength)
 				penv->VolPoints[i] = (WORD)pos;
 			}
 		}
+		if (dwMemPos + 5 > dwMemLength) return TRUE;
 		penv->nFadeOut = (((lpStream[dwMemPos+2] & 0x0F) << 8) | (lpStream[dwMemPos+1])) << 3;
 		UINT envflags = lpStream[dwMemPos+3];
 		if (envflags & 0x01) penv->dwFlags |= ENV_VOLLOOP;
@@ -396,16 +408,19 @@ BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength)
 		// Read Samples
 		for (UINT ismp=0; ismp<pins->samples; ismp++)
 		{
+			if (dwMemPos + 1 > dwMemLength) return TRUE;
 			MODINSTRUMENT *psmp = ((ismp < 16) && (smpmap[ismp])) ? &Ins[smpmap[ismp]] : NULL;
 			UINT smpnamelen = lpStream[dwMemPos];
+			if (dwMemPos + smpnamelen + 1 > dwMemLength) return TRUE;
 			if ((psmp) && (smpnamelen) && (smpnamelen <= 22))
 			{
 				memcpy(m_szNames[smpmap[ismp]], lpStream+dwMemPos+1, smpnamelen);
 			}
 			dwMemPos += smpnamelen + 1;
+			if (dwMemPos + sizeof(AMS2SAMPLE) > dwMemLength) return TRUE;
 			if (psmp)
 			{
-				AMS2SAMPLE *pams = (AMS2SAMPLE *)(lpStream+dwMemPos);
+				const AMS2SAMPLE *pams = (AMS2SAMPLE *)(lpStream+dwMemPos);
 				psmp->nGlobalVol = 64;
 				psmp->nPan = 128;
 				psmp->nLength = pams->length;
@@ -545,16 +560,40 @@ BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength)
 		if (packedsamples[iSmp] & 0x03)
 		{
 			flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8;
+			if (!AMSUnpackCheck(lpStream+dwMemPos, dwMemLength-dwMemPos, &Ins[iSmp])) break;
 		} else
 		{
 			flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S;
 		}
-		dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos);
+		dwMemPos += ReadSample(&Ins[iSmp], flags, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos);
 	}
 	return TRUE;
 }
 
 
+// Precheck AMS packed sample size to determine whether or not it could fit the actual size.
+static BOOL AMSUnpackCheck(const BYTE *lpStream, DWORD dwMemLength, MODINSTRUMENT *ins)
+// -----------------------------------------------------------------------------------
+{
+	if (dwMemLength < 9) return FALSE;
+	DWORD packedbytes = *((DWORD *)(lpStream + 4));
+
+	DWORD samplebytes = ins->nLength;
+	if (samplebytes > MAX_SAMPLE_LENGTH) samplebytes = MAX_SAMPLE_LENGTH;
+	if (ins->uFlags & CHN_16BIT) samplebytes *= 2;
+
+	// RLE can pack a run of up to 255 bytes into 3 bytes.
+	DWORD packedmin = (samplebytes * 3) >> 8;
+	if (packedbytes < packedmin)
+	{
+		samplebytes = packedbytes * (255 / 3) + 2;
+		ins->nLength = samplebytes;
+		if (ins->uFlags & CHN_16BIT) ins->nLength >>= 1;
+	}
+
+	return TRUE;
+}
+
 /////////////////////////////////////////////////////////////////////
 // AMS Sample unpacking
 
@@ -562,7 +601,7 @@ void AMSUnpack(const char *psrc, UINT inputlen, char *pdest, UINT dmax, char pac
 {
 	UINT tmplen = dmax;
 	signed char *amstmp = new signed char[tmplen];
-	
+
 	if (!amstmp) return;
 	// Unpack Loop
 	{
@@ -573,9 +612,11 @@ void AMSUnpack(const char *psrc, UINT inputlen, char *pdest, UINT dmax, char pac
 			signed char ch = psrc[i++];
 			if (ch == packcharacter)
 			{
+				if (i >= inputlen) break;
 				BYTE ch2 = psrc[i++];
 				if (ch2)
 				{
+					if (i >= inputlen) break;
 					ch = psrc[i++];
 					while (ch2--)
 					{
@@ -585,6 +626,12 @@ void AMSUnpack(const char *psrc, UINT inputlen, char *pdest, UINT dmax, char pac
 				} else p[j++] = packcharacter;
 			} else p[j++] = ch;
 		}
+		if (j < tmplen)
+		{
+			// Truncated or invalid; don't try to unpack this.
+			delete[] amstmp;
+			return;
+		}
 	}
 	// Bit Unpack Loop
 	{
diff --git a/external/libmodplug-0.8.9.0/src/load_dbm.cpp b/external/libmodplug-0.8.9.0/src/load_dbm.cpp
index 8bc9c9b..d8f770e 100644
--- a/external/libmodplug-0.8.9.0/src/load_dbm.cpp
+++ b/external/libmodplug-0.8.9.0/src/load_dbm.cpp
@@ -136,6 +136,9 @@ BOOL CSoundFile::ReadDBM(const BYTE *lpStream, DWORD dwMemLength)
 		// Instruments
 		if (chunk_id == bswapLE32(DBM_ID_INST))
 		{
+			// Skip duplicate chunks.
+			if (m_nInstruments) continue;
+
 			if (nInstruments >= MAX_INSTRUMENTS) nInstruments = MAX_INSTRUMENTS-1;
 			for (UINT iIns=0; iIns<nInstruments; iIns++)
 			{
@@ -239,6 +242,9 @@ BOOL CSoundFile::ReadDBM(const BYTE *lpStream, DWORD dwMemLength)
 				DWORD pksize;
 				UINT nRows;
 
+				// Skip duplicate chunks.
+				if (Patterns[iPat]) break;
+
 				if (chunk_pos + sizeof(DBMPATTERN) > dwMemPos) break;
 				pph = (DBMPATTERN *)(lpStream+chunk_pos);
 				pksize = bswapBE32(pph->packedsize);
diff --git a/external/libmodplug-0.8.9.0/src/load_dmf.cpp b/external/libmodplug-0.8.9.0/src/load_dmf.cpp
index 1d4f8a7..85d02f7 100644
--- a/external/libmodplug-0.8.9.0/src/load_dmf.cpp
+++ b/external/libmodplug-0.8.9.0/src/load_dmf.cpp
@@ -87,11 +87,12 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 //---------------------------------------------------------------
 {
 	const DMFHEADER *pfh = (DMFHEADER *)lpStream;
-	DMFINFO *psi;
-	DMFSEQU *sequ;
+	const DMFINFO *psi;
+	const DMFPATT *patt;
+	const DMFSEQU *sequ;
 	DWORD dwMemPos;
 	BYTE infobyte[32];
-	BYTE smplflags[MAX_SAMPLES], hasSMPI = 0;
+	BYTE smplflags[MAX_SAMPLES], hasSMPI = 0, hasSMPD = 0;
 
 	if ((!lpStream) || (dwMemLength < 1024)) return FALSE;
 	if ((pfh->id != 0x464d4444) || (!pfh->version) || (pfh->version & 0xF0)) return FALSE;
@@ -115,7 +116,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 		case 0x47534d43:
 			psi = (DMFINFO *)(lpStream+dwMemPos);
 			if (id == 0x47534d43) dwMemPos++;
-			if ((psi->infosize > dwMemLength) || (psi->infosize + dwMemPos + 8 > dwMemLength)) goto dmfexit;
+			if ((psi->infosize > dwMemLength) || (dwMemPos + 8 > dwMemLength - psi->infosize)) goto dmfexit;
 			if ((psi->infosize >= 8) && (!m_lpszSongComments))
 			{
 			    m_lpszSongComments = new char[psi->infosize]; // changed from CHAR
@@ -138,9 +139,10 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 		// "SEQU"
 		case 0x55514553:
 			sequ = (DMFSEQU *)(lpStream+dwMemPos);
-			if ((sequ->seqsize >= dwMemLength) || (dwMemPos + sequ->seqsize + 12 > dwMemLength)) goto dmfexit;
+			if ((sequ->seqsize >= dwMemLength) || (dwMemPos + 8 > dwMemLength - sequ->seqsize)) goto dmfexit;
+			if (sequ->seqsize >= 4)
 			{
-				UINT nseq = sequ->seqsize >> 1;
+				UINT nseq = (sequ->seqsize - 4) >> 1;
 				if (nseq >= MAX_ORDERS-1) nseq = MAX_ORDERS-1;
 				if (sequ->loopstart < nseq) m_nRestartPos = sequ->loopstart;
 				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)
 
 		// "PATT"
 		case 0x54544150:
-			if (!m_nChannels)
+			patt = (DMFPATT *)(lpStream+dwMemPos);
+			if ((patt->patsize >= dwMemLength) || (dwMemPos + 8 > dwMemLength - patt->patsize)) goto dmfexit;
+			if (patt->patsize >= 4 && !m_nChannels)
 			{
-				DMFPATT *patt = (DMFPATT *)(lpStream+dwMemPos);
 				UINT numpat;
 				DWORD dwPos = dwMemPos + 11;
-				if ((patt->patsize >= dwMemLength) || (dwMemPos + patt->patsize + 8 > dwMemLength)) goto dmfexit;
 				numpat = patt->numpat;
 				if (numpat > MAX_PATTERNS) numpat = MAX_PATTERNS;
 				m_nChannels = patt->tracks;
@@ -164,7 +166,8 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 				if (m_nChannels < 4) m_nChannels = 4;
 				for (UINT npat=0; npat<numpat; npat++)
 				{
-					DMFTRACK *pt = (DMFTRACK *)(lpStream+dwPos);
+					const DMFTRACK *pt = (DMFTRACK *)(lpStream+dwPos);
+					if (dwPos + 8 >= dwMemLength) break;
 				#ifdef DMFLOG
 					Log("Pattern #%d: %d tracks, %d rows\n", npat, pt->tracks, pt->ticks);
 				#endif
@@ -174,7 +177,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 					if (ticks > 256) ticks = 256;
 					if (ticks < 16) ticks = 16;
 					dwPos += 8;
-					if ((pt->jmpsize >= dwMemLength) || (dwPos + pt->jmpsize + 4 >= dwMemLength)) break;
+					if ((pt->jmpsize >= dwMemLength) || (dwPos + 4 > dwMemLength - pt->jmpsize)) break;
 					PatternSize[npat] = (WORD)ticks;
 					MODCOMMAND *m = AllocatePattern(PatternSize[npat], m_nChannels);
 					if (!m) goto dmfexit;
@@ -193,6 +196,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 						// Parse track global effects
 						if (!glbinfobyte)
 						{
+							if (d+1 > dwPos) break;
 							BYTE info = lpStream[d++];
 							BYTE infoval = 0;
 							if ((info & 0x80) && (d < dwPos)) glbinfobyte = lpStream[d++];
@@ -214,17 +218,24 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 						// Parse channels
 						for (UINT i=0; i<tracks; i++) if (!infobyte[i])
 						{
+							if (d+1 > dwPos) break;
 							MODCOMMAND cmd = {0,0,0,0,0,0};
 							BYTE info = lpStream[d++];
-							if (info & 0x80) infobyte[i] = lpStream[d++];
+							if (info & 0x80)
+							{
+								if (d+1 > dwPos) break;
+								infobyte[i] = lpStream[d++];
+							}
 							// Instrument
 							if (info & 0x40)
 							{
+								if (d+1 > dwPos) break;
 								cmd.instr = lpStream[d++];
 							}
 							// Note
 							if (info & 0x20)
 							{
+								if (d+1 > dwPos) break;
 								cmd.note = lpStream[d++];
 								if ((cmd.note) && (cmd.note < 0xfe)) cmd.note &= 0x7f;
 								if ((cmd.note) && (cmd.note < 128)) cmd.note += 24;
@@ -232,12 +243,15 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 							// Volume
 							if (info & 0x10)
 							{
+								if (d+1 > dwPos) break;
 								cmd.volcmd = VOLCMD_VOLUME;
 								cmd.vol = (lpStream[d++]+3)>>2;
 							}
 							// Effect 1
 							if (info & 0x08)
 							{
+								if (d+2 > dwPos) break;
+
 								BYTE efx = lpStream[d++];
 								BYTE eval = lpStream[d++];
 								switch(efx)
@@ -259,6 +273,8 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 							// Effect 2
 							if (info & 0x04)
 							{
+								if (d+2 > dwPos) break;
+
 								BYTE efx = lpStream[d++];
 								BYTE eval = lpStream[d++];
 								switch(efx)
@@ -289,6 +305,8 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 							// Effect 3
 							if (info & 0x02)
 							{
+								if (d+2 > dwPos) break;
+
 								BYTE efx = lpStream[d++];
 								BYTE eval = lpStream[d++];
 								switch(efx)
@@ -372,22 +390,24 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 				#endif
 					if (dwPos + 8 >= dwMemLength) break;
 				}
-				dwMemPos += patt->patsize + 8;
 			}
+			dwMemPos += patt->patsize + 8;
 			break;
 
 		// "SMPI": Sample Info
 		case 0x49504d53:
 			{
 				hasSMPI = 1;
-				DMFSMPI *pds = (DMFSMPI *)(lpStream+dwMemPos);
-				if (pds->size <= dwMemLength - dwMemPos)
+				const DMFSMPI *pds = (DMFSMPI *)(lpStream+dwMemPos);
+				if ((pds->size >= dwMemLength) || (dwMemPos + 8 > dwMemLength - pds->size)) goto dmfexit;
+				if (pds->size >= 1)
 				{
 					DWORD dwPos = dwMemPos + 9;
 					m_nSamples = pds->samples;
 					if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1;
 					for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++)
 					{
+						if (dwPos >= dwMemPos + pds->size + 8) break;
 						UINT namelen = lpStream[dwPos];
 						smplflags[iSmp] = 0;
 						if (dwPos+namelen+1+sizeof(DMFSAMPLE) > dwMemPos+pds->size+8) break;
@@ -398,7 +418,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 							m_szNames[iSmp][rlen] = 0;
 						}
 						dwPos += namelen + 1;
-						DMFSAMPLE *psh = (DMFSAMPLE *)(lpStream+dwPos);
+						const DMFSAMPLE *psh = (DMFSAMPLE *)(lpStream+dwPos);
 						MODINSTRUMENT *psmp = &Ins[iSmp];
 						psmp->nLength = psh->len;
 						psmp->nLoopStart = psh->loopstart;
@@ -425,7 +445,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 			{
 				DWORD dwPos = dwMemPos + 8;
 				UINT ismpd = 0;
-				for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++)
+				for (UINT iSmp=1; iSmp<=m_nSamples && !hasSMPD; iSmp++)
 				{
 					ismpd++;
 					DWORD pksize;
@@ -458,6 +478,7 @@ BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
 					}
 					dwPos += pksize;
 				}
+				hasSMPD = 1;
 				dwMemPos = dwPos;
 			}
 			break;
@@ -520,8 +541,8 @@ static BYTE DMFReadBits(DMF_HTREE *tree, UINT nbits)
 		{
 			tree->bitnum--;
 		} else
-		{
-			tree->bitbuf = (tree->ibuf < tree->ibufmax) ? *(tree->ibuf++) : 0;
+		if (tree->ibuf < tree->ibufmax) {
+			tree->bitbuf = *(tree->ibuf++);
 			tree->bitnum = 7;
 		}
 		if (tree->bitbuf & 1) x |= bitv;
@@ -576,14 +597,24 @@ int DMFUnpack(LPBYTE psample, LPBYTE ibuf, LPBYTE ibufmax, UINT maxlen)
 	DMF_HTREE tree;
 	UINT actnode;
 	BYTE value, sign, delta = 0;
-	
+
 	memset(&tree, 0, sizeof(tree));
 	tree.ibuf = ibuf;
 	tree.ibufmax = ibufmax;
 	DMFNewNode(&tree);
 	value = 0;
+
+	if (tree.ibuf >= ibufmax) return tree.ibuf - ibuf;
+
 	for (UINT i=0; i<maxlen; i++)
 	{
+		if ((tree.ibuf >= tree.ibufmax) && (!tree.bitnum))
+		{
+		#ifdef DMFLOG
+			Log("DMFUnpack: unexpected EOF at output byte %d / %d\n", i, maxlen);
+		#endif
+			break;
+		}
 		actnode = 0;
 		sign = DMFReadBits(&tree, 1);
 		do
diff --git a/external/libmodplug-0.8.9.0/src/load_far.cpp b/external/libmodplug-0.8.9.0/src/load_far.cpp
index a8245e5..11e676c 100644
--- a/external/libmodplug-0.8.9.0/src/load_far.cpp
+++ b/external/libmodplug-0.8.9.0/src/load_far.cpp
@@ -5,7 +5,7 @@
 */
 
 ////////////////////////////////////////
-// Farandole (FAR) module loader	  //
+// Farandole (FAR) module loader      //
 ////////////////////////////////////////
 #include "stdafx.h"
 #include "sndfile.h"
@@ -113,8 +113,6 @@ BOOL CSoundFile::ReadFAR(const BYTE *lpStream, DWORD dwMemLength)
 	dwMemPos += headerlen - (869 + stlen);
 	if (dwMemPos >= dwMemLength) return TRUE;
 
-	// end byteswap of pattern data
-
 	WORD *patsiz = (WORD *)pmh2->patsiz;
 	for (UINT ipat=0; ipat<256; ipat++) if (patsiz[ipat])
 	{
@@ -125,6 +123,7 @@ BOOL CSoundFile::ReadFAR(const BYTE *lpStream, DWORD dwMemLength)
 			continue;
 		}
 		if (dwMemPos + patlen >= dwMemLength) return TRUE;
+		UINT max  = (patlen - 2) & ~3;
 		UINT rows = (patlen - 2) >> 6;
 		if (!rows)
 		{
@@ -133,13 +132,12 @@ BOOL CSoundFile::ReadFAR(const BYTE *lpStream, DWORD dwMemLength)
 		}
 		if (rows > 256) rows = 256;
 		if (rows < 16) rows = 16;
+		if (max > rows*16*4) max = rows*16*4;
 		PatternSize[ipat] = rows;
 		if ((Patterns[ipat] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE;
 		MODCOMMAND *m = Patterns[ipat];
 		UINT patbrk = lpStream[dwMemPos];
 		const BYTE *p = lpStream + dwMemPos + 2;
-		UINT max = rows*16*4;
-		if (max > patlen-2) max = patlen-2;
 		for (UINT len=0; len<max; len += 4, m++)
 		{
 			BYTE note = p[len];
@@ -235,10 +233,10 @@ BOOL CSoundFile::ReadFAR(const BYTE *lpStream, DWORD dwMemLength)
 		dwMemPos += sizeof(FARSAMPLE);
 		m_nSamples = ismp + 1;
 		memcpy(m_szNames[ismp+1], pfs->samplename, 32);
-		const DWORD length = bswapLE32( pfs->length ) ; /* endian fix - Toad */
-		pins->nLength = length ;
-		pins->nLoopStart = bswapLE32(pfs->reppos) ;
-		pins->nLoopEnd = bswapLE32(pfs->repend) ;
+		const DWORD length = bswapLE32(pfs->length); /* endian fix - Toad */
+		pins->nLength = length;
+		pins->nLoopStart = bswapLE32(pfs->reppos);
+		pins->nLoopEnd = bswapLE32(pfs->repend);
 		pins->nFineTune = 0;
 		pins->nC4Speed = 8363*2;
 		pins->nGlobalVol = 64;
@@ -261,4 +259,3 @@ BOOL CSoundFile::ReadFAR(const BYTE *lpStream, DWORD dwMemLength)
 	}
 	return TRUE;
 }
-
diff --git a/external/libmodplug-0.8.9.0/src/load_it.cpp b/external/libmodplug-0.8.9.0/src/load_it.cpp
index ef1c4ef..89d934a 100644
--- a/external/libmodplug-0.8.9.0/src/load_it.cpp
+++ b/external/libmodplug-0.8.9.0/src/load_it.cpp
@@ -580,7 +580,7 @@ BOOL CSoundFile::ReadIT(const BYTE *lpStream, DWORD dwMemLength)
 //////////////////////////////////////////////////////////////////////////////
 // IT 2.14 compression
 
-DWORD ITReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n)
+DWORD ITReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, LPBYTE ibufend, CHAR n)
 //-----------------------------------------------------------------
 {
 	DWORD retval = 0;
@@ -596,6 +596,9 @@ DWORD ITReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n)
 		{
 			if (!bitnum)
 			{
+				if (ibuf >= ibufend)
+					return 0;
+
 				bitbuf = *ibuf++;
 				bitnum = 8;
 			}
@@ -616,6 +619,7 @@ void ITUnpack8Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dwM
 {
 	signed char *pDst = pSample;
 	LPBYTE pSrc = lpMemFile;
+	LPBYTE pStop = lpMemFile + dwMemLength;
 //	DWORD wHdr = 0;
 	DWORD wCount = 0;
 	DWORD bitbuf = 0;
@@ -639,13 +643,13 @@ void ITUnpack8Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dwM
 		DWORD dwPos = 0;
 		do
 		{
-			WORD wBits = (WORD)ITReadBits(bitbuf, bitnum, pSrc, bLeft);
+			WORD wBits = (WORD)ITReadBits(bitbuf, bitnum, pSrc, pStop, bLeft);
 			if (bLeft < 7)
 			{
 				DWORD i = 1 << (bLeft-1);
 				DWORD j = wBits & 0xFFFF;
 				if (i != j) goto UnpackByte;
-				wBits = (WORD)(ITReadBits(bitbuf, bitnum, pSrc, 3) + 1) & 0xFF;
+				wBits = (WORD)(ITReadBits(bitbuf, bitnum, pSrc, pStop, 3) + 1) & 0xFF;
 				bLeft = ((BYTE)wBits < bLeft) ? (BYTE)wBits : (BYTE)((wBits+1) & 0xFF);
 				goto Next;
 			}
@@ -683,7 +687,7 @@ void ITUnpack8Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dwM
 		SkipByte:
 			dwPos++;
 		Next:
-			if (pSrc >= lpMemFile+dwMemLength+1) return;
+			if (pSrc >= pStop + 1) return;
 		} while (dwPos < d);
 		// Move On
 		wCount -= d;
@@ -698,6 +702,7 @@ void ITUnpack16Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dw
 {
 	signed short *pDst = (signed short *)pSample;
 	LPBYTE pSrc = lpMemFile;
+	LPBYTE pStop = lpMemFile + dwMemLength;
 //	DWORD wHdr = 0;
 	DWORD wCount = 0;
 	DWORD bitbuf = 0;
@@ -722,13 +727,13 @@ void ITUnpack16Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dw
 		DWORD dwPos = 0;
 		do
 		{
-			DWORD dwBits = ITReadBits(bitbuf, bitnum, pSrc, bLeft);
+			DWORD dwBits = ITReadBits(bitbuf, bitnum, pSrc, pStop, bLeft);
 			if (bLeft < 7)
 			{
 				DWORD i = 1 << (bLeft-1);
 				DWORD j = dwBits;
 				if (i != j) goto UnpackByte;
-				dwBits = ITReadBits(bitbuf, bitnum, pSrc, 4) + 1;
+				dwBits = ITReadBits(bitbuf, bitnum, pSrc, pStop, 4) + 1;
 				bLeft = ((BYTE)(dwBits & 0xFF) < bLeft) ? (BYTE)(dwBits & 0xFF) : (BYTE)((dwBits+1) & 0xFF);
 				goto Next;
 			}
@@ -766,13 +771,13 @@ void ITUnpack16Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dw
 		SkipByte:
 			dwPos++;
 		Next:
-			if (pSrc >= lpMemFile+dwMemLength+1) return;
+			if (pSrc >= pStop + 1) return;
 		} while (dwPos < d);
 		// Move On
 		wCount -= d;
 		dwLen -= d;
 		pDst += d;
-		if (pSrc >= lpMemFile+dwMemLength) break;
+		if (pSrc >= pStop) break;
 	}
 }
 
diff --git a/external/libmodplug-0.8.9.0/src/load_mdl.cpp b/external/libmodplug-0.8.9.0/src/load_mdl.cpp
index 177f322..7e9f43b 100644
--- a/external/libmodplug-0.8.9.0/src/load_mdl.cpp
+++ b/external/libmodplug-0.8.9.0/src/load_mdl.cpp
@@ -216,6 +216,7 @@ BOOL CSoundFile::ReadMDL(const BYTE *lpStream, DWORD dwMemLength)
 			m_szNames[0][31] = 0;
 			norders = pmib->norders;
 			if (norders > MAX_ORDERS) norders = MAX_ORDERS;
+			if (blocklen < sizeof(MDLINFOBLOCK) + norders - sizeof(pmib->seq)) return FALSE;
 			m_nRestartPos = pmib->repeat

(Patch may be truncated, please check the link at the top of this post.)