|
| 1 | +use rustc_middle::ty::ScalarInt; |
| 2 | + |
| 3 | +use crate::*; |
| 4 | +use epoll::{Epoll, EpollEvent}; |
| 5 | +use event::Event; |
| 6 | +use socketpair::SocketPair; |
| 7 | + |
| 8 | +use shims::unix::fs::EvalContextExt as _; |
| 9 | + |
| 10 | +pub mod epoll; |
| 11 | +pub mod event; |
| 12 | +pub mod socketpair; |
| 13 | + |
| 14 | +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} |
| 15 | +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { |
| 16 | + /// This function returns a file descriptor referring to the new `Epoll` instance. This file |
| 17 | + /// descriptor is used for all subsequent calls to the epoll interface. If the `flags` argument |
| 18 | + /// is 0, then this function is the same as `epoll_create()`. |
| 19 | + /// |
| 20 | + /// <https://linux.die.net/man/2/epoll_create1> |
| 21 | + fn epoll_create1( |
| 22 | + &mut self, |
| 23 | + flags: &OpTy<'tcx, Provenance>, |
| 24 | + ) -> InterpResult<'tcx, Scalar<Provenance>> { |
| 25 | + let this = self.eval_context_mut(); |
| 26 | + |
| 27 | + let flags = this.read_scalar(flags)?.to_i32()?; |
| 28 | + |
| 29 | + let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC"); |
| 30 | + if flags == epoll_cloexec { |
| 31 | + // Miri does not support exec, so this flag has no effect. |
| 32 | + } else if flags != 0 { |
| 33 | + throw_unsup_format!("epoll_create1 flags {flags} are not implemented"); |
| 34 | + } |
| 35 | + |
| 36 | + let fd = this.machine.file_handler.insert_fd(Box::new(Epoll::default())); |
| 37 | + Ok(Scalar::from_i32(fd)) |
| 38 | + } |
| 39 | + |
| 40 | + /// This function performs control operations on the `Epoll` instance referred to by the file |
| 41 | + /// descriptor `epfd`. It requests that the operation `op` be performed for the target file |
| 42 | + /// descriptor, `fd`. |
| 43 | + /// |
| 44 | + /// Valid values for the op argument are: |
| 45 | + /// `EPOLL_CTL_ADD` - Register the target file descriptor `fd` on the `Epoll` instance referred |
| 46 | + /// to by the file descriptor `epfd` and associate the event `event` with the internal file |
| 47 | + /// linked to `fd`. |
| 48 | + /// `EPOLL_CTL_MOD` - Change the event `event` associated with the target file descriptor `fd`. |
| 49 | + /// `EPOLL_CTL_DEL` - Deregister the target file descriptor `fd` from the `Epoll` instance |
| 50 | + /// referred to by `epfd`. The `event` is ignored and can be null. |
| 51 | + /// |
| 52 | + /// <https://linux.die.net/man/2/epoll_ctl> |
| 53 | + fn epoll_ctl( |
| 54 | + &mut self, |
| 55 | + epfd: &OpTy<'tcx, Provenance>, |
| 56 | + op: &OpTy<'tcx, Provenance>, |
| 57 | + fd: &OpTy<'tcx, Provenance>, |
| 58 | + event: &OpTy<'tcx, Provenance>, |
| 59 | + ) -> InterpResult<'tcx, Scalar<Provenance>> { |
| 60 | + let this = self.eval_context_mut(); |
| 61 | + |
| 62 | + let epfd = this.read_scalar(epfd)?.to_i32()?; |
| 63 | + let op = this.read_scalar(op)?.to_i32()?; |
| 64 | + let fd = this.read_scalar(fd)?.to_i32()?; |
| 65 | + let _event = this.read_scalar(event)?.to_pointer(this)?; |
| 66 | + |
| 67 | + let epoll_ctl_add = this.eval_libc_i32("EPOLL_CTL_ADD"); |
| 68 | + let epoll_ctl_mod = this.eval_libc_i32("EPOLL_CTL_MOD"); |
| 69 | + let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL"); |
| 70 | + |
| 71 | + if op == epoll_ctl_add || op == epoll_ctl_mod { |
| 72 | + let event = this.deref_operand(event)?; |
| 73 | + |
| 74 | + let events = this.mplace_field(&event, 0)?; |
| 75 | + let events = this.read_scalar(&events.into())?.to_u32()?; |
| 76 | + let data = this.mplace_field(&event, 1)?; |
| 77 | + let data = this.read_scalar(&data.into())?; |
| 78 | + let event = EpollEvent { events, data }; |
| 79 | + |
| 80 | + if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) { |
| 81 | + let epfd = epfd.as_epoll_handle()?; |
| 82 | + |
| 83 | + epfd.file_descriptors.insert(fd, event); |
| 84 | + Ok(Scalar::from_i32(0)) |
| 85 | + } else { |
| 86 | + Ok(Scalar::from_i32(this.handle_not_found()?)) |
| 87 | + } |
| 88 | + } else if op == epoll_ctl_del { |
| 89 | + if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) { |
| 90 | + let epfd = epfd.as_epoll_handle()?; |
| 91 | + |
| 92 | + epfd.file_descriptors.remove(&fd); |
| 93 | + Ok(Scalar::from_i32(0)) |
| 94 | + } else { |
| 95 | + Ok(Scalar::from_i32(this.handle_not_found()?)) |
| 96 | + } |
| 97 | + } else { |
| 98 | + let einval = this.eval_libc("EINVAL"); |
| 99 | + this.set_last_error(einval)?; |
| 100 | + Ok(Scalar::from_i32(-1)) |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + /// This function creates an `Event` that is used as an event wait/notify mechanism by |
| 105 | + /// user-space applications, and by the kernel to notify user-space applications of events. |
| 106 | + /// The `Event` contains an `u64` counter maintained by the kernel. The counter is initialized |
| 107 | + /// with the value specified in the `initval` argument. |
| 108 | + /// |
| 109 | + /// A new file descriptor referring to the `Event` is returned. The `read`, `write`, `poll`, |
| 110 | + /// `select`, and `close` operations can be performed on the file descriptor. For more |
| 111 | + /// information on these operations, see the man page linked below. |
| 112 | + /// |
| 113 | + /// The `flags` are not currently implemented for eventfd. |
| 114 | + /// The `flags` may be bitwise ORed to change the behavior of `eventfd`: |
| 115 | + /// `EFD_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. |
| 116 | + /// `EFD_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the new open file description. |
| 117 | + /// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics. |
| 118 | + /// |
| 119 | + /// <https://linux.die.net/man/2/eventfd> |
| 120 | + fn eventfd( |
| 121 | + &mut self, |
| 122 | + val: &OpTy<'tcx, Provenance>, |
| 123 | + flags: &OpTy<'tcx, Provenance>, |
| 124 | + ) -> InterpResult<'tcx, Scalar<Provenance>> { |
| 125 | + let this = self.eval_context_mut(); |
| 126 | + |
| 127 | + let val = this.read_scalar(val)?.to_u32()?; |
| 128 | + let flags = this.read_scalar(flags)?.to_i32()?; |
| 129 | + |
| 130 | + let efd_cloexec = this.eval_libc_i32("EFD_CLOEXEC"); |
| 131 | + let efd_nonblock = this.eval_libc_i32("EFD_NONBLOCK"); |
| 132 | + let efd_semaphore = this.eval_libc_i32("EFD_SEMAPHORE"); |
| 133 | + |
| 134 | + if flags & (efd_cloexec | efd_nonblock | efd_semaphore) == 0 { |
| 135 | + throw_unsup_format!("{flags} is unsupported"); |
| 136 | + } |
| 137 | + // FIXME handle the cloexec and nonblock flags |
| 138 | + if flags & efd_cloexec == efd_cloexec {} |
| 139 | + if flags & efd_nonblock == efd_nonblock {} |
| 140 | + if flags & efd_semaphore == efd_semaphore { |
| 141 | + throw_unsup_format!("EFD_SEMAPHORE is unsupported"); |
| 142 | + } |
| 143 | + |
| 144 | + let fh = &mut this.machine.file_handler; |
| 145 | + let fd = fh.insert_fd(Box::new(Event { val })); |
| 146 | + Ok(Scalar::from_i32(fd)) |
| 147 | + } |
| 148 | + |
| 149 | + /// Currently this function creates new `SocketPair`s without specifying the domain, type, or |
| 150 | + /// protocol of the new socket and these are stored in the socket values `sv` argument. |
| 151 | + /// |
| 152 | + /// This function creates an unnamed pair of connected sockets in the specified domain, of the |
| 153 | + /// specified type, and using the optionally specified protocol. |
| 154 | + /// |
| 155 | + /// The `domain` argument specified a communication domain; this selects the protocol family |
| 156 | + /// used for communication. The socket `type` specifies the communication semantics. |
| 157 | + /// The `protocol` specifies a particular protocol to use with the socket. Normally there's |
| 158 | + /// only a single protocol supported for a particular socket type within a given protocol |
| 159 | + /// family, in which case `protocol` can be specified as 0. It is possible that many protocols |
| 160 | + /// exist and in that case, a particular protocol must be specified. |
| 161 | + /// |
| 162 | + /// For more information on the arguments see the socket manpage: |
| 163 | + /// <https://linux.die.net/man/2/socket> |
| 164 | + /// |
| 165 | + /// <https://linux.die.net/man/2/socketpair> |
| 166 | + fn socketpair( |
| 167 | + &mut self, |
| 168 | + domain: &OpTy<'tcx, Provenance>, |
| 169 | + type_: &OpTy<'tcx, Provenance>, |
| 170 | + protocol: &OpTy<'tcx, Provenance>, |
| 171 | + sv: &OpTy<'tcx, Provenance>, |
| 172 | + ) -> InterpResult<'tcx, Scalar<Provenance>> { |
| 173 | + let this = self.eval_context_mut(); |
| 174 | + |
| 175 | + let _domain = this.read_scalar(domain)?.to_i32()?; |
| 176 | + let _type_ = this.read_scalar(type_)?.to_i32()?; |
| 177 | + let _protocol = this.read_scalar(protocol)?.to_i32()?; |
| 178 | + let sv = this.deref_operand(sv)?; |
| 179 | + |
| 180 | + let fh = &mut this.machine.file_handler; |
| 181 | + let sv0 = fh.insert_fd(Box::new(SocketPair)); |
| 182 | + let sv0 = ScalarInt::try_from_int(sv0, sv.layout.size).unwrap(); |
| 183 | + let sv1 = fh.insert_fd(Box::new(SocketPair)); |
| 184 | + let sv1 = ScalarInt::try_from_int(sv1, sv.layout.size).unwrap(); |
| 185 | + |
| 186 | + this.write_scalar(sv0, &sv.into())?; |
| 187 | + this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?.into())?; |
| 188 | + |
| 189 | + Ok(Scalar::from_i32(0)) |
| 190 | + } |
| 191 | +} |
0 commit comments