1use std::io;
2
3use crate::*;
4
5#[derive(Debug)]
8pub enum IoError {
9 LibcError(&'static str),
10 WindowsError(&'static str),
11 HostError(io::Error),
12 Raw(Scalar),
13}
14pub use self::IoError::*;
15
16impl From<io::Error> for IoError {
17 fn from(value: io::Error) -> Self {
18 IoError::HostError(value)
19 }
20}
21
22impl From<io::ErrorKind> for IoError {
23 fn from(value: io::ErrorKind) -> Self {
24 IoError::HostError(value.into())
25 }
26}
27
28impl From<Scalar> for IoError {
29 fn from(value: Scalar) -> Self {
30 IoError::Raw(value)
31 }
32}
33
34const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
37 use std::io::ErrorKind::*;
38 &[
39 ("E2BIG", ArgumentListTooLong),
40 ("EADDRINUSE", AddrInUse),
41 ("EADDRNOTAVAIL", AddrNotAvailable),
42 ("EBUSY", ResourceBusy),
43 ("ECONNABORTED", ConnectionAborted),
44 ("ECONNREFUSED", ConnectionRefused),
45 ("ECONNRESET", ConnectionReset),
46 ("EDEADLK", Deadlock),
47 ("EDQUOT", QuotaExceeded),
48 ("EEXIST", AlreadyExists),
49 ("EFBIG", FileTooLarge),
50 ("EHOSTUNREACH", HostUnreachable),
51 ("EINTR", Interrupted),
52 ("EINVAL", InvalidInput),
53 ("EISDIR", IsADirectory),
54 ("ELOOP", FilesystemLoop),
55 ("ENOENT", NotFound),
56 ("ENOMEM", OutOfMemory),
57 ("ENOSPC", StorageFull),
58 ("ENOSYS", Unsupported),
59 ("EMLINK", TooManyLinks),
60 ("ENAMETOOLONG", InvalidFilename),
61 ("ENETDOWN", NetworkDown),
62 ("ENETUNREACH", NetworkUnreachable),
63 ("ENOTCONN", NotConnected),
64 ("ENOTDIR", NotADirectory),
65 ("ENOTEMPTY", DirectoryNotEmpty),
66 ("EPIPE", BrokenPipe),
67 ("EROFS", ReadOnlyFilesystem),
68 ("ESPIPE", NotSeekable),
69 ("ESTALE", StaleNetworkFileHandle),
70 ("ETIMEDOUT", TimedOut),
71 ("ETXTBSY", ExecutableFileBusy),
72 ("EXDEV", CrossesDevices),
73 ("EPERM", PermissionDenied),
76 ("EACCES", PermissionDenied),
77 ("EWOULDBLOCK", WouldBlock),
78 ("EAGAIN", WouldBlock),
79 ]
80};
81const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
84 use std::io::ErrorKind::*;
85 &[
90 ("WSAEADDRINUSE", AddrInUse),
91 ("WSAEADDRNOTAVAIL", AddrNotAvailable),
92 ("ERROR_ALREADY_EXISTS", AlreadyExists),
93 ("ERROR_FILE_EXISTS", AlreadyExists),
94 ("ERROR_NO_DATA", BrokenPipe),
95 ("WSAECONNABORTED", ConnectionAborted),
96 ("WSAECONNREFUSED", ConnectionRefused),
97 ("WSAECONNRESET", ConnectionReset),
98 ("ERROR_NOT_SAME_DEVICE", CrossesDevices),
99 ("ERROR_POSSIBLE_DEADLOCK", Deadlock),
100 ("ERROR_DIR_NOT_EMPTY", DirectoryNotEmpty),
101 ("ERROR_CANT_RESOLVE_FILENAME", FilesystemLoop),
102 ("ERROR_DISK_QUOTA_EXCEEDED", QuotaExceeded),
103 ("WSAEDQUOT", QuotaExceeded),
104 ("ERROR_FILE_TOO_LARGE", FileTooLarge),
105 ("ERROR_HOST_UNREACHABLE", HostUnreachable),
106 ("WSAEHOSTUNREACH", HostUnreachable),
107 ("ERROR_INVALID_NAME", InvalidFilename),
108 ("ERROR_BAD_PATHNAME", InvalidFilename),
109 ("ERROR_FILENAME_EXCED_RANGE", InvalidFilename),
110 ("ERROR_INVALID_PARAMETER", InvalidInput),
111 ("WSAEINVAL", InvalidInput),
112 ("ERROR_DIRECTORY_NOT_SUPPORTED", IsADirectory),
113 ("WSAENETDOWN", NetworkDown),
114 ("ERROR_NETWORK_UNREACHABLE", NetworkUnreachable),
115 ("WSAENETUNREACH", NetworkUnreachable),
116 ("ERROR_DIRECTORY", NotADirectory),
117 ("WSAENOTCONN", NotConnected),
118 ("ERROR_FILE_NOT_FOUND", NotFound),
119 ("ERROR_PATH_NOT_FOUND", NotFound),
120 ("ERROR_INVALID_DRIVE", NotFound),
121 ("ERROR_BAD_NETPATH", NotFound),
122 ("ERROR_BAD_NET_NAME", NotFound),
123 ("ERROR_SEEK_ON_DEVICE", NotSeekable),
124 ("ERROR_NOT_ENOUGH_MEMORY", OutOfMemory),
125 ("ERROR_OUTOFMEMORY", OutOfMemory),
126 ("ERROR_ACCESS_DENIED", PermissionDenied),
127 ("WSAEACCES", PermissionDenied),
128 ("ERROR_WRITE_PROTECT", ReadOnlyFilesystem),
129 ("ERROR_BUSY", ResourceBusy),
130 ("ERROR_DISK_FULL", StorageFull),
131 ("ERROR_HANDLE_DISK_FULL", StorageFull),
132 ("WAIT_TIMEOUT", TimedOut),
133 ("WSAETIMEDOUT", TimedOut),
134 ("ERROR_DRIVER_CANCEL_TIMEOUT", TimedOut),
135 ("ERROR_OPERATION_ABORTED", TimedOut),
136 ("ERROR_SERVICE_REQUEST_TIMEOUT", TimedOut),
137 ("ERROR_COUNTER_TIMEOUT", TimedOut),
138 ("ERROR_TIMEOUT", TimedOut),
139 ("ERROR_RESOURCE_CALL_TIMED_OUT", TimedOut),
140 ("ERROR_CTX_MODEM_RESPONSE_TIMEOUT", TimedOut),
141 ("ERROR_CTX_CLIENT_QUERY_TIMEOUT", TimedOut),
142 ("FRS_ERR_SYSVOL_POPULATE_TIMEOUT", TimedOut),
143 ("ERROR_DS_TIMELIMIT_EXCEEDED", TimedOut),
144 ("DNS_ERROR_RECORD_TIMED_OUT", TimedOut),
145 ("ERROR_IPSEC_IKE_TIMED_OUT", TimedOut),
146 ("ERROR_RUNLEVEL_SWITCH_TIMEOUT", TimedOut),
147 ("ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT", TimedOut),
148 ("ERROR_TOO_MANY_LINKS", TooManyLinks),
149 ("ERROR_CALL_NOT_IMPLEMENTED", Unsupported),
150 ("WSAEWOULDBLOCK", WouldBlock),
151 ]
152};
153
154impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
155pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
156 fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
159 let this = self.eval_context_mut();
160 if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() {
161 interp_ok(errno_place.clone())
162 } else {
163 let errno_layout = this.machine.layouts.u32;
165 let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?;
166 this.write_scalar(Scalar::from_u32(0), &errno_place)?;
167 this.active_thread_mut().last_error = Some(errno_place.clone());
168 interp_ok(errno_place)
169 }
170 }
171
172 fn set_last_error(&mut self, err: impl Into<IoError>) -> InterpResult<'tcx> {
174 let this = self.eval_context_mut();
175 let errno = match err.into() {
176 HostError(err) => this.io_error_to_errnum(err)?,
177 LibcError(name) => this.eval_libc(name),
178 WindowsError(name) => this.eval_windows("c", name),
179 Raw(val) => val,
180 };
181 let errno_place = this.last_error_place()?;
182 this.write_scalar(errno, &errno_place)
183 }
184
185 fn set_last_error_and_return(
187 &mut self,
188 err: impl Into<IoError>,
189 dest: &MPlaceTy<'tcx>,
190 ) -> InterpResult<'tcx> {
191 let this = self.eval_context_mut();
192 this.set_last_error(err)?;
193 this.write_int(-1, dest)?;
194 interp_ok(())
195 }
196
197 fn set_last_error_and_return_i32(
199 &mut self,
200 err: impl Into<IoError>,
201 ) -> InterpResult<'tcx, Scalar> {
202 let this = self.eval_context_mut();
203 this.set_last_error(err)?;
204 interp_ok(Scalar::from_i32(-1))
205 }
206
207 fn set_last_error_and_return_i64(
209 &mut self,
210 err: impl Into<IoError>,
211 ) -> InterpResult<'tcx, Scalar> {
212 let this = self.eval_context_mut();
213 this.set_last_error(err)?;
214 interp_ok(Scalar::from_i64(-1))
215 }
216
217 fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> {
219 let this = self.eval_context_mut();
220 let errno_place = this.last_error_place()?;
221 this.read_scalar(&errno_place)
222 }
223
224 fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> {
227 let this = self.eval_context_ref();
228 let target = &this.tcx.sess.target;
229 if target.families.iter().any(|f| f == "unix") {
230 for &(name, kind) in UNIX_IO_ERROR_TABLE {
231 if err.kind() == kind {
232 return interp_ok(this.eval_libc(name));
233 }
234 }
235 throw_unsup_format!("unsupported io error: {err}")
236 } else if target.families.iter().any(|f| f == "windows") {
237 for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
238 if err.kind() == kind {
239 return interp_ok(this.eval_windows("c", name));
240 }
241 }
242 throw_unsup_format!("unsupported io error: {err}");
243 } else {
244 throw_unsup_format!(
245 "converting io::Error into errnum is unsupported for OS {}",
246 target.os
247 )
248 }
249 }
250
251 #[expect(clippy::needless_return)]
253 fn try_errnum_to_io_error(
254 &self,
255 errnum: Scalar,
256 ) -> InterpResult<'tcx, Option<std::io::ErrorKind>> {
257 let this = self.eval_context_ref();
258 let target = &this.tcx.sess.target;
259 if target.families.iter().any(|f| f == "unix") {
260 let errnum = errnum.to_i32()?;
261 for &(name, kind) in UNIX_IO_ERROR_TABLE {
262 if errnum == this.eval_libc_i32(name) {
263 return interp_ok(Some(kind));
264 }
265 }
266 return interp_ok(None);
267 } else if target.families.iter().any(|f| f == "windows") {
268 let errnum = errnum.to_u32()?;
269 for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
270 if errnum == this.eval_windows("c", name).to_u32()? {
271 return interp_ok(Some(kind));
272 }
273 }
274 return interp_ok(None);
275 } else {
276 throw_unsup_format!(
277 "converting errnum into io::Error is unsupported for OS {}",
278 target.os
279 )
280 }
281 }
282
283 fn try_unwrap_io_result<T: From<i32>>(
290 &mut self,
291 result: std::io::Result<T>,
292 ) -> InterpResult<'tcx, T> {
293 match result {
294 Ok(ok) => interp_ok(ok),
295 Err(e) => {
296 self.eval_context_mut().set_last_error(e)?;
297 interp_ok((-1).into())
298 }
299 }
300 }
301}