From 16df2300e1e3f5a51f68fb1626429e58b531b7c8 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Tue, 10 Mar 2026 10:21:40 +0100
Subject: [PATCH] [codec,dsp] fix array bounds checks

* assert array indices where caller value is an internal constant
* add missing length/bounds checks
---
 libfreerdp/codec/dsp.c | 79 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 70 insertions(+), 9 deletions(-)

Index: FreeRDP-3.10.3/libfreerdp/codec/dsp.c
===================================================================
--- FreeRDP-3.10.3.orig/libfreerdp/codec/dsp.c
+++ FreeRDP-3.10.3/libfreerdp/codec/dsp.c
@@ -321,7 +321,14 @@ static const INT16 ima_step_size_table[]
 static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned int channel,
                                           BYTE sample)
 {
-	const INT32 ss = ima_step_size_table[adpcm->ima.last_step[channel]];
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step));
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample));
+
+	const INT16 offset = adpcm->ima.last_step[channel];
+	WINPR_ASSERT(offset >= 0);
+	WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table));
+
+	const INT32 ss = ima_step_size_table[offset];
 	INT32 d = (ss >> 3);
 
 	if (sample & 1)
@@ -344,6 +351,7 @@ static UINT16 dsp_decode_ima_adpcm_sampl
 		d = 32767;
 
 	adpcm->ima.last_sample[channel] = (INT16)d;
+	WINPR_ASSERT(sample < ARRAYSIZE(ima_step_index_table));
 	adpcm->ima.last_step[channel] += ima_step_index_table[sample];
 
 	if (adpcm->ima.last_step[channel] < 0)
@@ -369,6 +377,9 @@ static BOOL freerdp_dsp_decode_ima_adpcm
 	{
 		if (size % block_size == 0)
 		{
+			if (size < 4)
+				return FALSE;
+
 			context->adpcm.ima.last_sample[0] =
 			    (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
 			context->adpcm.ima.last_step[0] = (INT16)(*(src + 2));
@@ -378,6 +389,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm
 
 			if (channels > 1)
 			{
+				if (size < 4)
+					return FALSE;
 				context->adpcm.ima.last_sample[1] =
 				    (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
 				context->adpcm.ima.last_step[1] = (INT16)(*(src + 2));
@@ -389,6 +402,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm
 
 		if (channels > 1)
 		{
+			if (size < 8)
+				return FALSE;
 			for (size_t i = 0; i < 8; i++)
 			{
 				BYTE* dst = Stream_Pointer(out);
@@ -417,6 +432,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm
 		}
 		else
 		{
+			if (size < 1)
+				return FALSE;
 			BYTE* dst = Stream_Pointer(out);
 			if (!Stream_SafeSeek(out, 4))
 				return FALSE;
@@ -746,7 +763,14 @@ static const struct
 
 static BYTE dsp_encode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, int channel, INT16 sample)
 {
-	INT32 ss = ima_step_size_table[adpcm->ima.last_step[channel]];
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step));
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample));
+
+	const INT16 offset = adpcm->ima.last_step[channel];
+	WINPR_ASSERT(offset >= 0);
+	WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table));
+
+	INT32 ss = ima_step_size_table[offset];
 	INT32 e = sample - adpcm->ima.last_sample[channel];
 	INT32 d = e;
 	INT32 diff = ss >> 3;
@@ -793,6 +817,8 @@ static BYTE dsp_encode_ima_adpcm_sample(
 		diff = 32767;
 
 	adpcm->ima.last_sample[channel] = (INT16)diff;
+
+	WINPR_ASSERT(enc < ARRAYSIZE(ima_step_index_table));
 	adpcm->ima.last_step[channel] += ima_step_index_table[enc];
 
 	if (adpcm->ima.last_step[channel] < 0)
@@ -892,11 +918,21 @@ static const INT32 ms_adpcm_coeffs2[7] =
 static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, BYTE sample,
                                                        int channel)
 {
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1));
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2));
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta));
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor));
 	const INT8 nibble = (sample & 0x08 ? (INT8)sample - 16 : (INT8)sample);
+	const BYTE predictor = adpcm->ms.predictor[channel];
+	INT32 coeff1 = 0;
+	if (predictor < ARRAYSIZE(ms_adpcm_coeffs1))
+		coeff1 = ms_adpcm_coeffs1[predictor];
+
+	INT32 coeff2 = 0;
+	if (predictor < ARRAYSIZE(ms_adpcm_coeffs2))
+		coeff2 = ms_adpcm_coeffs2[predictor];
 	INT32 presample =
-	    ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) +
-	     (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) /
-	    256;
+	    ((adpcm->ms.sample1[channel] * coeff1) + (adpcm->ms.sample2[channel] * coeff2)) / 256;
 	presample += nibble * adpcm->ms.delta[channel];
 
 	if (presample > 32767)
@@ -906,7 +942,12 @@ static INLINE INT16 freerdp_dsp_decode_m
 
 	adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel];
 	adpcm->ms.sample1[channel] = presample;
-	adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[sample] / 256;
+
+	INT32 tableval = 0;
+	if (sample < ARRAYSIZE(ms_adpcm_adaptation_table))
+		tableval = ms_adpcm_adaptation_table[sample];
+
+	adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * tableval / 256;
 
 	if (adpcm->ms.delta[channel] < 16)
 		adpcm->ms.delta[channel] = 16;
@@ -931,6 +972,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(
 		{
 			if (channels > 1)
 			{
+				if (size < 14)
+					return FALSE;
+
 				context->adpcm.ms.predictor[0] = *src++;
 				context->adpcm.ms.predictor[1] = *src++;
 				context->adpcm.ms.delta[0] = read_int16(src);
@@ -953,6 +997,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(
 			}
 			else
 			{
+				if (size < 7)
+					return FALSE;
+
 				context->adpcm.ms.predictor[0] = *src++;
 				context->adpcm.ms.delta[0] = read_int16(src);
 				src += 2;
@@ -969,6 +1016,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(
 		if (channels > 1)
 		{
 			{
+				if (size < 1)
+					return FALSE;
 				const BYTE sample = *src++;
 				size--;
 				Stream_Write_INT16(
@@ -977,6 +1026,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(
 				    out, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1));
 			}
 			{
+				if (size < 1)
+					return FALSE;
 				const BYTE sample = *src++;
 				size--;
 				Stream_Write_INT16(
@@ -987,6 +1038,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(
 		}
 		else
 		{
+			if (size < 1)
+				return FALSE;
 			const BYTE sample = *src++;
 			size--;
 			Stream_Write_INT16(out,
@@ -1000,8 +1053,13 @@ static BOOL freerdp_dsp_decode_ms_adpcm(
 }
 
 static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, INT32 sample,
-                                               int channel)
+                                               size_t channel)
 {
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1));
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2));
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta));
+	WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor));
+
 	INT32 presample =
 	    ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) +
 	     (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) /
@@ -1025,8 +1083,9 @@ static BYTE freerdp_dsp_encode_ms_adpcm_
 
 	adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel];
 	adpcm->ms.sample1[channel] = presample;
-	adpcm->ms.delta[channel] =
-	    adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[(((BYTE)errordelta) & 0x0F)] / 256;
+	const size_t offset = (((BYTE)errordelta) & 0x0F);
+	WINPR_ASSERT(offset < ARRAYSIZE(ms_adpcm_adaptation_table));
+	adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[offset] / 256;
 
 	if (adpcm->ms.delta[channel] < 16)
 		adpcm->ms.delta[channel] = 16;
