Skip to content

Commit e9d4540

Browse files
committed
android: add fdsan support to libstd.
Android's libc provides facilities to mark file descriptor ownership, in order to prevent people from closing file descriptors that they don't own. Add support for this to FileDesc, which should cover all of the relevant file descriptor-owning types in Rust.
1 parent ac48e62 commit e9d4540

File tree

1 file changed

+69
-0
lines changed
  • library/std/src/sys/unix

1 file changed

+69
-0
lines changed

library/std/src/sys/unix/fd.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,66 @@ use crate::sys_common::AsInner;
88

99
use libc::{c_int, c_void};
1010

11+
// Android's libc allows for file descriptors to be marked with a tag that marks ownership.
12+
// If a tagged file descriptor is closed with the wrong tag, either a backtrace is printed to logs,
13+
// or the process aborts, depending on process configuration.
14+
#[cfg(target_os = "android")]
15+
mod android {
16+
use crate::sync::atomic::{AtomicU64, Ordering};
17+
use libc::c_int;
18+
19+
#[derive(Debug)]
20+
pub struct CloseTag(u64);
21+
22+
static FD_TAG: AtomicU64 = AtomicU64::new(0);
23+
24+
fn next_tag() -> u64 {
25+
// The most significant 8 bits of the tag are used to identify the type of the owner, as
26+
// a debugging aid. The value 13 has been reserved to identify Rust-owned fds.
27+
let tag = FD_TAG.fetch_add(1, Ordering::Relaxed);
28+
tag | 13 << 56
29+
}
30+
31+
impl CloseTag {
32+
pub fn tag(fd: c_int) -> CloseTag {
33+
weak!(fn android_fdsan_exchange_owner_tag(c_int, u64, u64) -> u64);
34+
match android_fdsan_exchange_owner_tag.get() {
35+
Some(f) => {
36+
let tag = next_tag();
37+
let prev = unsafe { f(fd, 0, tag) };
38+
if prev != 0 {
39+
panic!("attempted to take ownership of an already-owned file descriptor");
40+
}
41+
CloseTag(tag)
42+
}
43+
44+
None => CloseTag(0),
45+
}
46+
}
47+
48+
pub fn close(&mut self, fd: c_int) {
49+
weak!(fn android_fdsan_close_with_tag(c_int, u64) -> c_int);
50+
match android_fdsan_close_with_tag.get() {
51+
Some(f) => unsafe {
52+
f(fd, self.0);
53+
},
54+
55+
None => {
56+
assert_eq!(self.0, 0);
57+
unsafe {
58+
libc::close(fd);
59+
}
60+
}
61+
}
62+
}
63+
}
64+
}
65+
1166
#[derive(Debug)]
1267
pub struct FileDesc {
1368
fd: c_int,
69+
#[cfg(target_os = "android")]
70+
tag: android::CloseTag,
1471
}
1572

1673
// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
@@ -27,6 +84,12 @@ const READ_LIMIT: usize = c_int::MAX as usize - 1;
2784
const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
2885

2986
impl FileDesc {
87+
#[cfg(target_os = "android")]
88+
pub fn new(fd: c_int) -> FileDesc {
89+
FileDesc { fd, tag: android::CloseTag::tag(fd) }
90+
}
91+
92+
#[cfg(not(target_os = "android"))]
3093
pub fn new(fd: c_int) -> FileDesc {
3194
FileDesc { fd }
3295
}
@@ -247,6 +310,12 @@ impl AsInner<c_int> for FileDesc {
247310
}
248311

249312
impl Drop for FileDesc {
313+
#[cfg(target_os = "android")]
314+
fn drop(&mut self) {
315+
self.tag.close(self.fd);
316+
}
317+
318+
#[cfg(not(target_os = "android"))]
250319
fn drop(&mut self) {
251320
// Note that errors are ignored when closing a file descriptor. The
252321
// reason for this is that if an error occurs we don't actually know if

0 commit comments

Comments
 (0)