1
- use std:: ffi:: OsStr ;
1
+ use std:: ffi:: { OsStr , OsString } ;
2
2
use std:: { iter, mem} ;
3
3
use std:: convert:: TryFrom ;
4
4
@@ -456,6 +456,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
456
456
}
457
457
}
458
458
459
+ /// Dispatches to appropriate implementations for reading an OsString from Memory,
460
+ /// depending on the interpretation target.
461
+ /// FIXME: Use `Cow` to avoid copies
462
+ fn read_os_str_from_target_str ( & self , scalar : Scalar < Tag > ) -> InterpResult < ' tcx , OsString > {
463
+ let target_os = self . eval_context_ref ( ) . tcx . sess . target . target . target_os . as_str ( ) ;
464
+ match target_os {
465
+ "linux" | "macos" => self . read_os_str_from_c_str ( scalar) . map ( |x| x. to_os_string ( ) ) ,
466
+ "windows" => self . read_os_str_from_wide_str ( scalar) ,
467
+ unsupported => throw_unsup_format ! ( "OsString support for target OS `{}` not yet available" , unsupported) ,
468
+ }
469
+ }
470
+
459
471
/// Helper function to read an OsString from a null-terminated sequence of bytes, which is what
460
472
/// the Unix APIs usually handle.
461
473
fn read_os_str_from_c_str < ' a > ( & ' a self , scalar : Scalar < Tag > ) -> InterpResult < ' tcx , & ' a OsStr >
@@ -471,14 +483,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
471
483
fn bytes_to_os_str < ' tcx , ' a > ( bytes : & ' a [ u8 ] ) -> InterpResult < ' tcx , & ' a OsStr > {
472
484
let s = std:: str:: from_utf8 ( bytes)
473
485
. map_err ( |_| err_unsup_format ! ( "{:?} is not a valid utf-8 string" , bytes) ) ?;
474
- Ok ( & OsStr :: new ( s) )
486
+ Ok ( OsStr :: new ( s) )
475
487
}
476
488
477
489
let this = self . eval_context_ref ( ) ;
478
490
let bytes = this. memory . read_c_str ( scalar) ?;
479
491
bytes_to_os_str ( bytes)
480
492
}
481
493
494
+ /// Helper function to read an OsString from a 0x0000-terminated sequence of u16,
495
+ /// which is what the Windows APIs usually handle.
496
+ fn read_os_str_from_wide_str < ' a > ( & ' a self , scalar : Scalar < Tag > ) -> InterpResult < ' tcx , OsString >
497
+ where
498
+ ' tcx : ' a ,
499
+ ' mir : ' a ,
500
+ {
501
+ #[ cfg( windows) ]
502
+ pub fn u16vec_to_osstring < ' tcx , ' a > ( u16_vec : Vec < u16 > ) -> InterpResult < ' tcx , OsString > {
503
+ Ok ( std:: os:: windows:: ffi:: OsStringExt :: from_wide ( & u16_vec[ ..] ) )
504
+ }
505
+ #[ cfg( not( windows) ) ]
506
+ pub fn u16vec_to_osstring < ' tcx , ' a > ( u16_vec : Vec < u16 > ) -> InterpResult < ' tcx , OsString > {
507
+ let s = String :: from_utf16 ( & u16_vec[ ..] )
508
+ . map_err ( |_| err_unsup_format ! ( "{:?} is not a valid utf-16 string" , u16_vec) ) ?;
509
+ Ok ( s. into ( ) )
510
+ }
511
+
512
+ let u16_vec = self . eval_context_ref ( ) . memory . read_wide_str ( scalar) ?;
513
+ u16vec_to_osstring ( u16_vec)
514
+ }
515
+
516
+
482
517
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
483
518
/// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
484
519
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
@@ -518,6 +553,66 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
518
553
Ok ( ( true , string_length) )
519
554
}
520
555
556
+ /// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what
557
+ /// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
558
+ /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
559
+ /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
560
+ /// string length returned does not include the null terminator.
561
+ fn write_os_str_to_wide_str (
562
+ & mut self ,
563
+ os_str : & OsStr ,
564
+ mplace : MPlaceTy < ' tcx , Tag > ,
565
+ size : u64 ,
566
+ ) -> InterpResult < ' tcx , ( bool , u64 ) > {
567
+ #[ cfg( windows) ]
568
+ fn os_str_to_u16vec < ' tcx > ( os_str : & OsStr ) -> InterpResult < ' tcx , Vec < u16 > > {
569
+ Ok ( std:: os:: windows:: ffi:: OsStrExt :: encode_wide ( os_str) . collect ( ) )
570
+ }
571
+ #[ cfg( not( windows) ) ]
572
+ fn os_str_to_u16vec < ' tcx > ( os_str : & OsStr ) -> InterpResult < ' tcx , Vec < u16 > > {
573
+ // On non-Windows platforms the best we can do to transform Vec<u16> from/to OS strings is to do the
574
+ // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually
575
+ // valid.
576
+ os_str
577
+ . to_str ( )
578
+ . map ( |s| s. encode_utf16 ( ) . collect ( ) )
579
+ . ok_or_else ( || err_unsup_format ! ( "{:?} is not a valid utf-8 string" , os_str) . into ( ) )
580
+ }
581
+
582
+ let u16_vec = os_str_to_u16vec ( os_str) ?;
583
+ // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required
584
+ // 0x0000 terminator to memory would cause an out-of-bounds access.
585
+ let string_length = u64:: try_from ( u16_vec. len ( ) ) . unwrap ( ) ;
586
+ if size <= string_length {
587
+ return Ok ( ( false , string_length) ) ;
588
+ }
589
+
590
+ let this = self . eval_context_mut ( ) ;
591
+
592
+ // Store the UTF-16 string.
593
+ let char_size = Size :: from_bytes ( 2 ) ;
594
+ for ( idx, c) in u16_vec. into_iter ( ) . chain ( iter:: once ( 0x0000 ) ) . enumerate ( ) {
595
+ let place = this. mplace_field ( mplace, idx as u64 ) ?;
596
+ this. write_scalar ( Scalar :: from_uint ( c, char_size) , place. into ( ) ) ?;
597
+ }
598
+ Ok ( ( true , string_length) )
599
+ }
600
+
601
+ /// Dispatches to appropriate implementations for allocating & writing OsString in Memory,
602
+ /// depending on the interpretation target.
603
+ fn alloc_os_str_as_target_str (
604
+ & mut self ,
605
+ os_str : & OsStr ,
606
+ memkind : MemoryKind < MiriMemoryKind > ,
607
+ ) -> InterpResult < ' tcx , Pointer < Tag > > {
608
+ let target_os = self . eval_context_ref ( ) . tcx . sess . target . target . target_os . as_str ( ) ;
609
+ match target_os {
610
+ "linux" | "macos" => Ok ( self . alloc_os_str_as_c_str ( os_str, memkind) ) ,
611
+ "windows" => Ok ( self . alloc_os_str_as_wide_str ( os_str, memkind) ) ,
612
+ unsupported => throw_unsup_format ! ( "OsString support for target OS `{}` not yet available" , unsupported) ,
613
+ }
614
+ }
615
+
521
616
fn alloc_os_str_as_c_str (
522
617
& mut self ,
523
618
os_str : & OsStr ,
@@ -528,7 +623,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
528
623
529
624
let arg_type = this. tcx . mk_array ( this. tcx . types . u8 , size) ;
530
625
let arg_place = this. allocate ( this. layout_of ( arg_type) . unwrap ( ) , memkind) ;
531
- self . write_os_str_to_c_str ( os_str, arg_place. ptr , size) . unwrap ( ) ;
626
+ assert ! ( self . write_os_str_to_c_str( os_str, arg_place. ptr, size) . unwrap( ) . 0 ) ;
627
+ arg_place. ptr . assert_ptr ( )
628
+ }
629
+
630
+ fn alloc_os_str_as_wide_str (
631
+ & mut self ,
632
+ os_str : & OsStr ,
633
+ memkind : MemoryKind < MiriMemoryKind > ,
634
+ ) -> Pointer < Tag > {
635
+ let size = u64:: try_from ( os_str. len ( ) ) . unwrap ( ) . checked_add ( 1 ) . unwrap ( ) ; // Make space for `0x0000` terminator.
636
+ let this = self . eval_context_mut ( ) ;
637
+
638
+ let arg_type = this. tcx . mk_array ( this. tcx . types . u16 , size) ;
639
+ let arg_place = this. allocate ( this. layout_of ( arg_type) . unwrap ( ) , memkind) ;
640
+ assert ! ( self . write_os_str_to_wide_str( os_str, arg_place, size) . unwrap( ) . 0 ) ;
532
641
arg_place. ptr . assert_ptr ( )
533
642
}
534
643
}
0 commit comments