@@ -4546,9 +4546,17 @@ pub unsafe trait FromBytes: FromZeros {
4546
4546
Self : Sized ,
4547
4547
R : io:: Read ,
4548
4548
{
4549
- let mut buf = CoreMaybeUninit :: < Self > :: zeroed ( ) ;
4549
+ // NOTE(#2319, #2320): We do `buf.zero()` separately rather than
4550
+ // constructing `let buf = CoreMaybeUninit::zeroed()` because, if `Self`
4551
+ // contains padding bytes, then a typed copy of `CoreMaybeUninit<Self>`
4552
+ // will not necessarily preserve zeros written to those padding byte
4553
+ // locations, and so `buf` could contain uninitialized bytes.
4554
+ let mut buf = CoreMaybeUninit :: < Self > :: uninit ( ) ;
4555
+ buf. zero ( ) ;
4556
+
4550
4557
let ptr = Ptr :: from_mut ( & mut buf) ;
4551
- // SAFETY: `buf` consists entirely of initialized, zeroed bytes.
4558
+ // SAFETY: After `buf.zero()`, `buf` consists entirely of initialized,
4559
+ // zeroed bytes.
4552
4560
let ptr = unsafe { ptr. assume_validity :: < invariant:: Initialized > ( ) } ;
4553
4561
let ptr = ptr. as_bytes :: < BecauseExclusive > ( ) ;
4554
4562
src. read_exact ( ptr. as_mut ( ) ) ?;
@@ -5905,6 +5913,36 @@ mod tests {
5905
5913
assert_eq ! ( bytes, want) ;
5906
5914
}
5907
5915
5916
+ #[ test]
5917
+ #[ cfg( feature = "std" ) ]
5918
+ fn test_read_io_with_padding_soundness ( ) {
5919
+ // This test is designed to exhibit potential UB in
5920
+ // `FromBytes::read_from_io`. (see #2319, #2320).
5921
+
5922
+ // On most platforms (where `align_of::<u16>() == 2`), `WithPadding`
5923
+ // will have inter-field padding between `x` and `y`.
5924
+ #[ derive( FromBytes ) ]
5925
+ #[ repr( C ) ]
5926
+ struct WithPadding {
5927
+ x : u8 ,
5928
+ y : u16 ,
5929
+ }
5930
+ struct ReadsInRead ;
5931
+ impl std:: io:: Read for ReadsInRead {
5932
+ fn read ( & mut self , buf : & mut [ u8 ] ) -> std:: io:: Result < usize > {
5933
+ // This body branches on every byte of `buf`, ensuring that it
5934
+ // exhibits UB if any byte of `buf` is uninitialized.
5935
+ if buf. iter ( ) . all ( |& x| x == 0 ) {
5936
+ Ok ( buf. len ( ) )
5937
+ } else {
5938
+ buf. iter_mut ( ) . for_each ( |x| * x = 0 ) ;
5939
+ Ok ( buf. len ( ) )
5940
+ }
5941
+ }
5942
+ }
5943
+ assert ! ( matches!( WithPadding :: read_from_io( ReadsInRead ) , Ok ( WithPadding { x: 0 , y: 0 } ) ) ) ;
5944
+ }
5945
+
5908
5946
#[ test]
5909
5947
#[ cfg( feature = "std" ) ]
5910
5948
fn test_read_write_io ( ) {
0 commit comments