@@ -479,6 +479,36 @@ fn append_pax_entry(archive: &mut Vec<u8>, typeflag: u8, payload: &[u8]) {
479479 ) ;
480480}
481481
482+ fn cve_2025_62518_archive ( ) -> ( [ u8 ; BLOCK_SIZE ] , Vec < u8 > ) {
483+ let mut archive = Vec :: new ( ) ;
484+ append_entry (
485+ & mut archive,
486+ build_header ( b"normal.txt" , b'0' , 6 , HeaderFlavor :: Ustar ) ,
487+ b"normal" ,
488+ ) ;
489+
490+ let pax = pax_record_bytes ( "size" , b"1024" ) ;
491+ append_pax_entry ( & mut archive, b'x' , & pax) ;
492+
493+ let fake_header = build_header ( b"smuggled.txt" , b'0' , 4 , HeaderFlavor :: Ustar ) ;
494+ let mut payload = vec ! [ 0 ; 1024 ] ;
495+ payload[ ..BLOCK_SIZE ] . copy_from_slice ( & fake_header) ;
496+ payload[ BLOCK_SIZE ..BLOCK_SIZE + 4 ] . copy_from_slice ( b"EVIL" ) ;
497+
498+ append_entry (
499+ & mut archive,
500+ build_header ( b"blob.bin" , b'0' , 0 , HeaderFlavor :: Ustar ) ,
501+ & payload,
502+ ) ;
503+ append_entry (
504+ & mut archive,
505+ build_header ( b"marker.txt" , b'0' , 6 , HeaderFlavor :: Ustar ) ,
506+ b"marker" ,
507+ ) ;
508+ append_eof ( & mut archive) ;
509+ ( fake_header, archive)
510+ }
511+
482512#[ test]
483513fn read_simple_archive ( ) -> Result < ( ) > {
484514 block_on_test ( async {
@@ -755,7 +785,8 @@ fn tar_writer_inherent_write_and_finish_round_trip() -> Result<()> {
755785
756786 writer. finish ( ) . await ?;
757787
758- let entries = collect_entries_async ( Cursor :: new ( shared. lock ( ) . unwrap ( ) . clone ( ) ) ) . await ?;
788+ let buffer = shared. lock ( ) . unwrap ( ) . clone ( ) ;
789+ let entries = collect_entries_async ( Cursor :: new ( buffer) ) . await ?;
759790 assert_eq ! (
760791 entries,
761792 vec![
@@ -1911,6 +1942,92 @@ fn reader_applies_extended_pax_metadata() -> Result<()> {
19111942 } )
19121943}
19131944
1945+ #[ test]
1946+ fn reader_does_not_surface_smuggled_entries_from_pax_size_override ( ) -> Result < ( ) > {
1947+ block_on_test ( async {
1948+ let ( fake_header, archive) = cve_2025_62518_archive ( ) ;
1949+ let mut stream = Box :: pin ( TarReader :: new ( Cursor :: new ( archive) ) ) ;
1950+
1951+ match stream. next ( ) . await . unwrap ( ) ? {
1952+ TarEntry :: File ( mut file) => {
1953+ let mut data = Vec :: new ( ) ;
1954+ file. read_to_end ( & mut data) . await ?;
1955+ assert_eq ! ( file. path( ) , "normal.txt" ) ;
1956+ assert_eq ! ( data, b"normal" ) ;
1957+ }
1958+ other => panic ! ( "expected leading file, got {:?}" , other) ,
1959+ }
1960+
1961+ match stream. next ( ) . await . unwrap ( ) ? {
1962+ TarEntry :: File ( mut file) => {
1963+ let mut data = Vec :: new ( ) ;
1964+ file. read_to_end ( & mut data) . await ?;
1965+ assert_eq ! ( file. path( ) , "blob.bin" ) ;
1966+ assert_eq ! ( file. size( ) , 1024 ) ;
1967+ assert_eq ! ( data. len( ) , 1024 ) ;
1968+ assert_eq ! ( & data[ ..BLOCK_SIZE ] , & fake_header) ;
1969+ assert_eq ! ( & data[ BLOCK_SIZE ..BLOCK_SIZE + 4 ] , b"EVIL" ) ;
1970+ }
1971+ other => panic ! ( "expected CVE regression file, got {:?}" , other) ,
1972+ }
1973+
1974+ match stream. next ( ) . await . unwrap ( ) ? {
1975+ TarEntry :: File ( mut file) => {
1976+ let mut data = Vec :: new ( ) ;
1977+ file. read_to_end ( & mut data) . await ?;
1978+ assert_eq ! ( file. path( ) , "marker.txt" ) ;
1979+ assert_eq ! ( data, b"marker" ) ;
1980+ }
1981+ other => panic ! ( "expected trailing marker file, got {:?}" , other) ,
1982+ }
1983+
1984+ assert ! ( stream. next( ) . await . is_none( ) ) ;
1985+ Ok ( ( ) )
1986+ } )
1987+ }
1988+
1989+ #[ test]
1990+ fn dropping_pax_sized_file_skips_over_embedded_tar_headers ( ) -> Result < ( ) > {
1991+ block_on_test ( async {
1992+ let ( _, archive) = cve_2025_62518_archive ( ) ;
1993+ let reader = ChunkedReader :: new ( Cursor :: new ( archive) , 17 ) ;
1994+ let mut stream = Box :: pin ( TarReader :: new ( reader) ) ;
1995+
1996+ match stream. next ( ) . await . unwrap ( ) ? {
1997+ TarEntry :: File ( mut file) => {
1998+ let mut data = Vec :: new ( ) ;
1999+ file. read_to_end ( & mut data) . await ?;
2000+ assert_eq ! ( file. path( ) , "normal.txt" ) ;
2001+ assert_eq ! ( data, b"normal" ) ;
2002+ }
2003+ other => panic ! ( "expected leading file, got {:?}" , other) ,
2004+ }
2005+
2006+ match stream. next ( ) . await . unwrap ( ) ? {
2007+ TarEntry :: File ( mut file) => {
2008+ assert_eq ! ( file. path( ) , "blob.bin" ) ;
2009+ assert_eq ! ( file. size( ) , 1024 ) ;
2010+ let mut byte = [ 0u8 ; 1 ] ;
2011+ file. read_exact ( & mut byte) . await ?;
2012+ }
2013+ other => panic ! ( "expected CVE regression file, got {:?}" , other) ,
2014+ }
2015+
2016+ match stream. next ( ) . await . unwrap ( ) ? {
2017+ TarEntry :: File ( mut file) => {
2018+ let mut data = Vec :: new ( ) ;
2019+ file. read_to_end ( & mut data) . await ?;
2020+ assert_eq ! ( file. path( ) , "marker.txt" ) ;
2021+ assert_eq ! ( data, b"marker" ) ;
2022+ }
2023+ other => panic ! ( "expected trailing marker file, got {:?}" , other) ,
2024+ }
2025+
2026+ assert ! ( stream. next( ) . await . is_none( ) ) ;
2027+ Ok ( ( ) )
2028+ } )
2029+ }
2030+
19142031#[ test]
19152032fn reader_rejects_invalid_pax_time_metadata ( ) {
19162033 for ( value, expected) in [
0 commit comments