@@ -9,7 +9,6 @@ use crate::*;
9
9
10
10
pub struct FileHandle {
11
11
file : File ,
12
- flag : i32 ,
13
12
}
14
13
15
14
pub struct FileHandler {
@@ -44,27 +43,57 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
44
43
45
44
let mut options = OpenOptions :: new ( ) ;
46
45
47
- // The first two bits of the flag correspond to the access mode of the file in linux.
46
+ let o_rdonly = this. eval_libc_i32 ( "O_RDONLY" ) ?;
47
+ let o_wronly = this. eval_libc_i32 ( "O_WRONLY" ) ?;
48
+ let o_rdwr = this. eval_libc_i32 ( "O_RDWR" ) ?;
49
+ // The first two bits of the flag correspond to the access mode in linux, macOS and
50
+ // windows. We need to check that in fact the access mode flags for the current platform
51
+ // only use these two bits, otherwise we are in an unsupported platform and should error.
52
+ if ( o_rdonly | o_wronly | o_rdwr) & !0b11 != 0 {
53
+ throw_unsup_format ! ( "Access mode flags on this platform are unsupported" ) ;
54
+ }
55
+ // Now we check the access mode
48
56
let access_mode = flag & 0b11 ;
49
57
50
- if access_mode == this . eval_libc_i32 ( "O_RDONLY" ) ? {
58
+ if access_mode == o_rdonly {
51
59
options. read ( true ) ;
52
- } else if access_mode == this . eval_libc_i32 ( "O_WRONLY" ) ? {
60
+ } else if access_mode == o_wronly {
53
61
options. write ( true ) ;
54
- } else if access_mode == this . eval_libc_i32 ( "O_RDWR" ) ? {
62
+ } else if access_mode == o_rdwr {
55
63
options. read ( true ) . write ( true ) ;
56
64
} else {
57
65
throw_unsup_format ! ( "Unsupported access mode {:#x}" , access_mode) ;
58
66
}
67
+ // We need to check that there aren't unsupported options in `flag`. For this we try to
68
+ // reproduce the content of `flag` in the `mirror` variable using only the supported
69
+ // options.
70
+ let mut mirror = access_mode;
59
71
60
- if flag & this. eval_libc_i32 ( "O_APPEND" ) ? != 0 {
72
+ let o_append = this. eval_libc_i32 ( "O_APPEND" ) ?;
73
+ if flag & o_append != 0 {
61
74
options. append ( true ) ;
75
+ mirror |= o_append;
62
76
}
63
- if flag & this. eval_libc_i32 ( "O_TRUNC" ) ? != 0 {
77
+ let o_trunc = this. eval_libc_i32 ( "O_TRUNC" ) ?;
78
+ if flag & o_trunc != 0 {
64
79
options. truncate ( true ) ;
80
+ mirror |= o_trunc;
65
81
}
66
- if flag & this. eval_libc_i32 ( "O_CREAT" ) ? != 0 {
82
+ let o_creat = this. eval_libc_i32 ( "O_CREAT" ) ?;
83
+ if flag & o_creat != 0 {
67
84
options. create ( true ) ;
85
+ mirror |= o_creat;
86
+ }
87
+ let o_cloexec = this. eval_libc_i32 ( "O_CLOEXEC" ) ?;
88
+ if flag & o_cloexec != 0 {
89
+ // We do not need to do anything for this flag because `std` already sets it.
90
+ // (Technically we do not support *not* setting this flag, but we ignore that.)
91
+ mirror |= o_cloexec;
92
+ }
93
+ // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
94
+ // then we throw an error.
95
+ if flag != mirror {
96
+ throw_unsup_format ! ( "unsupported flags {:#x}" , flag & !mirror) ;
68
97
}
69
98
70
99
let path_bytes = this
@@ -76,7 +105,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
76
105
let fd = options. open ( path) . map ( |file| {
77
106
let mut fh = & mut this. machine . file_handler ;
78
107
fh. low += 1 ;
79
- fh. handles . insert ( fh. low , FileHandle { file, flag } ) ;
108
+ fh. handles . insert ( fh. low , FileHandle { file } ) ;
80
109
fh. low
81
110
} ) ;
82
111
@@ -87,7 +116,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
87
116
& mut self ,
88
117
fd_op : OpTy < ' tcx , Tag > ,
89
118
cmd_op : OpTy < ' tcx , Tag > ,
90
- arg_op : Option < OpTy < ' tcx , Tag > > ,
119
+ _arg1_op : Option < OpTy < ' tcx , Tag > > ,
91
120
) -> InterpResult < ' tcx , i32 > {
92
121
let this = self . eval_context_mut ( ) ;
93
122
@@ -97,29 +126,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
97
126
98
127
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
99
128
let cmd = this. read_scalar ( cmd_op) ?. to_i32 ( ) ?;
100
-
101
- if cmd == this. eval_libc_i32 ( "F_SETFD " ) ? {
102
- // This does not affect the file itself. Certain flags might require changing the file
103
- // or the way it is accessed somehow.
104
- let flag = this. read_scalar ( arg_op . unwrap ( ) ) ? . to_i32 ( ) ? ;
105
- // The only usage of this in stdlib at the moment is to enable the `FD_CLOEXEC` flag .
129
+ // We only support getting the flags for a descriptor
130
+ if cmd == this. eval_libc_i32 ( "F_GETFD " ) ? {
131
+ // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
132
+ // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
133
+ // always sets this flag when opening a file. However we still need to check that the
134
+ // file itself is open .
106
135
let fd_cloexec = this. eval_libc_i32 ( "FD_CLOEXEC" ) ?;
107
- if let Some ( FileHandle { flag : old_flag, .. } ) =
108
- this. machine . file_handler . handles . get_mut ( & fd)
109
- {
110
- // Check that the only difference between the old flag and the current flag is
111
- // exactly the `FD_CLOEXEC` value.
112
- if flag ^ * old_flag == fd_cloexec {
113
- * old_flag = flag;
114
- } else {
115
- throw_unsup_format ! ( "Unsupported arg {:#x} for `F_SETFD`" , flag) ;
116
- }
117
- }
118
- Ok ( 0 )
119
- } else if cmd == this. eval_libc_i32 ( "F_GETFD" ) ? {
120
- this. get_handle_and ( fd, |handle| Ok ( handle. flag ) )
136
+ this. get_handle_and ( fd, |_| Ok ( fd_cloexec) )
121
137
} else {
122
- throw_unsup_format ! ( "Unsupported command {:#x}" , cmd) ;
138
+ throw_unsup_format ! ( "The {:#x} command is not supported for `fcntl`) " , cmd) ;
123
139
}
124
140
}
125
141
0 commit comments