@@ -601,6 +601,10 @@ trait CDFContextLogOps: CDFContextLogSize {
601601 let capacity = log. data . capacity ( ) ;
602602 debug_assert ! ( new_len <= capacity) ;
603603 let dst = log. data . as_mut_ptr ( ) . add ( len) ;
604+ // This reads beyond the end of `cdf`.
605+ // Since it's part of a larger data structure, the out-of-bounds data is valid.
606+ // However this violates the stacked-borrows memory model and is technically undefined behavior.
607+ // See https://github.com/xiph/rav1e/issues/3166
604608 dst. copy_from_nonoverlapping ( cdf. as_ptr ( ) , Self :: CDF_LEN_MAX ) ;
605609 * dst. add ( Self :: CDF_LEN_MAX ) = offset as u16 ;
606610 log. data . set_len ( new_len) ;
@@ -615,14 +619,20 @@ trait CDFContextLogOps: CDFContextLogSize {
615619 ) {
616620 let base = fc as * mut _ as * mut u8 ;
617621 let mut len = log. data . len ( ) ;
622+ debug_assert ! ( len % ( Self :: CDF_LEN_MAX + 1 ) == 0 ) ;
618623 // SAFETY: We use unchecked pointers here for performance.
619624 // Since we know the length, we can ensure not to go OOB.
620625 unsafe {
621626 let mut src = log. data . as_mut_ptr ( ) . add ( len) ;
622627 while len > checkpoint {
628+ debug_assert ! ( len >= Self :: CDF_LEN_MAX + 1 ) ;
623629 len -= Self :: CDF_LEN_MAX + 1 ;
624630 src = src. sub ( Self :: CDF_LEN_MAX + 1 ) ;
625631 let offset = * src. add ( Self :: CDF_LEN_MAX ) as usize ;
632+ debug_assert ! (
633+ offset + Self :: CDF_LEN_MAX * mem:: size_of:: <u16 >( )
634+ <= mem:: size_of:: <CDFContext >( )
635+ ) ;
626636 let dst = base. add ( offset) as * mut u16 ;
627637 dst. copy_from_nonoverlapping ( src, Self :: CDF_LEN_MAX ) ;
628638 }
0 commit comments