Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f2af69d authored by Brian Duddie's avatar Brian Duddie
Browse files

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
parent 68e3a6f9
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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",
+39 −20
Original line number Diff line number Diff line
@@ -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, 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.
 *
 * closes the file descriptors contained in this native_handle_t
 * 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);

+75 −12
Original line number Diff line number Diff line
@@ -22,6 +22,67 @@
#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)) {
        errno = EINVAL;
@@ -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]);
    return close_internal(h, /*allowUntagged=*/true);
}
    errno = saved_errno;
    return 0;

int native_handle_close_with_tag(const native_handle_t* h) {
    return close_internal(h, /*allowUntagged=*/false);
}