@@ -9,6 +9,7 @@ use std::rc::{Rc, Weak};
9
9
10
10
use rustc_target:: abi:: Size ;
11
11
12
+ use crate :: helpers:: check_min_arg_count;
12
13
use crate :: shims:: unix:: linux:: epoll:: EpollReadyEvents ;
13
14
use crate :: shims:: unix:: * ;
14
15
use crate :: * ;
@@ -481,56 +482,62 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
481
482
fn fcntl ( & mut self , args : & [ OpTy < ' tcx > ] ) -> InterpResult < ' tcx , Scalar > {
482
483
let this = self . eval_context_mut ( ) ;
483
484
484
- let [ fd_num, cmd, ..] = args else {
485
- throw_ub_format ! (
486
- "incorrect number of arguments for fcntl: got {}, expected at least 2" ,
487
- args. len( )
488
- ) ;
489
- } ;
485
+ let [ fd_num, cmd] = check_min_arg_count ( "fcntl" , args) ?;
486
+
490
487
let fd_num = this. read_scalar ( fd_num) ?. to_i32 ( ) ?;
491
488
let cmd = this. read_scalar ( cmd) ?. to_i32 ( ) ?;
492
489
490
+ let f_getfd = this. eval_libc_i32 ( "F_GETFD" ) ;
491
+ let f_dupfd = this. eval_libc_i32 ( "F_DUPFD" ) ;
492
+ let f_dupfd_cloexec = this. eval_libc_i32 ( "F_DUPFD_CLOEXEC" ) ;
493
+
493
494
// We only support getting the flags for a descriptor.
494
- if cmd == this. eval_libc_i32 ( "F_GETFD" ) {
495
- // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
496
- // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
497
- // always sets this flag when opening a file. However we still need to check that the
498
- // file itself is open.
499
- interp_ok ( Scalar :: from_i32 ( if this. machine . fds . is_fd_num ( fd_num) {
500
- this. eval_libc_i32 ( "FD_CLOEXEC" )
501
- } else {
502
- this. fd_not_found ( ) ?
503
- } ) )
504
- } else if cmd == this. eval_libc_i32 ( "F_DUPFD" )
505
- || cmd == this. eval_libc_i32 ( "F_DUPFD_CLOEXEC" )
506
- {
507
- // Note that we always assume the FD_CLOEXEC flag is set for every open file, in part
508
- // because exec() isn't supported. The F_DUPFD and F_DUPFD_CLOEXEC commands only
509
- // differ in whether the FD_CLOEXEC flag is pre-set on the new file descriptor,
510
- // thus they can share the same implementation here.
511
- let [ _, _, start, ..] = args else {
512
- throw_ub_format ! (
513
- "incorrect number of arguments for fcntl with cmd=`F_DUPFD`/`F_DUPFD_CLOEXEC`: got {}, expected at least 3" ,
514
- args. len( )
515
- ) ;
516
- } ;
517
- let start = this. read_scalar ( start) ?. to_i32 ( ) ?;
518
-
519
- match this. machine . fds . get ( fd_num) {
520
- Some ( fd) =>
521
- interp_ok ( Scalar :: from_i32 ( this. machine . fds . insert_with_min_num ( fd, start) ) ) ,
522
- None => interp_ok ( Scalar :: from_i32 ( this. fd_not_found ( ) ?) ) ,
495
+ match cmd {
496
+ cmd if cmd == f_getfd => {
497
+ // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
498
+ // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
499
+ // always sets this flag when opening a file. However we still need to check that the
500
+ // file itself is open.
501
+ interp_ok ( Scalar :: from_i32 ( if this. machine . fds . is_fd_num ( fd_num) {
502
+ this. eval_libc_i32 ( "FD_CLOEXEC" )
503
+ } else {
504
+ this. fd_not_found ( ) ?
505
+ } ) )
523
506
}
524
- } else if this. tcx . sess . target . os == "macos" && cmd == this. eval_libc_i32 ( "F_FULLFSYNC" ) {
525
- // Reject if isolation is enabled.
526
- if let IsolatedOp :: Reject ( reject_with) = this. machine . isolated_op {
527
- this. reject_in_isolation ( "`fcntl`" , reject_with) ?;
528
- return this. set_last_error_and_return_i32 ( ErrorKind :: PermissionDenied ) ;
507
+ cmd if cmd == f_dupfd || cmd == f_dupfd_cloexec => {
508
+ // Note that we always assume the FD_CLOEXEC flag is set for every open file, in part
509
+ // because exec() isn't supported. The F_DUPFD and F_DUPFD_CLOEXEC commands only
510
+ // differ in whether the FD_CLOEXEC flag is pre-set on the new file descriptor,
511
+ // thus they can share the same implementation here.
512
+ let cmd_name = if cmd == f_dupfd {
513
+ "fcntl(fd, F_DUPFD, ...)"
514
+ } else {
515
+ "fcntl(fd, F_DUPFD_CLOEXEC, ...)"
516
+ } ;
517
+
518
+ let [ _, _, start] = check_min_arg_count ( cmd_name, args) ?;
519
+ let start = this. read_scalar ( start) ?. to_i32 ( ) ?;
520
+
521
+ if let Some ( fd) = this. machine . fds . get ( fd_num) {
522
+ interp_ok ( Scalar :: from_i32 ( this. machine . fds . insert_with_min_num ( fd, start) ) )
523
+ } else {
524
+ interp_ok ( Scalar :: from_i32 ( this. fd_not_found ( ) ?) )
525
+ }
529
526
}
527
+ cmd if this. tcx . sess . target . os == "macos"
528
+ && cmd == this. eval_libc_i32 ( "F_FULLFSYNC" ) =>
529
+ {
530
+ // Reject if isolation is enabled.
531
+ if let IsolatedOp :: Reject ( reject_with) = this. machine . isolated_op {
532
+ this. reject_in_isolation ( "`fcntl`" , reject_with) ?;
533
+ return this. set_last_error_and_return_i32 ( ErrorKind :: PermissionDenied ) ;
534
+ }
530
535
531
- this. ffullsync_fd ( fd_num)
532
- } else {
533
- throw_unsup_format ! ( "the {:#x} command is not supported for `fcntl`)" , cmd) ;
536
+ this. ffullsync_fd ( fd_num)
537
+ }
538
+ cmd => {
539
+ throw_unsup_format ! ( "fcntl: unsupported command {cmd:#x}" ) ;
540
+ }
534
541
}
535
542
}
536
543
0 commit comments