@@ -23,15 +23,13 @@ use ironrdp_pdu::codecs::clearcodec::{
2323pub struct ClearCodecDecoder {
2424 vbar_cache : VBarCache ,
2525 glyph_cache : GlyphCache ,
26- expected_seq : u8 ,
2726}
2827
2928impl ClearCodecDecoder {
3029 pub fn new ( ) -> Self {
3130 Self {
3231 vbar_cache : VBarCache :: new ( ) ,
3332 glyph_cache : GlyphCache :: new ( ) ,
34- expected_seq : 0 ,
3533 }
3634 }
3735
@@ -44,13 +42,6 @@ impl ClearCodecDecoder {
4442 let mut src = ReadCursor :: new ( data) ;
4543 let stream = ClearCodecBitmapStream :: decode ( & mut src) ?;
4644
47- // Per spec (2.2.4.1 seqNumber): must increment by 1, wrapping 0xFF -> 0x00.
48- // Mismatches indicate packet loss or server restart. We tolerate them
49- // (FreeRDP does too) but re-sync to the received sequence number.
50- // ironrdp-graphics has no tracing dependency, so callers that need
51- // diagnostics should track sequence numbers externally.
52- self . expected_seq = stream. seq_number . wrapping_add ( 1 ) ;
53-
5445 // Handle cache reset
5546 if stream. is_cache_reset ( ) {
5647 self . vbar_cache . reset ( ) ;
@@ -78,6 +69,9 @@ impl ClearCodecDecoder {
7869 . glyph_cache
7970 . get ( glyph_index)
8071 . ok_or_else ( || invalid_field_err ! ( "glyphIndex" , "glyph cache miss on hit" ) ) ?;
72+ if entry. width != width || entry. height != height {
73+ return Err ( invalid_field_err ! ( "glyphIndex" , "cached glyph dimensions mismatch" ) ) ;
74+ }
8175 return Ok ( entry. pixels . clone ( ) ) ;
8276 }
8377
@@ -247,13 +241,22 @@ impl ClearCodecDecoder {
247241 surface_width : u16 ,
248242 ) -> DecodeResult < ( ) > {
249243 let sw = usize:: from ( surface_width) ;
244+ let sh = output. len ( ) / ( sw * 4 ) . max ( 1 ) ;
245+
246+ let x_end = usize:: from ( sub. x_start ) + usize:: from ( sub. width ) ;
247+ let y_end = usize:: from ( sub. y_start ) + usize:: from ( sub. height ) ;
248+ if x_end > sw || y_end > sh {
249+ return Err ( invalid_field_err ! ( "subcodec" , "region exceeds surface bounds" ) ) ;
250+ }
250251
251252 match sub. codec_id {
252253 SubcodecId :: Raw => {
253- // Raw BGR: 3 bytes per pixel
254254 let w = usize:: from ( sub. width ) ;
255255 let h = usize:: from ( sub. height ) ;
256- let expected = w * h * 3 ;
256+ let expected = w
257+ . checked_mul ( h)
258+ . and_then ( |v| v. checked_mul ( 3 ) )
259+ . ok_or_else ( || invalid_field_err ! ( "bitmapData" , "raw subcodec dimensions overflow" ) ) ?;
257260 if sub. bitmap_data . len ( ) < expected {
258261 return Err ( invalid_field_err ! ( "bitmapData" , "raw subcodec data too short" ) ) ;
259262 }
@@ -263,65 +266,61 @@ impl ClearCodecDecoder {
263266 let y = usize:: from ( sub. y_start ) + row;
264267 let src_idx = ( row * w + col) * 3 ;
265268 let dst_idx = ( y * sw + x) * 4 ;
266- if dst_idx + 3 < output. len ( ) && src_idx + 2 < sub. bitmap_data . len ( ) {
267- output[ dst_idx] = sub. bitmap_data [ src_idx] ; // B
268- output[ dst_idx + 1 ] = sub. bitmap_data [ src_idx + 1 ] ; // G
269- output[ dst_idx + 2 ] = sub. bitmap_data [ src_idx + 2 ] ; // R
270- output[ dst_idx + 3 ] = 0xFF ; // A
271- }
269+ output[ dst_idx] = sub. bitmap_data [ src_idx] ;
270+ output[ dst_idx + 1 ] = sub. bitmap_data [ src_idx + 1 ] ;
271+ output[ dst_idx + 2 ] = sub. bitmap_data [ src_idx + 2 ] ;
272+ output[ dst_idx + 3 ] = 0xFF ;
272273 }
273274 }
274275 }
275276 SubcodecId :: Rlex => {
276- // RLEX: decode palette + run/suite segments
277277 let rlex = ironrdp_pdu:: codecs:: clearcodec:: decode_rlex ( sub. bitmap_data ) ?;
278278 let w = usize:: from ( sub. width ) ;
279+ let region_pixels = usize:: from ( sub. width ) * usize:: from ( sub. height ) ;
280+ let palette_len = rlex. palette . len ( ) ;
279281 let mut px = 0usize ;
280282
281283 for seg in & rlex. segments {
282- // Run: repeat start_index color for run_length pixels
283- if let Some ( color) = rlex. palette . get ( usize:: from ( seg. start_index ) ) {
284- for _ in 0 ..seg. run_length {
285- let col = px % w;
286- let row = px / w;
287- let x = usize:: from ( sub. x_start ) + col;
288- let y = usize:: from ( sub. y_start ) + row;
289- let dst_idx = ( y * sw + x) * 4 ;
290- if dst_idx + 3 < output. len ( ) {
291- output[ dst_idx] = color[ 0 ] ; // B
292- output[ dst_idx + 1 ] = color[ 1 ] ; // G
293- output[ dst_idx + 2 ] = color[ 2 ] ; // R
294- output[ dst_idx + 3 ] = 0xFF ;
295- }
296- px += 1 ;
284+ if usize:: from ( seg. start_index ) >= palette_len {
285+ return Err ( invalid_field_err ! ( "rlex" , "start_index exceeds palette size" ) ) ;
286+ }
287+ if usize:: from ( seg. stop_index ) >= palette_len {
288+ return Err ( invalid_field_err ! ( "rlex" , "stop_index exceeds palette size" ) ) ;
289+ }
290+
291+ let color = & rlex. palette [ usize:: from ( seg. start_index ) ] ;
292+ for _ in 0 ..seg. run_length {
293+ if px >= region_pixels {
294+ return Err ( invalid_field_err ! ( "rlex" , "run exceeds region pixel count" ) ) ;
297295 }
296+ let x = usize:: from ( sub. x_start ) + px % w;
297+ let y = usize:: from ( sub. y_start ) + px / w;
298+ let dst_idx = ( y * sw + x) * 4 ;
299+ output[ dst_idx] = color[ 0 ] ;
300+ output[ dst_idx + 1 ] = color[ 1 ] ;
301+ output[ dst_idx + 2 ] = color[ 2 ] ;
302+ output[ dst_idx + 3 ] = 0xFF ;
303+ px += 1 ;
298304 }
299305
300- // Suite: sequential palette walk from start_index to stop_index
301306 for palette_idx in seg. start_index ..=seg. stop_index {
302- if let Some ( color) = rlex. palette . get ( usize:: from ( palette_idx) ) {
303- let col = px % w;
304- let row = px / w;
305- let x = usize:: from ( sub. x_start ) + col;
306- let y = usize:: from ( sub. y_start ) + row;
307- let dst_idx = ( y * sw + x) * 4 ;
308- if dst_idx + 3 < output. len ( ) {
309- output[ dst_idx] = color[ 0 ] ;
310- output[ dst_idx + 1 ] = color[ 1 ] ;
311- output[ dst_idx + 2 ] = color[ 2 ] ;
312- output[ dst_idx + 3 ] = 0xFF ;
313- }
314- px += 1 ;
307+ if px >= region_pixels {
308+ return Err ( invalid_field_err ! ( "rlex" , "suite exceeds region pixel count" ) ) ;
315309 }
310+ let color = & rlex. palette [ usize:: from ( palette_idx) ] ;
311+ let x = usize:: from ( sub. x_start ) + px % w;
312+ let y = usize:: from ( sub. y_start ) + px / w;
313+ let dst_idx = ( y * sw + x) * 4 ;
314+ output[ dst_idx] = color[ 0 ] ;
315+ output[ dst_idx + 1 ] = color[ 1 ] ;
316+ output[ dst_idx + 2 ] = color[ 2 ] ;
317+ output[ dst_idx + 3 ] = 0xFF ;
318+ px += 1 ;
316319 }
317320 }
318321 }
319322 SubcodecId :: NsCodec => {
320- // NSCodec (MS-RDPNSC) decode not yet implemented.
321- // Encoder avoids generating NSCodec tiles, so this path only
322- // triggers when decoding streams from other implementations.
323- // The region is left at its current pixel values (transparent
324- // or whatever the residual layer filled in).
323+ // Not yet implemented; encoder avoids generating NSCodec tiles.
325324 }
326325 }
327326
@@ -363,7 +362,7 @@ impl ClearCodecEncoder {
363362 pub fn encode ( & mut self , bgra : & [ u8 ] , width : u16 , height : u16 ) -> Vec < u8 > {
364363 let w = usize:: from ( width) ;
365364 let h = usize:: from ( height) ;
366- let pixel_count = w * h ;
365+ let pixel_count = w. saturating_mul ( h ) ;
367366 let use_glyph = pixel_count <= 1024 ;
368367
369368 // Check glyph cache for exact match
0 commit comments