Add fdsan capabilities for native handles
Introduces new APIs which can be used to simplify application of fdsan
to native_handle_t usage, and applies fdsan protection to
native_handle_clone() by default.
Bug: 244214188
Test: validated alongside sensor service change to use the new APIs
Change-Id: I3be16a09c336bcbe880bdb542d5da2969c2c34d3
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index c8bfb01..fde30ad 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -168,6 +168,9 @@
target: {
linux_bionic: {
enabled: true,
+ static_libs: [
+ "libasync_safe",
+ ],
},
not_windows: {
srcs: libcutils_nonwindows_sources + [
@@ -190,6 +193,9 @@
],
},
android: {
+ static_libs: [
+ "libasync_safe",
+ ],
srcs: libcutils_nonwindows_sources + [
"android_reboot.cpp",
"ashmem-dev.cpp",
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 4f07456..e46e7cd 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -49,18 +49,28 @@
typedef const native_handle_t* buffer_handle_t;
/*
- * native_handle_close
- *
- * closes the file descriptors contained in this native_handle_t
- *
+ * Closes the file descriptors contained in this native_handle_t, which may
+ * either be untagged or tagged for ownership by this native_handle_t via
+ * native_handle_set_tag(). Mixing untagged and tagged fds in the same
+ * native_handle_t is not permitted and triggers an fdsan exception, but
+ * native_handle_set_fdsan_tag() can be used to bring consistency if this is
+ * intentional.
+ *
+ * If it's known that fds are tagged, prefer native_handle_close_with_tag() for
+ * better safety.
+ *
* return 0 on success, or a negative error code on failure
- *
*/
int native_handle_close(const native_handle_t* h);
/*
- * native_handle_init
- *
+ * Equivalent to native_handle_close(), but throws an fdsan exception if the fds
+ * are untagged. Use if it's known that the fds in this native_handle_t were
+ * previously tagged via native_handle_set_tag().
+ */
+int native_handle_close_with_tag(const native_handle_t* h);
+
+/*
* Initializes a native_handle_t from storage. storage must be declared with
* NATIVE_HANDLE_DECLARE_STORAGE. numFds and numInts must not respectively
* exceed maxFds and maxInts used to declare the storage.
@@ -68,33 +78,42 @@
native_handle_t* native_handle_init(char* storage, int numFds, int numInts);
/*
- * native_handle_create
- *
- * creates a native_handle_t and initializes it. must be destroyed with
+ * Creates a native_handle_t and initializes it. Must be destroyed with
* native_handle_delete(). Note that numFds must be <= NATIVE_HANDLE_MAX_FDS,
* numInts must be <= NATIVE_HANDLE_MAX_INTS, and both must be >= 0.
- *
*/
native_handle_t* native_handle_create(int numFds, int numInts);
/*
- * native_handle_clone
- *
- * creates a native_handle_t and initializes it from another native_handle_t.
+ * Updates the fdsan tag for any file descriptors contained in the supplied
+ * handle to indicate that they are owned by this handle and should only be
+ * closed via native_handle_close()/native_handle_close_with_tag(). Each fd in
+ * the handle must have a tag of either 0 (unset) or the tag associated with
+ * this handle, otherwise an fdsan exception will be triggered.
+ */
+void native_handle_set_fdsan_tag(const native_handle_t* handle);
+
+/*
+ * Clears the fdsan tag for any file descriptors contained in the supplied
+ * native_handle_t. Use if this native_handle_t is giving up ownership of its
+ * fds, but the fdsan tags were previously set. Each fd in the handle must have
+ * a tag of either 0 (unset) or the tag associated with this handle, otherwise
+ * an fdsan exception will be triggered.
+ */
+void native_handle_unset_fdsan_tag(const native_handle_t* handle);
+
+/*
+ * Creates a native_handle_t and initializes it from another native_handle_t.
* Must be destroyed with native_handle_delete().
- *
*/
native_handle_t* native_handle_clone(const native_handle_t* handle);
/*
- * native_handle_delete
- *
- * frees a native_handle_t allocated with native_handle_create().
+ * Frees a native_handle_t allocated with native_handle_create().
* This ONLY frees the memory allocated for the native_handle_t, but doesn't
* close the file descriptors; which can be achieved with native_handle_close().
- *
+ *
* return 0 on success, or a negative error code on failure
- *
*/
int native_handle_delete(native_handle_t* h);
diff --git a/libcutils/native_handle.cpp b/libcutils/native_handle.cpp
index 5804ab1..b85c93b 100644
--- a/libcutils/native_handle.cpp
+++ b/libcutils/native_handle.cpp
@@ -22,13 +22,74 @@
#include <string.h>
#include <unistd.h>
+// Needs to come after stdlib includes to capture the __BIONIC__ definition
+#ifdef __BIONIC__
+#include <android/fdsan.h>
+#endif
+
+namespace {
+
+#if !defined(__BIONIC__)
+// fdsan stubs when not linked against bionic
+#define ANDROID_FDSAN_OWNER_TYPE_NATIVE_HANDLE 0
+
+uint64_t android_fdsan_create_owner_tag(int /*type*/, uint64_t /*tag*/) {
+ return 0;
+}
+uint64_t android_fdsan_get_owner_tag(int /*fd*/) {
+ return 0;
+}
+int android_fdsan_close_with_tag(int fd, uint64_t /*tag*/) {
+ return close(fd);
+}
+void android_fdsan_exchange_owner_tag(int /*fd*/, uint64_t /*expected_tag*/, uint64_t /*tag*/) {}
+#endif // !__BIONIC__
+
+uint64_t get_fdsan_tag(const native_handle_t* handle) {
+ return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_NATIVE_HANDLE,
+ reinterpret_cast<uint64_t>(handle));
+}
+
+int close_internal(const native_handle_t* h, bool allowUntagged) {
+ if (!h) return 0;
+
+ if (h->version != sizeof(native_handle_t)) return -EINVAL;
+
+ const int numFds = h->numFds;
+ uint64_t tag;
+ if (allowUntagged && numFds > 0 && android_fdsan_get_owner_tag(h->data[0]) == 0) {
+ tag = 0;
+ } else {
+ tag = get_fdsan_tag(h);
+ }
+ int saved_errno = errno;
+ for (int i = 0; i < numFds; ++i) {
+ android_fdsan_close_with_tag(h->data[i], tag);
+ }
+ errno = saved_errno;
+ return 0;
+}
+
+void swap_fdsan_tags(const native_handle_t* handle, uint64_t expected_tag, uint64_t new_tag) {
+ if (!handle || handle->version != sizeof(native_handle_t)) return;
+
+ for (int i = 0; i < handle->numFds; i++) {
+ // allow for idempotence to make the APIs easier to use
+ if (android_fdsan_get_owner_tag(handle->data[i]) != new_tag) {
+ android_fdsan_exchange_owner_tag(handle->data[i], expected_tag, new_tag);
+ }
+ }
+}
+
+} // anonymous namespace
+
native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
- if ((uintptr_t) storage % alignof(native_handle_t)) {
+ if ((uintptr_t)storage % alignof(native_handle_t)) {
errno = EINVAL;
return NULL;
}
- native_handle_t* handle = (native_handle_t*) storage;
+ native_handle_t* handle = (native_handle_t*)storage;
handle->version = sizeof(native_handle_t);
handle->numFds = numFds;
handle->numInts = numInts;
@@ -52,6 +113,14 @@
return h;
}
+void native_handle_set_fdsan_tag(const native_handle_t* handle) {
+ swap_fdsan_tags(handle, 0, get_fdsan_tag(handle));
+}
+
+void native_handle_unset_fdsan_tag(const native_handle_t* handle) {
+ swap_fdsan_tags(handle, get_fdsan_tag(handle), 0);
+}
+
native_handle_t* native_handle_clone(const native_handle_t* handle) {
native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
if (clone == NULL) return NULL;
@@ -81,15 +150,9 @@
}
int native_handle_close(const native_handle_t* h) {
- if (!h) return 0;
+ return close_internal(h, /*allowUntagged=*/true);
+}
- if (h->version != sizeof(native_handle_t)) return -EINVAL;
-
- int saved_errno = errno;
- const int numFds = h->numFds;
- for (int i = 0; i < numFds; ++i) {
- close(h->data[i]);
- }
- errno = saved_errno;
- return 0;
+int native_handle_close_with_tag(const native_handle_t* h) {
+ return close_internal(h, /*allowUntagged=*/false);
}