1
- use std:: ffi:: OsStr ;
1
+ use std:: ffi:: { OsStr , OsString } ;
2
2
use std:: { iter, mem} ;
3
3
4
4
use rustc:: mir;
@@ -447,29 +447,77 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
447
447
}
448
448
}
449
449
450
+ fn read_os_str_from_target_str < ' a > ( & ' a self , scalar : Scalar < Tag > ) -> InterpResult < ' tcx , OsString > {
451
+ if cfg ! ( target_os = "unix" ) {
452
+ self . read_os_str_from_c_str ( scalar)
453
+ } else if cfg ! ( target_os = "windows" ) {
454
+ self . read_os_str_from_wide_str ( scalar)
455
+ } else {
456
+ throw_unsup_format ! ( "support for target OS not yet available" )
457
+ }
458
+ }
459
+
450
460
/// Helper function to read an OsString from a null-terminated sequence of bytes, which is what
451
461
/// the Unix APIs usually handle.
452
- fn read_os_str_from_c_str < ' a > ( & ' a self , scalar : Scalar < Tag > ) -> InterpResult < ' tcx , & ' a OsStr >
462
+ fn read_os_str_from_c_str < ' a > ( & ' a self , scalar : Scalar < Tag > ) -> InterpResult < ' tcx , OsString >
453
463
where
454
464
' tcx : ' a ,
455
465
' mir : ' a ,
456
466
{
457
467
#[ cfg( target_os = "unix" ) ]
458
- fn bytes_to_os_str < ' tcx , ' a > ( bytes : & ' a [ u8 ] ) -> InterpResult < ' tcx , & ' a OsStr > {
459
- Ok ( std:: os:: unix:: ffi:: OsStringExt :: from_bytes ( bytes) )
468
+ fn bytes_to_os_str < ' tcx > ( bytes : & [ u8 ] ) -> InterpResult < ' tcx , OsString > {
469
+ Ok ( std:: os:: unix:: ffi:: OsStringExt :: from_bytes ( bytes) . to_os_string ( ) )
460
470
}
461
471
#[ cfg( not( target_os = "unix" ) ) ]
462
- fn bytes_to_os_str < ' tcx , ' a > ( bytes : & ' a [ u8 ] ) -> InterpResult < ' tcx , & ' a OsStr > {
472
+ fn bytes_to_os_str < ' tcx > ( bytes : & [ u8 ] ) -> InterpResult < ' tcx , OsString > {
463
473
let s = std:: str:: from_utf8 ( bytes)
464
474
. map_err ( |_| err_unsup_format ! ( "{:?} is not a valid utf-8 string" , bytes) ) ?;
465
- Ok ( & OsStr :: new ( s) )
475
+ Ok ( OsStr :: new ( s) . to_os_string ( ) )
466
476
}
467
477
468
478
let this = self . eval_context_ref ( ) ;
469
479
let bytes = this. memory . read_c_str ( scalar) ?;
470
480
bytes_to_os_str ( bytes)
471
481
}
472
482
483
+ /// Helper function to read an OsString from a 0x0000-terminated sequence of u16,
484
+ /// which is what the Windows APIs usually handle.
485
+ fn read_os_str_from_wide_str < ' a > ( & ' a self , scalar : Scalar < Tag > ) -> InterpResult < ' tcx , OsString >
486
+ where
487
+ ' tcx : ' a ,
488
+ ' mir : ' a ,
489
+ {
490
+ #[ cfg( target_os = "windows" ) ]
491
+ fn u16vec_to_osstring < ' tcx > ( u16_vec : Vec < u16 > ) -> InterpResult < ' tcx , OsString > {
492
+ Ok ( std:: os:: windows:: ffi:: OsStringExt :: from_wide ( & u16_vec[ ..] ) )
493
+ }
494
+ #[ cfg( not( target_os = "windows" ) ) ]
495
+ fn u16vec_to_osstring < ' tcx > ( u16_vec : Vec < u16 > ) -> InterpResult < ' tcx , OsString > {
496
+ let s = String :: from_utf16 ( & u16_vec[ ..] )
497
+ . map_err ( |_| err_unsup_format ! ( "{:?} is not a valid utf-16 string" , u16_vec) ) ?;
498
+ Ok ( s. into ( ) )
499
+ }
500
+
501
+ let this = self . eval_context_ref ( ) ;
502
+ let u16_vec = this. memory . read_wide_str ( scalar) ?;
503
+ u16vec_to_osstring ( u16_vec)
504
+ }
505
+
506
+ fn write_os_str_to_target_str (
507
+ & mut self ,
508
+ os_str : & OsStr ,
509
+ scalar : Scalar < Tag > ,
510
+ size : u64 ,
511
+ ) -> InterpResult < ' tcx , ( bool , u64 ) > {
512
+ if cfg ! ( target_os = "unix" ) {
513
+ self . write_os_str_to_c_str ( os_str, scalar, size)
514
+ } else if cfg ! ( target_os = "windows" ) {
515
+ self . write_os_str_to_wide_str ( os_str, scalar, size)
516
+ } else {
517
+ panic ! ( "support for target OS not yet available" )
518
+ }
519
+ }
520
+
473
521
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
474
522
/// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
475
523
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
@@ -509,6 +557,62 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
509
557
Ok ( ( true , string_length) )
510
558
}
511
559
560
+ /// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what
561
+ /// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
562
+ /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
563
+ /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
564
+ /// string length returned does not include the null terminator.
565
+ fn write_os_str_to_wide_str (
566
+ & mut self ,
567
+ os_str : & OsStr ,
568
+ scalar : Scalar < Tag > ,
569
+ size : u64 ,
570
+ ) -> InterpResult < ' tcx , ( bool , u64 ) > {
571
+ #[ cfg( target_os = "windows" ) ]
572
+ fn os_str_to_u16vec ( os_str : & OsStr ) -> Vec < u16 > {
573
+ std:: os:: windows:: ffi:: OsStrExt :: encode_wide ( os_str) . collect ( )
574
+ }
575
+ #[ cfg( not( target_os = "windows" ) ) ]
576
+ fn os_str_to_u16vec ( os_str : & OsStr ) -> Vec < u16 > {
577
+ os_str. to_str ( ) . encode_utf16 ( ) . collect ( )
578
+ }
579
+
580
+ let u16_vec = os_str_to_u16vec ( os_str) ;
581
+ // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
582
+ // terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
583
+ let string_length = u16_vec. len ( ) as u64 ;
584
+ if size <= string_length {
585
+ return Ok ( ( false , string_length) ) ;
586
+ }
587
+
588
+ let this = self . eval_context_mut ( ) ;
589
+
590
+ // Store the UTF-16 string. We just allocated so we know the bounds are fine.
591
+ let char_size = Size :: from_bytes ( 2 ) ;
592
+ let place_ptr = scalar. assert_ptr ( ) ;
593
+ /*
594
+ for (idx, &c) in u16_vec.iter().enumerate() {
595
+ let place = this.mplace_field(place, idx as u64)?;
596
+ this.write_scalar(Scalar::from_uint(c, char_size), place.into())?;
597
+ }
598
+ */
599
+ Ok ( ( true , string_length) )
600
+ }
601
+
602
+ fn alloc_os_str_as_target_str (
603
+ & mut self ,
604
+ os_str : & OsStr ,
605
+ memkind : MemoryKind < MiriMemoryKind > ,
606
+ ) -> Pointer < Tag > {
607
+ if cfg ! ( target_os = "unix" ) {
608
+ self . alloc_os_str_as_c_str ( os_str, memkind)
609
+ } else if cfg ! ( target_os = "windows" ) {
610
+ self . alloc_os_str_as_wide_str ( os_str, memkind)
611
+ } else {
612
+ panic ! ( "support for target OS not yet available" )
613
+ }
614
+ }
615
+
512
616
fn alloc_os_str_as_c_str (
513
617
& mut self ,
514
618
os_str : & OsStr ,
@@ -522,6 +626,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
522
626
self . write_os_str_to_c_str ( os_str, arg_place. ptr , size) . unwrap ( ) ;
523
627
arg_place. ptr . assert_ptr ( )
524
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 = os_str. len ( ) as u64 + 1 ; // 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
+ self . write_os_str_to_wide_str ( os_str, arg_place. ptr , size) . unwrap ( ) ;
641
+ arg_place. ptr . assert_ptr ( )
642
+ }
525
643
}
526
644
527
645
pub fn immty_from_int_checked < ' tcx > (
0 commit comments