CVE-2020-4033: Out-of-bounds Read (CWE-125)
value: In FreeRDP before version 2.1.2, there is an out of bounds read in RLEDECOMPRESS. All FreeRDP based clients with sessions with color depth < 32 are affected. This is fixed in version 2.1.2.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 6.5
Published: 2020-06-22T22:15Z
Repository: https://github.com/FreeRDP/FreeRDP
Language: C
Files changed: libfreerdp/codec/include/bitmap.c
Commit date: 2020-06-22 11:51:39 +0200
Commit message: Fixed out of bound read in RLEDECOMPRESS

CVE-2020-4033 thanks to @antonio-morales for finding this.

Diff: diff --git a/libfreerdp/codec/include/bitmap.c b/libfreerdp/codec/include/bitmap.c
index 5b4804f2ea2..38bcaa859f8 100644
--- a/libfreerdp/codec/include/bitmap.c
+++ b/libfreerdp/codec/include/bitmap.c
@@ -199,10 +199,12 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 				runLength = ExtractRunLength(code, pbSrc, &advance);
 				pbSrc = pbSrc + advance;
 
 				if (code == LITE_SET_FG_FG_RUN || code == MEGA_MEGA_SET_FG_RUN)
 				{
+					if (pbSrc >= pbEnd)
+						return FALSE;
 					SRCREADPIXEL(fgPel, pbSrc);
 					SRCNEXTPIXEL(pbSrc);
 				}
 
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
@@ -229,12 +231,16 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 			/* Handle Dithered Run Orders. */
 			case LITE_DITHERED_RUN:
 			case MEGA_MEGA_DITHERED_RUN:
 				runLength = ExtractRunLength(code, pbSrc, &advance);
 				pbSrc = pbSrc + advance;
+				if (pbSrc >= pbEnd)
+					return FALSE;
 				SRCREADPIXEL(pixelA, pbSrc);
 				SRCNEXTPIXEL(pbSrc);
+				if (pbSrc >= pbEnd)
+					return FALSE;
 				SRCREADPIXEL(pixelB, pbSrc);
 				SRCNEXTPIXEL(pbSrc);
 
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength * 2))
 					return FALSE;
@@ -250,10 +256,12 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 			/* Handle Color Run Orders. */
 			case REGULAR_COLOR_RUN:
 			case MEGA_MEGA_COLOR_RUN:
 				runLength = ExtractRunLength(code, pbSrc, &advance);
 				pbSrc = pbSrc + advance;
+				if (pbSrc >= pbEnd)
+					return FALSE;
 				SRCREADPIXEL(pixelA, pbSrc);
 				SRCNEXTPIXEL(pbSrc);
 
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
 					return FALSE;
@@ -270,10 +278,12 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 			case LITE_SET_FG_FGBG_IMAGE:
 			case MEGA_MEGA_SET_FGBG_IMAGE:
 				runLength = ExtractRunLength(code, pbSrc, &advance);
 				pbSrc = pbSrc + advance;
 
+				if (pbSrc >= pbEnd)
+					return FALSE;
 				if (code == LITE_SET_FG_FGBG_IMAGE || code == MEGA_MEGA_SET_FGBG_IMAGE)
 				{
 					SRCREADPIXEL(fgPel, pbSrc);
 					SRCNEXTPIXEL(pbSrc);
 				}
@@ -336,10 +346,12 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 				pbSrc = pbSrc + advance;
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
 					return FALSE;
 
 				UNROLL(runLength, {
+					if (pbSrc >= pbEnd)
+						return FALSE;
 					SRCREADPIXEL(temp, pbSrc);
 					SRCNEXTPIXEL(pbSrc);
 					DESTWRITEPIXEL(pbDest, temp);
 					DESTNEXTPIXEL(pbDest);
 				});

Fixed code: 					if (pbSrc >= pbEnd)
						return FALSE;
				if (pbSrc >= pbEnd)
					return FALSE;
				if (pbSrc >= pbEnd)
					return FALSE;
				if (pbSrc >= pbEnd)
					return FALSE;
				if (pbSrc >= pbEnd)
					return FALSE;
					if (pbSrc >= pbEnd)
						return FALSE;