Vulnerability: 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.
Vulnerability ID: CVE-2020-4033
CWE: CWE-125
Severity score: 4.9
Project: https://github.com/FreeRDP/FreeRDP
Published: 2020-06-22
File: libfreerdp/codec/include/bitmap.c
Commit message: Fixed out of bound read in RLEDECOMPRESS

CVE-2020-4033 thanks to @antonio-morales for finding this.
Commit author: akallabeth

Vulnerable version (before fix):
 static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY  				if (code == LITE_SET_FG_FG_RUN || code == MEGA_MEGA_SET_FG_RUN) 				{ 					SRCREADPIXEL(fgPel, pbSrc); 					SRCNEXTPIXEL(pbSrc); 				} static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY 			case MEGA_MEGA_DITHERED_RUN: 				runLength = ExtractRunLength(code, pbSrc, &advance); 				pbSrc = pbSrc + advance; 				SRCREADPIXEL(pixelA, pbSrc); 				SRCNEXTPIXEL(pbSrc); 				SRCREADPIXEL(pixelB, pbSrc); 				SRCNEXTPIXEL(pbSrc);  static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY 			case MEGA_MEGA_COLOR_RUN: 				runLength = ExtractRunLength(code, pbSrc, &advance); 				pbSrc = pbSrc + advance; 				SRCREADPIXEL(pixelA, pbSrc); 				SRCNEXTPIXEL(pbSrc);  static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY 				runLength = ExtractRunLength(code, pbSrc, &advance); 				pbSrc = pbSrc + advance;  				if (code == LITE_SET_FG_FGBG_IMAGE || code == MEGA_MEGA_SET_FGBG_IMAGE) 				{ 					SRCREADPIXEL(fgPel, pbSrc); static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY 					return FALSE;  				UNROLL(runLength, { 					SRCREADPIXEL(temp, pbSrc); 					SRCNEXTPIXEL(pbSrc); 					DESTWRITEPIXEL(pbDest, temp);

Fixed version (after fix):
 static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY  				if (code == LITE_SET_FG_FG_RUN || code == MEGA_MEGA_SET_FG_RUN) 				{ 					if (pbSrc >= pbEnd) 						return FALSE; 					SRCREADPIXEL(fgPel, pbSrc); 					SRCNEXTPIXEL(pbSrc); 				} static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY 			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);  static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY 			case MEGA_MEGA_COLOR_RUN: 				runLength = ExtractRunLength(code, pbSrc, &advance); 				pbSrc = pbSrc + advance; 				if (pbSrc >= pbEnd) 					return FALSE; 				SRCREADPIXEL(pixelA, pbSrc); 				SRCNEXTPIXEL(pbSrc);  static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY 				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); static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY 					return FALSE;  				UNROLL(runLength, { 					if (pbSrc >= pbEnd) 						return FALSE; 					SRCREADPIXEL(temp, pbSrc); 					SRCNEXTPIXEL(pbSrc); 					DESTWRITEPIXEL(pbDest, temp);

Patch:
patch: @@ -201,6 +201,8 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 
 				if (code == LITE_SET_FG_FG_RUN || code == MEGA_MEGA_SET_FG_RUN)
 				{
+					if (pbSrc >= pbEnd)
+						return FALSE;
 					SRCREADPIXEL(fgPel, pbSrc);
 					SRCNEXTPIXEL(pbSrc);
 				}
@@ -231,8 +233,12 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 			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);
 
@@ -252,6 +258,8 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 			case MEGA_MEGA_COLOR_RUN:
 				runLength = ExtractRunLength(code, pbSrc, &advance);
 				pbSrc = pbSrc + advance;
+				if (pbSrc >= pbEnd)
+					return FALSE;
 				SRCREADPIXEL(pixelA, pbSrc);
 				SRCNEXTPIXEL(pbSrc);
 
@@ -272,6 +280,8 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 				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);
@@ -338,6 +348,8 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 					return FALSE;
 
 				UNROLL(runLength, {
+					if (pbSrc >= pbEnd)
+						return FALSE;
 					SRCREADPIXEL(temp, pbSrc);
 					SRCNEXTPIXEL(pbSrc);
 					DESTWRITEPIXEL(pbDest, temp);
path: libfreerdp/codec/include/bitmap.c