CVE-2023-40589 (C, published 2023-08-31T19:15Z)
Description: value: FreeRDP is a free implementation of the Remote Desktop Protocol (RDP), released under the Apache license. In affected versions there is a Global-Buffer-Overflow in the ncrush_decompress function. Feeding crafted input into this function can trigger the overflow which has only been shown to cause a crash. This issue has been addressed in versions 2.11.0 and 3.0.0-beta3. Users are advised to upgrade. There are no known workarounds for this issue.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-120 \u2014 Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
Repository: https://github.com/FreeRDP/FreeRDP
Files changed:
libfreerdp/codec/ncrush.c
Commit message: [codec,ncrush] fix index checks

properly verify all offsets while decoding data.


Vulnerable code:
	UINT32 bits;
	INT32 nbits;
	const BYTE* SrcPtr;
	const BYTE* SrcEnd;
	UINT16 Mask;
	UINT32 MaskedBits;
	BYTE* HistoryPtr;
	BYTE* HistoryBuffer;
	BYTE* HistoryBufferEnd;
	HistoryBuffer = ncrush->HistoryBuffer;
	HistoryBufferEnd = &HistoryBuffer[ncrush->HistoryEndOffset];
	HistoryPtr = ncrush->HistoryPtr;
	SrcEnd = &pSrcData[SrcSize];
	nbits = 32;
	bits = get_dword(pSrcData);
	SrcPtr = pSrcData + 4;
			Mask = get_word(&HuffTableMask[29]);
			MaskedBits = bits & Mask;
			Mask = get_word(&HuffTableMask[21]);
			MaskedBits = bits & Mask;
				Mask = get_word(&HuffTableMask[(2 * LengthOfMatchBits) + 3]);
				MaskedBits = bits & Mask;
				Mask = get_word(&HuffTableMask[(2 * CopyOffsetBits) + 3]);
				MaskedBits = bits & Mask;
				CopyOffset = CopyOffsetBase + MaskedBits - 1;
			Mask = get_word(&HuffTableMask[21]);
			MaskedBits = bits & Mask;
				Mask = get_word(&HuffTableMask[(2 * LengthOfMatchBits) + 3]);
				MaskedBits = bits & Mask;

Fixed code:
	BYTE* HistoryBuffer = ncrush->HistoryBuffer;
	const BYTE* HistoryBufferEnd = &HistoryBuffer[ncrush->HistoryEndOffset];
	BYTE* HistoryPtr = ncrush->HistoryPtr;
	const BYTE* SrcEnd = &pSrcData[SrcSize];
	const BYTE* SrcPtr = pSrcData + 4;
	INT32 nbits = 32;
	UINT32 bits = get_dword(pSrcData);
			const UINT16 Mask = get_word(&HuffTableMask[29]);
			const UINT32 MaskedBits = bits & Mask;
			if (MaskedBits >= ARRAYSIZE(HuffTableLEC))
				return -1;
			const UINT16 Mask = get_word(&HuffTableMask[21]);
			const UINT32 MaskedBits = bits & Mask;
			if (MaskedBits > ARRAYSIZE(HuffTableLOM))
				return -1;
			if (LengthOfMatch >= ARRAYSIZE(LOMBitsLUT))
				return -1;


			if (LengthOfMatch >= ARRAYSIZE(LOMBaseLUT))
				return -1;
				const size_t idx = (2ull * LengthOfMatchBits) + 3ull;
				if (idx >= ARRAYSIZE(HuffTableMask))
					return -1;

				const UINT16 Mask = get_word(&HuffTableMask[idx]);
				const UINT32 MaskedBits = bits & Mask;
			if (CopyOffsetIndex >= ARRAYSIZE(CopyOffsetBitsLUT))
				return -1;


			if (CopyOffsetIndex >= ARRAYSIZE(CopyOffsetBaseLUT))
				return -1;
				const size_t idx = (2ull * CopyOffsetBits) + 3ull;
				if (idx >= ARRAYSIZE(HuffTableMask))
					return -1;

				const UINT16 Mask = get_word(&HuffTableMask[idx]);
				const UINT32 MaskedBits = bits & Mask;
				const UINT32 tmp = CopyOffsetBase + MaskedBits;
				if (tmp < 1)
					return -1;
				CopyOffset = tmp - 1;
			const UINT16 Mask = get_word(&HuffTableMask[21]);
			const UINT32 MaskedBits = bits & Mask;
			if (MaskedBits >= ARRAYSIZE(HuffTableLOM))
				return -1;

			if (LengthOfMatch >= ARRAYSIZE(LOMBitsLUT))
				return -1;


			if (LengthOfMatch >= ARRAYSIZE(LOMBaseLUT))
				return -1;
				const size_t idx = (2ull * LengthOfMatchBits) + 3ull;
				if (idx >= ARRAYSIZE(HuffTableMask))
					return -1;

				const UINT16 Mask = get_word(&HuffTableMask[idx]);
				const UINT32 MaskedBits = bits & Mask;
			if (IndexLEC >= ARRAYSIZE(HuffLengthLEC))
				return -1;

			if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC))
				return -1;

				if (CopyOffsetIndex >= ARRAYSIZE(CopyOffsetBitsLUT))
					return -1;

				if (IndexLEC >= ARRAYSIZE(HuffLengthLEC))
					return -1;

				if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC))
					return -1;
				if (IndexCO >= ARRAYSIZE(HuffLengthLOM))
					return -1;

				if (IndexCO >= ARRAYSIZE(LOMBitsLUT))
					return -1;

				if (IndexCO >= ARRAYSIZE(HuffCodeLOM))
					return -1;
				if (IndexCO >= ARRAYSIZE(LOMBaseLUT))
					return -1;
				if (IndexLEC >= ARRAYSIZE(HuffLengthLEC))
					return -1;
				if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC))
					return -1;
				if (IndexCO >= ARRAYSIZE(HuffLengthLOM))
					return -1;


				if (IndexCO >= ARRAYSIZE(LOMBitsLUT))
					return -1;

				if (IndexCO >= ARRAYSIZE(HuffCodeLOM))
					return -1;
				if (IndexCO >= ARRAYSIZE(LOMBaseLUT))
					return -1;
		if (IndexLEC >= ARRAYSIZE(HuffLengthLEC))
			return -1;
		if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC))
			return -1;
	WINPR_ASSERT(28 < ARRAYSIZE(LOMBitsLUT));
		if (i >= ARRAYSIZE(LOMBitsLUT))
			return -1;
		if (i >= ARRAYSIZE(LOMBaseLUT))
			return -1;