CVE-2018-8788 (C, published 2018-11-29T18:29Z)
Description: value: FreeRDP prior to version 2.0.0-rc4 contains an Out-Of-Bounds Write of up to 4 bytes in function nsc_rle_decode() that results in a memory corruption and possibly even a remote code execution.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-787 \u2014 Out-of-bounds Write
The product writes data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/FreeRDP/FreeRDP
Files changed:
include/freerdp/codec/nsc.h
libfreerdp/codec/nsc.c
libfreerdp/codec/nsc_encode.c
libfreerdp/codec/nsc_encode.h
libfreerdp/codec/nsc_sse2.c
Commit message: Fixed CVE-2018-8788

Thanks to Eyal Itkin from Check Point Software Technologies.


Vulnerable code:
	void (*decode)(NSC_CONTEXT* context);
	void (*encode)(NSC_CONTEXT* context, const BYTE* BitmapData,
static void nsc_decode(NSC_CONTEXT* context)
	UINT16 rw = ROUND_UP_TO(context->width, 8);
	BYTE shift = context->ColorLossLevel - 1; /* colorloss recovery + YCoCg shift */
	BYTE* bmpdata = context->BitmapData;
static void nsc_rle_decode(BYTE* in, BYTE* out, UINT32 originalSize)
	*((UINT32*)out) = *((UINT32*)in);
static void nsc_rle_decompress_data(NSC_CONTEXT* context)
			nsc_rle_decode(rle, context->priv->PlaneBuffers[i], originalSize);
	PROFILER_ENTER(context->priv->prof_nsc_rle_decompress_data)
	nsc_rle_decompress_data(context);
	PROFILER_EXIT(context->priv->prof_nsc_rle_decompress_data)
	PROFILER_ENTER(context->priv->prof_nsc_decode)
	context->decode(context);
	PROFILER_EXIT(context->priv->prof_nsc_decode)
static void nsc_encode_argb_to_aycocg(NSC_CONTEXT* context, const BYTE* data,
static void nsc_encode_subsampling(NSC_CONTEXT* context)
	BYTE* co_dst;
	BYTE* cg_dst;
	INT8* co_src0;
	INT8* co_src1;
	INT8* cg_src0;
	INT8* cg_src1;
		co_dst = context->priv->PlaneBuffers[1] + y * (tempWidth >> 1);
		cg_dst = context->priv->PlaneBuffers[2] + y * (tempWidth >> 1);
		co_src0 = (INT8*) context->priv->PlaneBuffers[1] + (y << 1) * tempWidth;
		co_src1 = co_src0 + tempWidth;
		cg_src0 = (INT8*) context->priv->PlaneBuffers[2] + (y << 1) * tempWidth;
		cg_src1 = cg_src0 + tempWidth;
void nsc_encode(NSC_CONTEXT* context, const BYTE* bmpdata, UINT32 rowstride)
	nsc_encode_argb_to_aycocg(context, bmpdata, rowstride);
		nsc_encode_subsampling(context);
static UINT32 nsc_rle_encode(BYTE* in, BYTE* out, UINT32 originalSize)
FREERDP_LOCAL void nsc_encode(NSC_CONTEXT* context, const BYTE* bmpdata,
static void nsc_encode_sse2(NSC_CONTEXT* context, const BYTE* data,

Fixed code:
	BOOL (*decode)(NSC_CONTEXT* context);
	BOOL (*encode)(NSC_CONTEXT* context, const BYTE* BitmapData,
static BOOL nsc_decode(NSC_CONTEXT* context)
	UINT16 rw;
	BYTE shift;
	BYTE* bmpdata;
	size_t pos = 0;

	if (!context)
		return FALSE;

	rw = ROUND_UP_TO(context->width, 8);
	shift = context->ColorLossLevel - 1; /* colorloss recovery + YCoCg shift */
	bmpdata = context->BitmapData;

	if (!bmpdata)
		return FALSE;

			if (pos + 4 > context->BitmapDataLength)
				return FALSE;

			pos += 4;

	return TRUE;
static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalSize)
			if (outSize < 1)
				return FALSE;

			outSize--;
			if (outSize < len)
				return FALSE;

			outSize -= len;
			if (outSize < 1)
				return FALSE;

			outSize--;
	if ((outSize < 4) || (left < 4))
		return FALSE;

	memcpy(out, in, 4);
	return TRUE;
static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)

	if (!context)
		return FALSE;

		{
			if (context->priv->PlaneBuffersLength < originalSize)
				return FALSE;

		}
		{
			if (!nsc_rle_decode(rle, context->priv->PlaneBuffers[i], context->priv->PlaneBuffersLength,
			                    originalSize))
				return FALSE;
		}
		{
			if (context->priv->PlaneBuffersLength < originalSize)
				return FALSE;

		}

	return TRUE;
	{
		BOOL rc;
		PROFILER_ENTER(context->priv->prof_nsc_rle_decompress_data)
		rc = nsc_rle_decompress_data(context);
		PROFILER_EXIT(context->priv->prof_nsc_rle_decompress_data)

		if (!rc)
			return FALSE;
	}
	{
		BOOL rc;
		PROFILER_ENTER(context->priv->prof_nsc_decode)
		rc = context->decode(context);
		PROFILER_EXIT(context->priv->prof_nsc_decode)

		if (!rc)
			return FALSE;
	}

static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* context, const BYTE* data,

	if (!context || data || (scanline == 0))
		return FALSE;

	if (context->priv->PlaneBuffersLength < rw * scanline)
		return FALSE;

	if (rw < scanline * 2)
		return FALSE;


	return TRUE;
static BOOL nsc_encode_subsampling(NSC_CONTEXT* context)

	if (!context)
		return FALSE;

	if (tempHeight == 0)
		return FALSE;

	if (tempWidth > context->priv->PlaneBuffersLength / tempHeight)
		return FALSE;

		BYTE* co_dst = context->priv->PlaneBuffers[1] + y * (tempWidth >> 1);
		BYTE* cg_dst = context->priv->PlaneBuffers[2] + y * (tempWidth >> 1);
		const INT8* co_src0 = (INT8*) context->priv->PlaneBuffers[1] + (y << 1) * tempWidth;
		const INT8* co_src1 = co_src0 + tempWidth;
		const INT8* cg_src0 = (INT8*) context->priv->PlaneBuffers[2] + (y << 1) * tempWidth;
		const INT8* cg_src1 = cg_src0 + tempWidth;

	return TRUE;
BOOL nsc_encode(NSC_CONTEXT* context, const BYTE* bmpdata, UINT32 rowstride)
	if (!context || !bmpdata || (rowstride == 0))
		return FALSE;

	if (!nsc_encode_argb_to_aycocg(context, bmpdata, rowstride))
		return FALSE;
		if (!nsc_encode_subsampling(context))
			return FALSE;

	return TRUE;
static UINT32 nsc_rle_encode(const BYTE* in, BYTE* out, UINT32 originalSize)
FREERDP_LOCAL BOOL nsc_encode(NSC_CONTEXT* context, const BYTE* bmpdata,
static BOOL nsc_encode_sse2(NSC_CONTEXT* context, const BYTE* data,

	return TRUE;