1*e51878c1SAndroid Build Coastguard Worker // Copyright 2024, The Android Open Source Project
2*e51878c1SAndroid Build Coastguard Worker //
3*e51878c1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*e51878c1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*e51878c1SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*e51878c1SAndroid Build Coastguard Worker //
7*e51878c1SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*e51878c1SAndroid Build Coastguard Worker //
9*e51878c1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*e51878c1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*e51878c1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e51878c1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*e51878c1SAndroid Build Coastguard Worker // limitations under the License.
14*e51878c1SAndroid Build Coastguard Worker
15*e51878c1SAndroid Build Coastguard Worker //! Library for safely obtaining `OwnedFd` for inherited file descriptors.
16*e51878c1SAndroid Build Coastguard Worker
17*e51878c1SAndroid Build Coastguard Worker use nix::fcntl::{fcntl, FdFlag, F_SETFD};
18*e51878c1SAndroid Build Coastguard Worker use nix::libc;
19*e51878c1SAndroid Build Coastguard Worker use std::collections::HashMap;
20*e51878c1SAndroid Build Coastguard Worker use std::fs::canonicalize;
21*e51878c1SAndroid Build Coastguard Worker use std::fs::read_dir;
22*e51878c1SAndroid Build Coastguard Worker use std::os::fd::FromRawFd;
23*e51878c1SAndroid Build Coastguard Worker use std::os::fd::OwnedFd;
24*e51878c1SAndroid Build Coastguard Worker use std::os::fd::RawFd;
25*e51878c1SAndroid Build Coastguard Worker use std::sync::Mutex;
26*e51878c1SAndroid Build Coastguard Worker use std::sync::OnceLock;
27*e51878c1SAndroid Build Coastguard Worker use thiserror::Error;
28*e51878c1SAndroid Build Coastguard Worker
29*e51878c1SAndroid Build Coastguard Worker /// Errors that can occur while taking an ownership of `RawFd`
30*e51878c1SAndroid Build Coastguard Worker #[derive(Debug, PartialEq, Error)]
31*e51878c1SAndroid Build Coastguard Worker pub enum Error {
32*e51878c1SAndroid Build Coastguard Worker /// init_once() not called
33*e51878c1SAndroid Build Coastguard Worker #[error("init_once() not called")]
34*e51878c1SAndroid Build Coastguard Worker NotInitialized,
35*e51878c1SAndroid Build Coastguard Worker
36*e51878c1SAndroid Build Coastguard Worker /// Ownership already taken
37*e51878c1SAndroid Build Coastguard Worker #[error("Ownership of FD {0} is already taken")]
38*e51878c1SAndroid Build Coastguard Worker OwnershipTaken(RawFd),
39*e51878c1SAndroid Build Coastguard Worker
40*e51878c1SAndroid Build Coastguard Worker /// Not an inherited file descriptor
41*e51878c1SAndroid Build Coastguard Worker #[error("FD {0} is either invalid file descriptor or not an inherited one")]
42*e51878c1SAndroid Build Coastguard Worker FileDescriptorNotInherited(RawFd),
43*e51878c1SAndroid Build Coastguard Worker
44*e51878c1SAndroid Build Coastguard Worker /// Failed to set CLOEXEC
45*e51878c1SAndroid Build Coastguard Worker #[error("Failed to set CLOEXEC on FD {0}")]
46*e51878c1SAndroid Build Coastguard Worker FailCloseOnExec(RawFd),
47*e51878c1SAndroid Build Coastguard Worker }
48*e51878c1SAndroid Build Coastguard Worker
49*e51878c1SAndroid Build Coastguard Worker static INHERITED_FDS: OnceLock<Mutex<HashMap<RawFd, Option<OwnedFd>>>> = OnceLock::new();
50*e51878c1SAndroid Build Coastguard Worker
51*e51878c1SAndroid Build Coastguard Worker /// Take ownership of all open file descriptors in this process, which later can be obtained by
52*e51878c1SAndroid Build Coastguard Worker /// calling `take_fd_ownership`.
53*e51878c1SAndroid Build Coastguard Worker ///
54*e51878c1SAndroid Build Coastguard Worker /// # Safety
55*e51878c1SAndroid Build Coastguard Worker /// This function has to be called very early in the program before the ownership of any file
56*e51878c1SAndroid Build Coastguard Worker /// descriptors (except stdin/out/err) is taken.
init_once() -> Result<(), std::io::Error>57*e51878c1SAndroid Build Coastguard Worker pub unsafe fn init_once() -> Result<(), std::io::Error> {
58*e51878c1SAndroid Build Coastguard Worker let mut fds = HashMap::new();
59*e51878c1SAndroid Build Coastguard Worker
60*e51878c1SAndroid Build Coastguard Worker let fd_path = canonicalize("/proc/self/fd")?;
61*e51878c1SAndroid Build Coastguard Worker
62*e51878c1SAndroid Build Coastguard Worker for entry in read_dir(&fd_path)? {
63*e51878c1SAndroid Build Coastguard Worker let entry = entry?;
64*e51878c1SAndroid Build Coastguard Worker
65*e51878c1SAndroid Build Coastguard Worker // Files in /prod/self/fd are guaranteed to be numbers. So parsing is always successful.
66*e51878c1SAndroid Build Coastguard Worker let file_name = entry.file_name();
67*e51878c1SAndroid Build Coastguard Worker let raw_fd = file_name.to_str().unwrap().parse::<RawFd>().unwrap();
68*e51878c1SAndroid Build Coastguard Worker
69*e51878c1SAndroid Build Coastguard Worker // We don't take ownership of the stdio FDs as the Rust runtime owns them.
70*e51878c1SAndroid Build Coastguard Worker if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
71*e51878c1SAndroid Build Coastguard Worker continue;
72*e51878c1SAndroid Build Coastguard Worker }
73*e51878c1SAndroid Build Coastguard Worker
74*e51878c1SAndroid Build Coastguard Worker // Exceptional case: /proc/self/fd/* may be a dir fd created by read_dir just above. Since
75*e51878c1SAndroid Build Coastguard Worker // the file descriptor is owned by read_dir (and thus closed by it), we shouldn't take
76*e51878c1SAndroid Build Coastguard Worker // ownership to it.
77*e51878c1SAndroid Build Coastguard Worker if entry.path().read_link()? == fd_path {
78*e51878c1SAndroid Build Coastguard Worker continue;
79*e51878c1SAndroid Build Coastguard Worker }
80*e51878c1SAndroid Build Coastguard Worker
81*e51878c1SAndroid Build Coastguard Worker // SAFETY: /proc/self/fd/* are file descriptors that are open. If `init_once()` was called
82*e51878c1SAndroid Build Coastguard Worker // at the very beginning of the program execution (as requested by the safety requirement
83*e51878c1SAndroid Build Coastguard Worker // of this function), this is the first time to claim the ownership of these file
84*e51878c1SAndroid Build Coastguard Worker // descriptors.
85*e51878c1SAndroid Build Coastguard Worker let owned_fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
86*e51878c1SAndroid Build Coastguard Worker fds.insert(raw_fd, Some(owned_fd));
87*e51878c1SAndroid Build Coastguard Worker }
88*e51878c1SAndroid Build Coastguard Worker
89*e51878c1SAndroid Build Coastguard Worker INHERITED_FDS
90*e51878c1SAndroid Build Coastguard Worker .set(Mutex::new(fds))
91*e51878c1SAndroid Build Coastguard Worker .or(Err(std::io::Error::other("Inherited fds were already initialized")))
92*e51878c1SAndroid Build Coastguard Worker }
93*e51878c1SAndroid Build Coastguard Worker
94*e51878c1SAndroid Build Coastguard Worker /// Take the ownership of the given `RawFd` and returns `OwnedFd` for it. The returned FD is set
95*e51878c1SAndroid Build Coastguard Worker /// CLOEXEC. `Error` is returned when the ownership was already taken (by a prior call to this
96*e51878c1SAndroid Build Coastguard Worker /// function with the same `RawFd`) or `RawFd` is not an inherited file descriptor.
take_fd_ownership(raw_fd: RawFd) -> Result<OwnedFd, Error>97*e51878c1SAndroid Build Coastguard Worker pub fn take_fd_ownership(raw_fd: RawFd) -> Result<OwnedFd, Error> {
98*e51878c1SAndroid Build Coastguard Worker let mut fds = INHERITED_FDS.get().ok_or(Error::NotInitialized)?.lock().unwrap();
99*e51878c1SAndroid Build Coastguard Worker
100*e51878c1SAndroid Build Coastguard Worker if let Some(value) = fds.get_mut(&raw_fd) {
101*e51878c1SAndroid Build Coastguard Worker if let Some(owned_fd) = value.take() {
102*e51878c1SAndroid Build Coastguard Worker fcntl(raw_fd, F_SETFD(FdFlag::FD_CLOEXEC)).or(Err(Error::FailCloseOnExec(raw_fd)))?;
103*e51878c1SAndroid Build Coastguard Worker Ok(owned_fd)
104*e51878c1SAndroid Build Coastguard Worker } else {
105*e51878c1SAndroid Build Coastguard Worker Err(Error::OwnershipTaken(raw_fd))
106*e51878c1SAndroid Build Coastguard Worker }
107*e51878c1SAndroid Build Coastguard Worker } else {
108*e51878c1SAndroid Build Coastguard Worker Err(Error::FileDescriptorNotInherited(raw_fd))
109*e51878c1SAndroid Build Coastguard Worker }
110*e51878c1SAndroid Build Coastguard Worker }
111*e51878c1SAndroid Build Coastguard Worker
112*e51878c1SAndroid Build Coastguard Worker #[cfg(test)]
113*e51878c1SAndroid Build Coastguard Worker mod test {
114*e51878c1SAndroid Build Coastguard Worker use super::*;
115*e51878c1SAndroid Build Coastguard Worker use anyhow::Result;
116*e51878c1SAndroid Build Coastguard Worker use nix::fcntl::{fcntl, FdFlag, F_GETFD, F_SETFD};
117*e51878c1SAndroid Build Coastguard Worker use nix::unistd::close;
118*e51878c1SAndroid Build Coastguard Worker use std::os::fd::{AsRawFd, IntoRawFd};
119*e51878c1SAndroid Build Coastguard Worker use tempfile::tempfile;
120*e51878c1SAndroid Build Coastguard Worker
121*e51878c1SAndroid Build Coastguard Worker struct Fixture {
122*e51878c1SAndroid Build Coastguard Worker fds: Vec<RawFd>,
123*e51878c1SAndroid Build Coastguard Worker }
124*e51878c1SAndroid Build Coastguard Worker
125*e51878c1SAndroid Build Coastguard Worker impl Fixture {
setup(num_fds: usize) -> Result<Self>126*e51878c1SAndroid Build Coastguard Worker fn setup(num_fds: usize) -> Result<Self> {
127*e51878c1SAndroid Build Coastguard Worker let mut fds = Vec::new();
128*e51878c1SAndroid Build Coastguard Worker for _ in 0..num_fds {
129*e51878c1SAndroid Build Coastguard Worker fds.push(tempfile()?.into_raw_fd());
130*e51878c1SAndroid Build Coastguard Worker }
131*e51878c1SAndroid Build Coastguard Worker Ok(Fixture { fds })
132*e51878c1SAndroid Build Coastguard Worker }
133*e51878c1SAndroid Build Coastguard Worker
open_new_file(&mut self) -> Result<RawFd>134*e51878c1SAndroid Build Coastguard Worker fn open_new_file(&mut self) -> Result<RawFd> {
135*e51878c1SAndroid Build Coastguard Worker let raw_fd = tempfile()?.into_raw_fd();
136*e51878c1SAndroid Build Coastguard Worker self.fds.push(raw_fd);
137*e51878c1SAndroid Build Coastguard Worker Ok(raw_fd)
138*e51878c1SAndroid Build Coastguard Worker }
139*e51878c1SAndroid Build Coastguard Worker }
140*e51878c1SAndroid Build Coastguard Worker
141*e51878c1SAndroid Build Coastguard Worker impl Drop for Fixture {
drop(&mut self)142*e51878c1SAndroid Build Coastguard Worker fn drop(&mut self) {
143*e51878c1SAndroid Build Coastguard Worker self.fds.iter().for_each(|fd| {
144*e51878c1SAndroid Build Coastguard Worker let _ = close(*fd);
145*e51878c1SAndroid Build Coastguard Worker });
146*e51878c1SAndroid Build Coastguard Worker }
147*e51878c1SAndroid Build Coastguard Worker }
148*e51878c1SAndroid Build Coastguard Worker
is_fd_opened(raw_fd: RawFd) -> bool149*e51878c1SAndroid Build Coastguard Worker fn is_fd_opened(raw_fd: RawFd) -> bool {
150*e51878c1SAndroid Build Coastguard Worker fcntl(raw_fd, F_GETFD).is_ok()
151*e51878c1SAndroid Build Coastguard Worker }
152*e51878c1SAndroid Build Coastguard Worker
153*e51878c1SAndroid Build Coastguard Worker #[test]
happy_case() -> Result<()>154*e51878c1SAndroid Build Coastguard Worker fn happy_case() -> Result<()> {
155*e51878c1SAndroid Build Coastguard Worker let fixture = Fixture::setup(2)?;
156*e51878c1SAndroid Build Coastguard Worker let f0 = fixture.fds[0];
157*e51878c1SAndroid Build Coastguard Worker let f1 = fixture.fds[1];
158*e51878c1SAndroid Build Coastguard Worker
159*e51878c1SAndroid Build Coastguard Worker // SAFETY: assume files opened by Fixture are inherited ones
160*e51878c1SAndroid Build Coastguard Worker unsafe {
161*e51878c1SAndroid Build Coastguard Worker init_once()?;
162*e51878c1SAndroid Build Coastguard Worker }
163*e51878c1SAndroid Build Coastguard Worker
164*e51878c1SAndroid Build Coastguard Worker let f0_owned = take_fd_ownership(f0)?;
165*e51878c1SAndroid Build Coastguard Worker let f1_owned = take_fd_ownership(f1)?;
166*e51878c1SAndroid Build Coastguard Worker assert_eq!(f0, f0_owned.as_raw_fd());
167*e51878c1SAndroid Build Coastguard Worker assert_eq!(f1, f1_owned.as_raw_fd());
168*e51878c1SAndroid Build Coastguard Worker
169*e51878c1SAndroid Build Coastguard Worker drop(f0_owned);
170*e51878c1SAndroid Build Coastguard Worker drop(f1_owned);
171*e51878c1SAndroid Build Coastguard Worker assert!(!is_fd_opened(f0));
172*e51878c1SAndroid Build Coastguard Worker assert!(!is_fd_opened(f1));
173*e51878c1SAndroid Build Coastguard Worker Ok(())
174*e51878c1SAndroid Build Coastguard Worker }
175*e51878c1SAndroid Build Coastguard Worker
176*e51878c1SAndroid Build Coastguard Worker #[test]
access_non_inherited_fd() -> Result<()>177*e51878c1SAndroid Build Coastguard Worker fn access_non_inherited_fd() -> Result<()> {
178*e51878c1SAndroid Build Coastguard Worker let mut fixture = Fixture::setup(2)?;
179*e51878c1SAndroid Build Coastguard Worker
180*e51878c1SAndroid Build Coastguard Worker // SAFETY: assume files opened by Fixture are inherited ones
181*e51878c1SAndroid Build Coastguard Worker unsafe {
182*e51878c1SAndroid Build Coastguard Worker init_once()?;
183*e51878c1SAndroid Build Coastguard Worker }
184*e51878c1SAndroid Build Coastguard Worker
185*e51878c1SAndroid Build Coastguard Worker let f = fixture.open_new_file()?;
186*e51878c1SAndroid Build Coastguard Worker assert_eq!(Some(Error::FileDescriptorNotInherited(f)), take_fd_ownership(f).err());
187*e51878c1SAndroid Build Coastguard Worker Ok(())
188*e51878c1SAndroid Build Coastguard Worker }
189*e51878c1SAndroid Build Coastguard Worker
190*e51878c1SAndroid Build Coastguard Worker #[test]
call_init_once_multiple_times() -> Result<()>191*e51878c1SAndroid Build Coastguard Worker fn call_init_once_multiple_times() -> Result<()> {
192*e51878c1SAndroid Build Coastguard Worker let _ = Fixture::setup(2)?;
193*e51878c1SAndroid Build Coastguard Worker
194*e51878c1SAndroid Build Coastguard Worker // SAFETY: assume files opened by Fixture are inherited ones
195*e51878c1SAndroid Build Coastguard Worker unsafe {
196*e51878c1SAndroid Build Coastguard Worker init_once()?;
197*e51878c1SAndroid Build Coastguard Worker }
198*e51878c1SAndroid Build Coastguard Worker
199*e51878c1SAndroid Build Coastguard Worker // SAFETY: for testing
200*e51878c1SAndroid Build Coastguard Worker let res = unsafe { init_once() };
201*e51878c1SAndroid Build Coastguard Worker assert!(res.is_err());
202*e51878c1SAndroid Build Coastguard Worker Ok(())
203*e51878c1SAndroid Build Coastguard Worker }
204*e51878c1SAndroid Build Coastguard Worker
205*e51878c1SAndroid Build Coastguard Worker #[test]
access_without_init_once() -> Result<()>206*e51878c1SAndroid Build Coastguard Worker fn access_without_init_once() -> Result<()> {
207*e51878c1SAndroid Build Coastguard Worker let fixture = Fixture::setup(2)?;
208*e51878c1SAndroid Build Coastguard Worker
209*e51878c1SAndroid Build Coastguard Worker let f = fixture.fds[0];
210*e51878c1SAndroid Build Coastguard Worker assert_eq!(Some(Error::NotInitialized), take_fd_ownership(f).err());
211*e51878c1SAndroid Build Coastguard Worker Ok(())
212*e51878c1SAndroid Build Coastguard Worker }
213*e51878c1SAndroid Build Coastguard Worker
214*e51878c1SAndroid Build Coastguard Worker #[test]
double_ownership() -> Result<()>215*e51878c1SAndroid Build Coastguard Worker fn double_ownership() -> Result<()> {
216*e51878c1SAndroid Build Coastguard Worker let fixture = Fixture::setup(2)?;
217*e51878c1SAndroid Build Coastguard Worker let f = fixture.fds[0];
218*e51878c1SAndroid Build Coastguard Worker
219*e51878c1SAndroid Build Coastguard Worker // SAFETY: assume files opened by Fixture are inherited ones
220*e51878c1SAndroid Build Coastguard Worker unsafe {
221*e51878c1SAndroid Build Coastguard Worker init_once()?;
222*e51878c1SAndroid Build Coastguard Worker }
223*e51878c1SAndroid Build Coastguard Worker
224*e51878c1SAndroid Build Coastguard Worker let f_owned = take_fd_ownership(f)?;
225*e51878c1SAndroid Build Coastguard Worker let f_double_owned = take_fd_ownership(f);
226*e51878c1SAndroid Build Coastguard Worker assert_eq!(Some(Error::OwnershipTaken(f)), f_double_owned.err());
227*e51878c1SAndroid Build Coastguard Worker
228*e51878c1SAndroid Build Coastguard Worker // just to highlight that f_owned is kept alive when the second call to take_fd_ownership
229*e51878c1SAndroid Build Coastguard Worker // is made.
230*e51878c1SAndroid Build Coastguard Worker drop(f_owned);
231*e51878c1SAndroid Build Coastguard Worker Ok(())
232*e51878c1SAndroid Build Coastguard Worker }
233*e51878c1SAndroid Build Coastguard Worker
234*e51878c1SAndroid Build Coastguard Worker #[test]
take_drop_retake() -> Result<()>235*e51878c1SAndroid Build Coastguard Worker fn take_drop_retake() -> Result<()> {
236*e51878c1SAndroid Build Coastguard Worker let fixture = Fixture::setup(2)?;
237*e51878c1SAndroid Build Coastguard Worker let f = fixture.fds[0];
238*e51878c1SAndroid Build Coastguard Worker
239*e51878c1SAndroid Build Coastguard Worker // SAFETY: assume files opened by Fixture are inherited ones
240*e51878c1SAndroid Build Coastguard Worker unsafe {
241*e51878c1SAndroid Build Coastguard Worker init_once()?;
242*e51878c1SAndroid Build Coastguard Worker }
243*e51878c1SAndroid Build Coastguard Worker
244*e51878c1SAndroid Build Coastguard Worker let f_owned = take_fd_ownership(f)?;
245*e51878c1SAndroid Build Coastguard Worker drop(f_owned);
246*e51878c1SAndroid Build Coastguard Worker
247*e51878c1SAndroid Build Coastguard Worker let f_double_owned = take_fd_ownership(f);
248*e51878c1SAndroid Build Coastguard Worker assert_eq!(Some(Error::OwnershipTaken(f)), f_double_owned.err());
249*e51878c1SAndroid Build Coastguard Worker Ok(())
250*e51878c1SAndroid Build Coastguard Worker }
251*e51878c1SAndroid Build Coastguard Worker
252*e51878c1SAndroid Build Coastguard Worker #[test]
cloexec() -> Result<()>253*e51878c1SAndroid Build Coastguard Worker fn cloexec() -> Result<()> {
254*e51878c1SAndroid Build Coastguard Worker let fixture = Fixture::setup(2)?;
255*e51878c1SAndroid Build Coastguard Worker let f = fixture.fds[0];
256*e51878c1SAndroid Build Coastguard Worker
257*e51878c1SAndroid Build Coastguard Worker // SAFETY: assume files opened by Fixture are inherited ones
258*e51878c1SAndroid Build Coastguard Worker unsafe {
259*e51878c1SAndroid Build Coastguard Worker init_once()?;
260*e51878c1SAndroid Build Coastguard Worker }
261*e51878c1SAndroid Build Coastguard Worker
262*e51878c1SAndroid Build Coastguard Worker // Intentionally cleaar cloexec to see if it is set by take_fd_ownership
263*e51878c1SAndroid Build Coastguard Worker fcntl(f, F_SETFD(FdFlag::empty()))?;
264*e51878c1SAndroid Build Coastguard Worker
265*e51878c1SAndroid Build Coastguard Worker let f_owned = take_fd_ownership(f)?;
266*e51878c1SAndroid Build Coastguard Worker let flags = fcntl(f_owned.as_raw_fd(), F_GETFD)?;
267*e51878c1SAndroid Build Coastguard Worker assert_eq!(flags, FdFlag::FD_CLOEXEC.bits());
268*e51878c1SAndroid Build Coastguard Worker Ok(())
269*e51878c1SAndroid Build Coastguard Worker }
270*e51878c1SAndroid Build Coastguard Worker }
271