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
This commit is contained in:
Brian Duddie 2022-08-24 19:52:08 +00:00
parent 68e3a6f94a
commit 9f2af69d2a
3 changed files with 121 additions and 33 deletions

View file

@ -168,6 +168,9 @@ cc_library {
target: {
linux_bionic: {
enabled: true,
static_libs: [
"libasync_safe",
],
},
not_windows: {
srcs: libcutils_nonwindows_sources + [
@ -190,6 +193,9 @@ cc_library {
],
},
android: {
static_libs: [
"libasync_safe",
],
srcs: libcutils_nonwindows_sources + [
"android_reboot.cpp",
"ashmem-dev.cpp",

View file

@ -49,18 +49,28 @@ typedef struct native_handle
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 @@ int native_handle_close(const native_handle_t* h);
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);

View file

@ -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 @@ native_handle_t* native_handle_create(int numFds, int numInts) {
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_delete(native_handle_t* h) {
}
int native_handle_close(const native_handle_t* h) {
if (!h) return 0;
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;
return close_internal(h, /*allowUntagged=*/true);
}
int native_handle_close_with_tag(const native_handle_t* h) {
return close_internal(h, /*allowUntagged=*/false);
}