xref: /aosp_15_r20/bionic/docs/fdsan.md (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1*8d67ca89SAndroid Build Coastguard Worker## fdsan
2*8d67ca89SAndroid Build Coastguard Worker
3*8d67ca89SAndroid Build Coastguard Worker[TOC]
4*8d67ca89SAndroid Build Coastguard Worker
5*8d67ca89SAndroid Build Coastguard Workerfdsan is a file descriptor sanitizer added to Android in API level 29.
6*8d67ca89SAndroid Build Coastguard WorkerIn API level 29, fdsan warns when it finds a bug.
7*8d67ca89SAndroid Build Coastguard WorkerIn API level 30, fdsan aborts when it finds a bug.
8*8d67ca89SAndroid Build Coastguard Worker
9*8d67ca89SAndroid Build Coastguard Worker### Background
10*8d67ca89SAndroid Build Coastguard Worker*What problem is fdsan trying to solve? Why should I care?*
11*8d67ca89SAndroid Build Coastguard Worker
12*8d67ca89SAndroid Build Coastguard Workerfdsan (file descriptor sanitizer) detects mishandling of file descriptor ownership, which tend to manifest as *use-after-close* and *double-close*. These errors are direct analogues of the memory allocation *use-after-free* and *double-free* bugs, but tend to be much more difficult to diagnose and fix. With `malloc` and `free`, implementations have free reign to detect errors and abort on double free. File descriptors, on the other hand, are mandated by the POSIX standard to be allocated with the lowest available number being returned for new allocations. As a result, many file descriptor bugs can *never* be noticed on the thread on which the error occurred, and will manifest as "impossible" behavior on another thread.
13*8d67ca89SAndroid Build Coastguard Worker
14*8d67ca89SAndroid Build Coastguard WorkerFor example, given two threads running the following code:
15*8d67ca89SAndroid Build Coastguard Worker```cpp
16*8d67ca89SAndroid Build Coastguard Workervoid thread_one() {
17*8d67ca89SAndroid Build Coastguard Worker    int fd = open("/dev/null", O_RDONLY);
18*8d67ca89SAndroid Build Coastguard Worker    close(fd);
19*8d67ca89SAndroid Build Coastguard Worker    close(fd);
20*8d67ca89SAndroid Build Coastguard Worker}
21*8d67ca89SAndroid Build Coastguard Worker
22*8d67ca89SAndroid Build Coastguard Workervoid thread_two() {
23*8d67ca89SAndroid Build Coastguard Worker    while (true) {
24*8d67ca89SAndroid Build Coastguard Worker        int fd = open("log", O_WRONLY | O_APPEND);
25*8d67ca89SAndroid Build Coastguard Worker        if (write(fd, "foo", 3) != 3) {
26*8d67ca89SAndroid Build Coastguard Worker            err(1, "write failed!");
27*8d67ca89SAndroid Build Coastguard Worker        }
28*8d67ca89SAndroid Build Coastguard Worker    }
29*8d67ca89SAndroid Build Coastguard Worker}
30*8d67ca89SAndroid Build Coastguard Worker```
31*8d67ca89SAndroid Build Coastguard Workerthe following interleaving is possible:
32*8d67ca89SAndroid Build Coastguard Worker```cpp
33*8d67ca89SAndroid Build Coastguard Workerthread one                                thread two
34*8d67ca89SAndroid Build Coastguard Workeropen("/dev/null", O_RDONLY) = 123
35*8d67ca89SAndroid Build Coastguard Workerclose(123) = 0
36*8d67ca89SAndroid Build Coastguard Worker                                          open("log", O_WRONLY | APPEND) = 123
37*8d67ca89SAndroid Build Coastguard Workerclose(123) = 0
38*8d67ca89SAndroid Build Coastguard Worker                                          write(123, "foo", 3) = -1 (EBADF)
39*8d67ca89SAndroid Build Coastguard Worker                                          err(1, "write failed!")
40*8d67ca89SAndroid Build Coastguard Worker```
41*8d67ca89SAndroid Build Coastguard Worker
42*8d67ca89SAndroid Build Coastguard WorkerAssertion failures are probably the most innocuous result that can arise from these bugs: silent data corruption [[1](#footnotes), [2](#footnotes)] or security vulnerabilities are also possible (e.g. suppose thread two was saving user data to disk when a third thread came in and opened a socket to the Internet).
43*8d67ca89SAndroid Build Coastguard Worker
44*8d67ca89SAndroid Build Coastguard Worker### Design
45*8d67ca89SAndroid Build Coastguard Worker*What does fdsan do?*
46*8d67ca89SAndroid Build Coastguard Worker
47*8d67ca89SAndroid Build Coastguard Workerfdsan attempts to detect and/or prevent file descriptor mismanagement by enforcing file descriptor ownership. Like how most memory allocations can have their ownership handled by types such as `std::unique_ptr`, almost all file descriptors can be associated with a unique owner which is responsible for their closure. fdsan provides functions to associate a file descriptor with an owner; if someone tries to close a file descriptor that they don't own, depending on configuration, either a warning is emitted, or the process aborts.
48*8d67ca89SAndroid Build Coastguard Worker
49*8d67ca89SAndroid Build Coastguard WorkerThe way this is implemented is by providing functions to set a 64-bit closure tag on a file descriptor. The tag consists of an 8-bit type byte that identifies the type of the owner (`enum android_fdan_owner_type` in [`<android/fdsan.h>`](https://android.googlesource.com/platform/bionic/+/main/libc/include/android/fdsan.h)), and a 56-bit value. The value should ideally be something that uniquely identifies the object (object address for native objects and `System.identityHashCode` for Java objects), but in cases where it's hard to derive an identifier for the "owner" that should close a file descriptor, even using the same value for all file descriptors in the module can be useful, since it'll catch other code that closes your file descriptors.
50*8d67ca89SAndroid Build Coastguard Worker
51*8d67ca89SAndroid Build Coastguard WorkerIf a file descriptor that's been marked with a tag is closed with an incorrect tag, or without a tag, we know something has gone wrong, and can generate diagnostics or abort.
52*8d67ca89SAndroid Build Coastguard Worker
53*8d67ca89SAndroid Build Coastguard Worker### Enabling fdsan (as a user)
54*8d67ca89SAndroid Build Coastguard Worker*How do I use fdsan?*
55*8d67ca89SAndroid Build Coastguard Worker
56*8d67ca89SAndroid Build Coastguard Workerfdsan has four severity levels:
57*8d67ca89SAndroid Build Coastguard Worker - disabled (`ANDROID_FDSAN_ERROR_LEVEL_DISABLED`)
58*8d67ca89SAndroid Build Coastguard Worker - warn-once (`ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE`)
59*8d67ca89SAndroid Build Coastguard Worker   - Upon detecting an error, emit a warning to logcat, generate a tombstone, and then continue execution with fdsan disabled.
60*8d67ca89SAndroid Build Coastguard Worker - warn-always (`ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS`)
61*8d67ca89SAndroid Build Coastguard Worker   - Same as warn-once, except without disabling after the first warning.
62*8d67ca89SAndroid Build Coastguard Worker - fatal (`ANDROID_FDSAN_ERROR_LEVEL_FATAL`)
63*8d67ca89SAndroid Build Coastguard Worker   - Abort upon detecting an error.
64*8d67ca89SAndroid Build Coastguard Worker
65*8d67ca89SAndroid Build Coastguard WorkerIn API level 29, fdsan had a global default of warn-once.
66*8d67ca89SAndroid Build Coastguard WorkerIn API level 30 and higher, fdsan has a global default of fatal.
67*8d67ca89SAndroid Build Coastguard Workerfdsan can be made more or less strict at runtime via the `android_fdsan_set_error_level` function in [`<android/fdsan.h>`](https://android.googlesource.com/platform/bionic/+/main/libc/include/android/fdsan.h).
68*8d67ca89SAndroid Build Coastguard Worker
69*8d67ca89SAndroid Build Coastguard WorkerThe likelihood of fdsan catching a file descriptor error is proportional to the percentage of file descriptors in your process that are tagged with an owner.
70*8d67ca89SAndroid Build Coastguard Worker
71*8d67ca89SAndroid Build Coastguard Worker### Using fdsan to fix a bug
72*8d67ca89SAndroid Build Coastguard Worker*No, really, how do I use fdsan?*
73*8d67ca89SAndroid Build Coastguard Worker
74*8d67ca89SAndroid Build Coastguard WorkerLet's look at a simple contrived example that uses sleeps to force a particular interleaving of thread execution.
75*8d67ca89SAndroid Build Coastguard Worker
76*8d67ca89SAndroid Build Coastguard Worker```cpp
77*8d67ca89SAndroid Build Coastguard Worker#include <err.h>
78*8d67ca89SAndroid Build Coastguard Worker#include <unistd.h>
79*8d67ca89SAndroid Build Coastguard Worker
80*8d67ca89SAndroid Build Coastguard Worker#include <chrono>
81*8d67ca89SAndroid Build Coastguard Worker#include <thread>
82*8d67ca89SAndroid Build Coastguard Worker#include <vector>
83*8d67ca89SAndroid Build Coastguard Worker
84*8d67ca89SAndroid Build Coastguard Worker#include <android-base/unique_fd.h>
85*8d67ca89SAndroid Build Coastguard Worker
86*8d67ca89SAndroid Build Coastguard Workerusing namespace std::chrono_literals;
87*8d67ca89SAndroid Build Coastguard Workerusing std::this_thread::sleep_for;
88*8d67ca89SAndroid Build Coastguard Worker
89*8d67ca89SAndroid Build Coastguard Workervoid victim() {
90*8d67ca89SAndroid Build Coastguard Worker  sleep_for(300ms);
91*8d67ca89SAndroid Build Coastguard Worker  int fd = dup(STDOUT_FILENO);
92*8d67ca89SAndroid Build Coastguard Worker  sleep_for(200ms);
93*8d67ca89SAndroid Build Coastguard Worker  ssize_t rc = write(fd, "good\n", 5);
94*8d67ca89SAndroid Build Coastguard Worker  if (rc == -1) {
95*8d67ca89SAndroid Build Coastguard Worker    err(1, "good failed to write?!");
96*8d67ca89SAndroid Build Coastguard Worker  }
97*8d67ca89SAndroid Build Coastguard Worker  close(fd);
98*8d67ca89SAndroid Build Coastguard Worker}
99*8d67ca89SAndroid Build Coastguard Worker
100*8d67ca89SAndroid Build Coastguard Workervoid bystander() {
101*8d67ca89SAndroid Build Coastguard Worker  sleep_for(100ms);
102*8d67ca89SAndroid Build Coastguard Worker  int fd = dup(STDOUT_FILENO);
103*8d67ca89SAndroid Build Coastguard Worker  sleep_for(300ms);
104*8d67ca89SAndroid Build Coastguard Worker  close(fd);
105*8d67ca89SAndroid Build Coastguard Worker}
106*8d67ca89SAndroid Build Coastguard Worker
107*8d67ca89SAndroid Build Coastguard Workervoid offender() {
108*8d67ca89SAndroid Build Coastguard Worker  int fd = dup(STDOUT_FILENO);
109*8d67ca89SAndroid Build Coastguard Worker  close(fd);
110*8d67ca89SAndroid Build Coastguard Worker  sleep_for(200ms);
111*8d67ca89SAndroid Build Coastguard Worker  close(fd);
112*8d67ca89SAndroid Build Coastguard Worker}
113*8d67ca89SAndroid Build Coastguard Worker
114*8d67ca89SAndroid Build Coastguard Workerint main() {
115*8d67ca89SAndroid Build Coastguard Worker  std::vector<std::thread> threads;
116*8d67ca89SAndroid Build Coastguard Worker  for (auto function : { victim, bystander, offender }) {
117*8d67ca89SAndroid Build Coastguard Worker    threads.emplace_back(function);
118*8d67ca89SAndroid Build Coastguard Worker  }
119*8d67ca89SAndroid Build Coastguard Worker  for (auto& thread : threads) {
120*8d67ca89SAndroid Build Coastguard Worker    thread.join();
121*8d67ca89SAndroid Build Coastguard Worker  }
122*8d67ca89SAndroid Build Coastguard Worker}
123*8d67ca89SAndroid Build Coastguard Worker```
124*8d67ca89SAndroid Build Coastguard Worker
125*8d67ca89SAndroid Build Coastguard WorkerWhen running the program, the threads' executions will be interleaved as follows:
126*8d67ca89SAndroid Build Coastguard Worker
127*8d67ca89SAndroid Build Coastguard Worker```cpp
128*8d67ca89SAndroid Build Coastguard Worker// victim                         bystander                       offender
129*8d67ca89SAndroid Build Coastguard Worker                                                                  int fd = dup(1); // 3
130*8d67ca89SAndroid Build Coastguard Worker                                                                  close(3);
131*8d67ca89SAndroid Build Coastguard Worker                                  int fd = dup(1); // 3
132*8d67ca89SAndroid Build Coastguard Worker                                                                  close(3);
133*8d67ca89SAndroid Build Coastguard Workerint fd = dup(1); // 3
134*8d67ca89SAndroid Build Coastguard Worker                                  close(3);
135*8d67ca89SAndroid Build Coastguard Workerwrite(3, "good\n") = ��;
136*8d67ca89SAndroid Build Coastguard Worker```
137*8d67ca89SAndroid Build Coastguard Worker
138*8d67ca89SAndroid Build Coastguard Workerwhich results in the following output:
139*8d67ca89SAndroid Build Coastguard Worker
140*8d67ca89SAndroid Build Coastguard Worker    fdsan_test: good failed to write?!: Bad file descriptor
141*8d67ca89SAndroid Build Coastguard Worker
142*8d67ca89SAndroid Build Coastguard WorkerThis implies that either we're accidentally closing out file descriptor too early, or someone else is helpfully closing it for us. Let's use `android::base::unique_fd` in `victim` to guard the file descriptor with fdsan:
143*8d67ca89SAndroid Build Coastguard Worker
144*8d67ca89SAndroid Build Coastguard Worker```diff
145*8d67ca89SAndroid Build Coastguard Worker--- a/fdsan_test.cpp
146*8d67ca89SAndroid Build Coastguard Worker+++ b/fdsan_test.cpp
147*8d67ca89SAndroid Build Coastguard Worker@@ -12,13 +12,12 @@ using std::this_thread::sleep_for;
148*8d67ca89SAndroid Build Coastguard Worker
149*8d67ca89SAndroid Build Coastguard Worker void victim() {
150*8d67ca89SAndroid Build Coastguard Worker   sleep_for(200ms);
151*8d67ca89SAndroid Build Coastguard Worker-  int fd = dup(STDOUT_FILENO);
152*8d67ca89SAndroid Build Coastguard Worker+  android::base::unique_fd fd(dup(STDOUT_FILENO));
153*8d67ca89SAndroid Build Coastguard Worker   sleep_for(200ms);
154*8d67ca89SAndroid Build Coastguard Worker   ssize_t rc = write(fd, "good\n", 5);
155*8d67ca89SAndroid Build Coastguard Worker   if (rc == -1) {
156*8d67ca89SAndroid Build Coastguard Worker     err(1, "good failed to write?!");
157*8d67ca89SAndroid Build Coastguard Worker   }
158*8d67ca89SAndroid Build Coastguard Worker-  close(fd);
159*8d67ca89SAndroid Build Coastguard Worker }
160*8d67ca89SAndroid Build Coastguard Worker
161*8d67ca89SAndroid Build Coastguard Worker void bystander() {
162*8d67ca89SAndroid Build Coastguard Worker```
163*8d67ca89SAndroid Build Coastguard Worker
164*8d67ca89SAndroid Build Coastguard WorkerNow that we've guarded the file descriptor with fdsan, we should be able to find where the double close is:
165*8d67ca89SAndroid Build Coastguard Worker
166*8d67ca89SAndroid Build Coastguard Worker```
167*8d67ca89SAndroid Build Coastguard Workerpid: 25587, tid: 25589, name: fdsan_test  >>> fdsan_test <<<
168*8d67ca89SAndroid Build Coastguard Workersignal 35 (<debuggerd signal>), code -1 (SI_QUEUE), fault addr --------
169*8d67ca89SAndroid Build Coastguard WorkerAbort message: 'attempted to close file descriptor 3, expected to be unowned, actually owned by unique_fd 0x7bf15dc448'
170*8d67ca89SAndroid Build Coastguard Worker    x0  0000000000000000  x1  00000000000063f5  x2  0000000000000023  x3  0000007bf14de338
171*8d67ca89SAndroid Build Coastguard Worker    x4  0000007bf14de3b8  x5  3463643531666237  x6  3463643531666237  x7  3834346364353166
172*8d67ca89SAndroid Build Coastguard Worker    x8  00000000000000f0  x9  0000000000000000  x10 0000000000000059  x11 0000000000000035
173*8d67ca89SAndroid Build Coastguard Worker    x12 0000007bf1bebcfa  x13 0000007bf14ddf0a  x14 0000007bf14ddf0a  x15 0000000000000000
174*8d67ca89SAndroid Build Coastguard Worker    x16 0000007bf1c33048  x17 0000007bf1ba9990  x18 0000000000000000  x19 00000000000063f3
175*8d67ca89SAndroid Build Coastguard Worker    x20 00000000000063f5  x21 0000007bf14de588  x22 0000007bf1f1b864  x23 0000000000000001
176*8d67ca89SAndroid Build Coastguard Worker    x24 0000007bf14de130  x25 0000007bf13e1000  x26 0000007bf1f1f580  x27 0000005ab43ab8f0
177*8d67ca89SAndroid Build Coastguard Worker    x28 0000000000000000  x29 0000007bf14de400
178*8d67ca89SAndroid Build Coastguard Worker    sp  0000007bf14ddff0  lr  0000007bf1b5fd6c  pc  0000007bf1b5fd90
179*8d67ca89SAndroid Build Coastguard Worker
180*8d67ca89SAndroid Build Coastguard Workerbacktrace:
181*8d67ca89SAndroid Build Coastguard Worker    #00 pc 0000000000008d90  /system/lib64/libc.so (fdsan_error(char const*, ...)+384)
182*8d67ca89SAndroid Build Coastguard Worker    #01 pc 0000000000008ba8  /system/lib64/libc.so (android_fdsan_close_with_tag+632)
183*8d67ca89SAndroid Build Coastguard Worker    #02 pc 00000000000092a0  /system/lib64/libc.so (close+16)
184*8d67ca89SAndroid Build Coastguard Worker    #03 pc 00000000000003e4  /system/bin/fdsan_test (bystander()+84)
185*8d67ca89SAndroid Build Coastguard Worker    #04 pc 0000000000000918  /system/bin/fdsan_test
186*8d67ca89SAndroid Build Coastguard Worker    #05 pc 000000000006689c  /system/lib64/libc.so (__pthread_start(void*)+36)
187*8d67ca89SAndroid Build Coastguard Worker    #06 pc 000000000000712c  /system/lib64/libc.so (__start_thread+68)
188*8d67ca89SAndroid Build Coastguard Worker```
189*8d67ca89SAndroid Build Coastguard Worker
190*8d67ca89SAndroid Build Coastguard Worker...in the obviously correct bystander? What's going on here?
191*8d67ca89SAndroid Build Coastguard Worker
192*8d67ca89SAndroid Build Coastguard WorkerThe reason for this is (hopefully!) not a bug in fdsan, and will commonly be seen when tracking down double-closes in processes that have sparse fdsan coverage. What actually happened is that the culprit closed `bystander`'s file descriptor between its open and close, which resulted in `bystander` being blamed for closing `victim`'s fd. If we store `bystander`'s fd in a `unique_fd` as well, we should get something more useful:
193*8d67ca89SAndroid Build Coastguard Worker```diff
194*8d67ca89SAndroid Build Coastguard Worker--- a/tmp/fdsan_test.cpp
195*8d67ca89SAndroid Build Coastguard Worker+++ b/tmp/fdsan_test.cpp
196*8d67ca89SAndroid Build Coastguard Worker@@ -23,9 +23,8 @@ void victim() {
197*8d67ca89SAndroid Build Coastguard Worker
198*8d67ca89SAndroid Build Coastguard Worker void bystander() {
199*8d67ca89SAndroid Build Coastguard Worker   sleep_for(100ms);
200*8d67ca89SAndroid Build Coastguard Worker-  int fd = dup(STDOUT_FILENO);
201*8d67ca89SAndroid Build Coastguard Worker+  android::base::unique_fd fd(dup(STDOUT_FILENO));
202*8d67ca89SAndroid Build Coastguard Worker   sleep_for(200ms);
203*8d67ca89SAndroid Build Coastguard Worker-  close(fd);
204*8d67ca89SAndroid Build Coastguard Worker }
205*8d67ca89SAndroid Build Coastguard Worker```
206*8d67ca89SAndroid Build Coastguard Workergiving us:
207*8d67ca89SAndroid Build Coastguard Worker```
208*8d67ca89SAndroid Build Coastguard Workerpid: 25779, tid: 25782, name: fdsan_test  >>> fdsan_test <<<
209*8d67ca89SAndroid Build Coastguard Workersignal 35 (<debuggerd signal>), code -1 (SI_QUEUE), fault addr --------
210*8d67ca89SAndroid Build Coastguard WorkerAbort message: 'attempted to close file descriptor 3, expected to be unowned, actually owned by unique_fd 0x6fef9ff448'
211*8d67ca89SAndroid Build Coastguard Worker    x0  0000000000000000  x1  00000000000064b6  x2  0000000000000023  x3  0000006fef901338
212*8d67ca89SAndroid Build Coastguard Worker    x4  0000006fef9013b8  x5  3466663966656636  x6  3466663966656636  x7  3834346666396665
213*8d67ca89SAndroid Build Coastguard Worker    x8  00000000000000f0  x9  0000000000000000  x10 0000000000000059  x11 0000000000000039
214*8d67ca89SAndroid Build Coastguard Worker    x12 0000006ff0055cfa  x13 0000006fef900f0a  x14 0000006fef900f0a  x15 0000000000000000
215*8d67ca89SAndroid Build Coastguard Worker    x16 0000006ff009d048  x17 0000006ff0013990  x18 0000000000000000  x19 00000000000064b3
216*8d67ca89SAndroid Build Coastguard Worker    x20 00000000000064b6  x21 0000006fef901588  x22 0000006ff04ff864  x23 0000000000000001
217*8d67ca89SAndroid Build Coastguard Worker    x24 0000006fef901130  x25 0000006fef804000  x26 0000006ff0503580  x27 0000006368aa18f8
218*8d67ca89SAndroid Build Coastguard Worker    x28 0000000000000000  x29 0000006fef901400
219*8d67ca89SAndroid Build Coastguard Worker    sp  0000006fef900ff0  lr  0000006feffc9d6c  pc  0000006feffc9d90
220*8d67ca89SAndroid Build Coastguard Worker
221*8d67ca89SAndroid Build Coastguard Workerbacktrace:
222*8d67ca89SAndroid Build Coastguard Worker    #00 pc 0000000000008d90  /system/lib64/libc.so (fdsan_error(char const*, ...)+384)
223*8d67ca89SAndroid Build Coastguard Worker    #01 pc 0000000000008ba8  /system/lib64/libc.so (android_fdsan_close_with_tag+632)
224*8d67ca89SAndroid Build Coastguard Worker    #02 pc 00000000000092a0  /system/lib64/libc.so (close+16)
225*8d67ca89SAndroid Build Coastguard Worker    #03 pc 000000000000045c  /system/bin/fdsan_test (offender()+68)
226*8d67ca89SAndroid Build Coastguard Worker    #04 pc 0000000000000920  /system/bin/fdsan_test
227*8d67ca89SAndroid Build Coastguard Worker    #05 pc 000000000006689c  /system/lib64/libc.so (__pthread_start(void*)+36)
228*8d67ca89SAndroid Build Coastguard Worker    #06 pc 000000000000712c  /system/lib64/libc.so (__start_thread+68)
229*8d67ca89SAndroid Build Coastguard Worker```
230*8d67ca89SAndroid Build Coastguard Worker
231*8d67ca89SAndroid Build Coastguard WorkerHooray!
232*8d67ca89SAndroid Build Coastguard Worker
233*8d67ca89SAndroid Build Coastguard WorkerIn a real application, things are probably not going to be as detectable or reproducible as our toy example, which is a good reason to try to maximize the usage of fdsan-enabled types like `unique_fd` and `ParcelFileDescriptor`, to improve the odds that double closes in other code get detected.
234*8d67ca89SAndroid Build Coastguard Worker
235*8d67ca89SAndroid Build Coastguard Worker### Enabling fdsan (as a C++ library implementer)
236*8d67ca89SAndroid Build Coastguard Worker
237*8d67ca89SAndroid Build Coastguard Workerfdsan operates via two main primitives. `android_fdsan_exchange_owner_tag` modifies a file descriptor's close tag, and `android_fdsan_close_with_tag` closes a file descriptor with its tag. In the `<android/fdsan.h>` header, these are marked with `__attribute__((weak))`, so instead of passing down the platform version from JNI, availability of the functions can be queried directly. An example implementation of unique_fd follows:
238*8d67ca89SAndroid Build Coastguard Worker
239*8d67ca89SAndroid Build Coastguard Worker```cpp
240*8d67ca89SAndroid Build Coastguard Worker/*
241*8d67ca89SAndroid Build Coastguard Worker * Copyright (C) 2018 The Android Open Source Project
242*8d67ca89SAndroid Build Coastguard Worker * All rights reserved.
243*8d67ca89SAndroid Build Coastguard Worker *
244*8d67ca89SAndroid Build Coastguard Worker * Redistribution and use in source and binary forms, with or without
245*8d67ca89SAndroid Build Coastguard Worker * modification, are permitted provided that the following conditions
246*8d67ca89SAndroid Build Coastguard Worker * are met:
247*8d67ca89SAndroid Build Coastguard Worker *  * Redistributions of source code must retain the above copyright
248*8d67ca89SAndroid Build Coastguard Worker *    notice, this list of conditions and the following disclaimer.
249*8d67ca89SAndroid Build Coastguard Worker *  * Redistributions in binary form must reproduce the above copyright
250*8d67ca89SAndroid Build Coastguard Worker *    notice, this list of conditions and the following disclaimer in
251*8d67ca89SAndroid Build Coastguard Worker *    the documentation and/or other materials provided with the
252*8d67ca89SAndroid Build Coastguard Worker *    distribution.
253*8d67ca89SAndroid Build Coastguard Worker *
254*8d67ca89SAndroid Build Coastguard Worker * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
255*8d67ca89SAndroid Build Coastguard Worker * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
256*8d67ca89SAndroid Build Coastguard Worker * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
257*8d67ca89SAndroid Build Coastguard Worker * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
258*8d67ca89SAndroid Build Coastguard Worker * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
259*8d67ca89SAndroid Build Coastguard Worker * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
260*8d67ca89SAndroid Build Coastguard Worker * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
261*8d67ca89SAndroid Build Coastguard Worker * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
262*8d67ca89SAndroid Build Coastguard Worker * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
263*8d67ca89SAndroid Build Coastguard Worker * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
264*8d67ca89SAndroid Build Coastguard Worker * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
265*8d67ca89SAndroid Build Coastguard Worker * SUCH DAMAGE.
266*8d67ca89SAndroid Build Coastguard Worker */
267*8d67ca89SAndroid Build Coastguard Worker
268*8d67ca89SAndroid Build Coastguard Worker#pragma once
269*8d67ca89SAndroid Build Coastguard Worker
270*8d67ca89SAndroid Build Coastguard Worker#include <android/fdsan.h>
271*8d67ca89SAndroid Build Coastguard Worker#include <unistd.h>
272*8d67ca89SAndroid Build Coastguard Worker
273*8d67ca89SAndroid Build Coastguard Worker#include <utility>
274*8d67ca89SAndroid Build Coastguard Worker
275*8d67ca89SAndroid Build Coastguard Workerstruct unique_fd {
276*8d67ca89SAndroid Build Coastguard Worker    unique_fd() = default;
277*8d67ca89SAndroid Build Coastguard Worker
278*8d67ca89SAndroid Build Coastguard Worker    explicit unique_fd(int fd) {
279*8d67ca89SAndroid Build Coastguard Worker        reset(fd);
280*8d67ca89SAndroid Build Coastguard Worker    }
281*8d67ca89SAndroid Build Coastguard Worker
282*8d67ca89SAndroid Build Coastguard Worker    unique_fd(const unique_fd& copy) = delete;
283*8d67ca89SAndroid Build Coastguard Worker    unique_fd(unique_fd&& move) {
284*8d67ca89SAndroid Build Coastguard Worker        *this = std::move(move);
285*8d67ca89SAndroid Build Coastguard Worker    }
286*8d67ca89SAndroid Build Coastguard Worker
287*8d67ca89SAndroid Build Coastguard Worker    ~unique_fd() {
288*8d67ca89SAndroid Build Coastguard Worker        reset();
289*8d67ca89SAndroid Build Coastguard Worker    }
290*8d67ca89SAndroid Build Coastguard Worker
291*8d67ca89SAndroid Build Coastguard Worker    unique_fd& operator=(const unique_fd& copy) = delete;
292*8d67ca89SAndroid Build Coastguard Worker    unique_fd& operator=(unique_fd&& move) {
293*8d67ca89SAndroid Build Coastguard Worker        if (this == &move) {
294*8d67ca89SAndroid Build Coastguard Worker            return *this;
295*8d67ca89SAndroid Build Coastguard Worker        }
296*8d67ca89SAndroid Build Coastguard Worker
297*8d67ca89SAndroid Build Coastguard Worker        reset();
298*8d67ca89SAndroid Build Coastguard Worker
299*8d67ca89SAndroid Build Coastguard Worker        if (move.fd_ != -1) {
300*8d67ca89SAndroid Build Coastguard Worker            fd_ = move.fd_;
301*8d67ca89SAndroid Build Coastguard Worker            move.fd_ = -1;
302*8d67ca89SAndroid Build Coastguard Worker
303*8d67ca89SAndroid Build Coastguard Worker            // Acquire ownership from the moved-from object.
304*8d67ca89SAndroid Build Coastguard Worker            exchange_tag(fd_, move.tag(), tag());
305*8d67ca89SAndroid Build Coastguard Worker        }
306*8d67ca89SAndroid Build Coastguard Worker
307*8d67ca89SAndroid Build Coastguard Worker        return *this;
308*8d67ca89SAndroid Build Coastguard Worker    }
309*8d67ca89SAndroid Build Coastguard Worker
310*8d67ca89SAndroid Build Coastguard Worker    int get() { return fd_; }
311*8d67ca89SAndroid Build Coastguard Worker
312*8d67ca89SAndroid Build Coastguard Worker    int release() {
313*8d67ca89SAndroid Build Coastguard Worker        if (fd_ == -1) {
314*8d67ca89SAndroid Build Coastguard Worker            return -1;
315*8d67ca89SAndroid Build Coastguard Worker        }
316*8d67ca89SAndroid Build Coastguard Worker
317*8d67ca89SAndroid Build Coastguard Worker        int fd = fd_;
318*8d67ca89SAndroid Build Coastguard Worker        fd_ = -1;
319*8d67ca89SAndroid Build Coastguard Worker
320*8d67ca89SAndroid Build Coastguard Worker        // Release ownership.
321*8d67ca89SAndroid Build Coastguard Worker        exchange_tag(fd, tag(), 0);
322*8d67ca89SAndroid Build Coastguard Worker        return fd;
323*8d67ca89SAndroid Build Coastguard Worker    }
324*8d67ca89SAndroid Build Coastguard Worker
325*8d67ca89SAndroid Build Coastguard Worker    void reset(int new_fd = -1) {
326*8d67ca89SAndroid Build Coastguard Worker        if (fd_ != -1) {
327*8d67ca89SAndroid Build Coastguard Worker            close(fd_, tag());
328*8d67ca89SAndroid Build Coastguard Worker            fd_ = -1;
329*8d67ca89SAndroid Build Coastguard Worker        }
330*8d67ca89SAndroid Build Coastguard Worker
331*8d67ca89SAndroid Build Coastguard Worker        if (new_fd != -1) {
332*8d67ca89SAndroid Build Coastguard Worker            fd_ = new_fd;
333*8d67ca89SAndroid Build Coastguard Worker
334*8d67ca89SAndroid Build Coastguard Worker            // Acquire ownership of the presumably unowned fd.
335*8d67ca89SAndroid Build Coastguard Worker            exchange_tag(fd_, 0, tag());
336*8d67ca89SAndroid Build Coastguard Worker        }
337*8d67ca89SAndroid Build Coastguard Worker    }
338*8d67ca89SAndroid Build Coastguard Worker
339*8d67ca89SAndroid Build Coastguard Worker  private:
340*8d67ca89SAndroid Build Coastguard Worker    int fd_ = -1;
341*8d67ca89SAndroid Build Coastguard Worker
342*8d67ca89SAndroid Build Coastguard Worker    // The obvious choice of tag to use is the address of the object.
343*8d67ca89SAndroid Build Coastguard Worker    uint64_t tag() {
344*8d67ca89SAndroid Build Coastguard Worker        return reinterpret_cast<uint64_t>(this);
345*8d67ca89SAndroid Build Coastguard Worker    }
346*8d67ca89SAndroid Build Coastguard Worker
347*8d67ca89SAndroid Build Coastguard Worker    // These functions are marked with __attribute__((weak)), so that their
348*8d67ca89SAndroid Build Coastguard Worker    // availability can be determined at runtime. These wrappers will use them
349*8d67ca89SAndroid Build Coastguard Worker    // if available, and fall back to no-ops or regular close on devices older
350*8d67ca89SAndroid Build Coastguard Worker    // than API level 29.
351*8d67ca89SAndroid Build Coastguard Worker    static void exchange_tag(int fd, uint64_t old_tag, uint64_t new_tag) {
352*8d67ca89SAndroid Build Coastguard Worker        if (android_fdsan_exchange_owner_tag) {
353*8d67ca89SAndroid Build Coastguard Worker            android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
354*8d67ca89SAndroid Build Coastguard Worker        }
355*8d67ca89SAndroid Build Coastguard Worker    }
356*8d67ca89SAndroid Build Coastguard Worker
357*8d67ca89SAndroid Build Coastguard Worker    static int close(int fd, uint64_t tag) {
358*8d67ca89SAndroid Build Coastguard Worker        if (android_fdsan_close_with_tag) {
359*8d67ca89SAndroid Build Coastguard Worker            return android_fdsan_close_with_tag(fd, tag);
360*8d67ca89SAndroid Build Coastguard Worker        } else {
361*8d67ca89SAndroid Build Coastguard Worker            return ::close(fd);
362*8d67ca89SAndroid Build Coastguard Worker        }
363*8d67ca89SAndroid Build Coastguard Worker    }
364*8d67ca89SAndroid Build Coastguard Worker};
365*8d67ca89SAndroid Build Coastguard Worker```
366*8d67ca89SAndroid Build Coastguard Worker
367*8d67ca89SAndroid Build Coastguard Worker### Frequently seen bugs
368*8d67ca89SAndroid Build Coastguard Worker * Native APIs not making it clear when they take ownership of a file descriptor. <br/>
369*8d67ca89SAndroid Build Coastguard Worker   * Solution: accept `unique_fd` instead of `int` in functions that take ownership.
370*8d67ca89SAndroid Build Coastguard Worker   * [Example one](https://android-review.googlesource.com/c/platform/system/core/+/721985), [two](https://android-review.googlesource.com/c/platform/frameworks/native/+/709451)
371*8d67ca89SAndroid Build Coastguard Worker * Receiving a `ParcelFileDescriptor` via Intent, and then passing it into JNI code that ends up calling close on it. <br/>
372*8d67ca89SAndroid Build Coastguard Worker   * Solution: ¯\\\_(ツ)\_/¯. Use fdsan?
373*8d67ca89SAndroid Build Coastguard Worker   * [Example one](https://android-review.googlesource.com/c/platform/system/bt/+/710104), [two](https://android-review.googlesource.com/c/platform/frameworks/base/+/732305)
374*8d67ca89SAndroid Build Coastguard Worker
375*8d67ca89SAndroid Build Coastguard Worker### Footnotes
376*8d67ca89SAndroid Build Coastguard Worker1. [How To Corrupt An SQLite Database File](https://www.sqlite.org/howtocorrupt.html#_continuing_to_use_a_file_descriptor_after_it_has_been_closed)
377*8d67ca89SAndroid Build Coastguard Worker
378*8d67ca89SAndroid Build Coastguard Worker2. [<b><i>50%</i></b> of Facebook's iOS crashes caused by a file descriptor double close leading to SQLite database corruption](https://code.fb.com/ios/debugging-file-corruption-on-ios/)
379