@@ -9,6 +9,7 @@ use std::rc::{Rc, Weak};
99
1010use rustc_target:: abi:: Size ;
1111
12+ use crate :: helpers:: check_min_arg_count;
1213use crate :: shims:: unix:: linux:: epoll:: EpollReadyEvents ;
1314use crate :: shims:: unix:: * ;
1415use crate :: * ;
@@ -481,56 +482,62 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
481482 fn fcntl ( & mut self , args : & [ OpTy < ' tcx > ] ) -> InterpResult < ' tcx , Scalar > {
482483 let this = self . eval_context_mut ( ) ;
483484
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+
490487 let fd_num = this. read_scalar ( fd_num) ?. to_i32 ( ) ?;
491488 let cmd = this. read_scalar ( cmd) ?. to_i32 ( ) ?;
492489
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+
493494 // 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+ } ) )
523506 }
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+ }
529526 }
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+ }
530535
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+ }
534541 }
535542 }
536543
0 commit comments