• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::{
6     convert::TryFrom,
7     fs::File,
8     io::{Stderr, Stdin, Stdout},
9     net::UdpSocket,
10     ops::Drop,
11     os::unix::{
12         io::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
13         net::{UnixDatagram, UnixListener, UnixStream},
14     },
15 };
16 
17 use super::{
18     errno_result,
19     net::{UnixSeqpacket, UnlinkUnixSeqpacketListener},
20     Result,
21 };
22 use crate::descriptor::{
23     AsRawDescriptor, Descriptor, FromRawDescriptor, IntoRawDescriptor, SafeDescriptor,
24 };
25 
26 pub type RawDescriptor = RawFd;
27 
28 pub const INVALID_DESCRIPTOR: RawDescriptor = -1;
29 
30 /// Clones `descriptor`, returning a new `RawDescriptor` that refers to the same open file
31 /// description as `descriptor`. The cloned descriptor will have the `FD_CLOEXEC` flag set but will
32 /// not share any other file descriptor flags with `descriptor`.
clone_descriptor(descriptor: &dyn AsRawDescriptor) -> Result<RawDescriptor>33 pub fn clone_descriptor(descriptor: &dyn AsRawDescriptor) -> Result<RawDescriptor> {
34     clone_fd(&descriptor.as_raw_descriptor())
35 }
36 
37 /// Clones `fd`, returning a new file descriptor that refers to the same open file description as
38 /// `fd`. The cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file
39 /// descriptor flags with `fd`.
clone_fd(fd: &dyn AsRawFd) -> Result<RawFd>40 fn clone_fd(fd: &dyn AsRawFd) -> Result<RawFd> {
41     // Safe because this doesn't modify any memory and we check the return value.
42     let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) };
43     if ret < 0 {
44         errno_result()
45     } else {
46         Ok(ret)
47     }
48 }
49 
50 /// Clears CLOEXEC flag on descriptor
clear_descriptor_cloexec<A: AsRawDescriptor>(fd_owner: &A) -> Result<()>51 pub fn clear_descriptor_cloexec<A: AsRawDescriptor>(fd_owner: &A) -> Result<()> {
52     clear_fd_cloexec(&fd_owner.as_raw_descriptor())
53 }
54 
55 /// Clears CLOEXEC flag on fd
clear_fd_cloexec<A: AsRawFd>(fd_owner: &A) -> Result<()>56 fn clear_fd_cloexec<A: AsRawFd>(fd_owner: &A) -> Result<()> {
57     let fd = fd_owner.as_raw_fd();
58     // Safe because fd is read only.
59     let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
60     if flags == -1 {
61         return errno_result();
62     }
63 
64     let masked_flags = flags & !libc::FD_CLOEXEC;
65     // Safe because this has no side effect(s) on the current process.
66     if masked_flags != flags && unsafe { libc::fcntl(fd, libc::F_SETFD, masked_flags) } == -1 {
67         errno_result()
68     } else {
69         Ok(())
70     }
71 }
72 
73 const KCMP_FILE: u32 = 0;
74 
75 impl PartialEq for SafeDescriptor {
eq(&self, other: &Self) -> bool76     fn eq(&self, other: &Self) -> bool {
77         // If RawFd numbers match then we can return early without calling kcmp
78         if self.descriptor == other.descriptor {
79             return true;
80         }
81 
82         // safe because we only use the return value and libc says it's always successful
83         let pid = unsafe { libc::getpid() };
84         // safe because we are passing everything by value and checking the return value
85         let ret = unsafe {
86             libc::syscall(
87                 libc::SYS_kcmp,
88                 pid,
89                 pid,
90                 KCMP_FILE,
91                 self.descriptor,
92                 other.descriptor,
93             )
94         };
95 
96         ret == 0
97     }
98 }
99 
100 impl Drop for SafeDescriptor {
drop(&mut self)101     fn drop(&mut self) {
102         let _ = unsafe { libc::close(self.descriptor) };
103     }
104 }
105 
106 impl AsRawFd for SafeDescriptor {
as_raw_fd(&self) -> RawFd107     fn as_raw_fd(&self) -> RawFd {
108         self.as_raw_descriptor()
109     }
110 }
111 
112 impl TryFrom<&dyn AsRawFd> for SafeDescriptor {
113     type Error = std::io::Error;
114 
try_from(fd: &dyn AsRawFd) -> std::result::Result<Self, Self::Error>115     fn try_from(fd: &dyn AsRawFd) -> std::result::Result<Self, Self::Error> {
116         Ok(SafeDescriptor {
117             descriptor: clone_fd(fd)?,
118         })
119     }
120 }
121 
122 impl SafeDescriptor {
123     /// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
124     /// share the same underlying count within the kernel.
try_clone(&self) -> Result<SafeDescriptor>125     pub fn try_clone(&self) -> Result<SafeDescriptor> {
126         // Safe because this doesn't modify any memory and we check the return value.
127         let descriptor = unsafe { libc::fcntl(self.descriptor, libc::F_DUPFD_CLOEXEC, 0) };
128         if descriptor < 0 {
129             errno_result()
130         } else {
131             Ok(SafeDescriptor { descriptor })
132         }
133     }
134 }
135 
136 impl From<SafeDescriptor> for File {
from(s: SafeDescriptor) -> File137     fn from(s: SafeDescriptor) -> File {
138         // Safe because we own the SafeDescriptor at this point.
139         unsafe { File::from_raw_fd(s.into_raw_descriptor()) }
140     }
141 }
142 
143 impl From<SafeDescriptor> for UnixStream {
from(s: SafeDescriptor) -> Self144     fn from(s: SafeDescriptor) -> Self {
145         // Safe because we own the SafeDescriptor at this point.
146         unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
147     }
148 }
149 
150 impl From<UnixSeqpacket> for SafeDescriptor {
from(s: UnixSeqpacket) -> Self151     fn from(s: UnixSeqpacket) -> Self {
152         // Safe because we own the UnixSeqpacket at this point.
153         unsafe { SafeDescriptor::from_raw_descriptor(s.into_raw_descriptor()) }
154     }
155 }
156 
157 // AsRawFd for interoperability with interfaces that require it. Within crosvm,
158 // always use AsRawDescriptor when possible.
159 impl AsRawFd for Descriptor {
as_raw_fd(&self) -> RawFd160     fn as_raw_fd(&self) -> RawFd {
161         self.0
162     }
163 }
164 
165 macro_rules! AsRawDescriptor {
166     ($name:ident) => {
167         impl AsRawDescriptor for $name {
168             fn as_raw_descriptor(&self) -> RawDescriptor {
169                 self.as_raw_fd()
170             }
171         }
172     };
173 }
174 
175 macro_rules! FromRawDescriptor {
176     ($name:ident) => {
177         impl FromRawDescriptor for $name {
178             unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
179                 $name::from_raw_fd(descriptor)
180             }
181         }
182     };
183 }
184 
185 macro_rules! IntoRawDescriptor {
186     ($name:ident) => {
187         impl IntoRawDescriptor for $name {
188             fn into_raw_descriptor(self) -> RawDescriptor {
189                 self.into_raw_fd()
190             }
191         }
192     };
193 }
194 
195 // Implementations for File. This enables the File-type to use
196 // RawDescriptor, but does not mean File should be used as a generic
197 // descriptor container. That should go to either SafeDescriptor or another more
198 // relevant container type.
199 AsRawDescriptor!(File);
200 AsRawDescriptor!(UnlinkUnixSeqpacketListener);
201 AsRawDescriptor!(UdpSocket);
202 AsRawDescriptor!(UnixDatagram);
203 AsRawDescriptor!(UnixListener);
204 AsRawDescriptor!(UnixStream);
205 FromRawDescriptor!(File);
206 FromRawDescriptor!(UnixStream);
207 FromRawDescriptor!(UnixDatagram);
208 IntoRawDescriptor!(File);
209 IntoRawDescriptor!(UnixDatagram);
210 AsRawDescriptor!(Stdin);
211 AsRawDescriptor!(Stdout);
212 AsRawDescriptor!(Stderr);
213 
214 #[test]
215 #[allow(clippy::eq_op)]
clone_equality()216 fn clone_equality() {
217     let ret = unsafe { libc::eventfd(0, 0) };
218     if ret < 0 {
219         panic!("failed to create eventfd");
220     }
221     let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
222 
223     assert_eq!(descriptor, descriptor);
224 
225     assert_eq!(
226         descriptor,
227         descriptor.try_clone().expect("failed to clone eventfd")
228     );
229 
230     let ret = unsafe { libc::eventfd(0, 0) };
231     if ret < 0 {
232         panic!("failed to create eventfd");
233     }
234     let another = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
235 
236     assert_ne!(descriptor, another);
237 }
238