Add a tool to track down fd leaks.
Add a hook that's called upon file descriptor creation to libc, and a
library that uses it to capture backtraces for file descriptor creation,
to make it easier to hunt down file descriptor leaks.
Currently, this doesn't capture all of the ways of creating a file
descriptor, but completeness isn't required for this to be useful as
long as leaked file descriptors are created with a function that is
tracked. The primary unhandled case is binder, which receives file
descriptors as a payload in a not-trivially-parsable byte blob, but
there's a chance that the leak we're currently trying to track down
isn't of a file descriptor received over binder, so leave that for
later.
Bug: http://b/140703823
Test: manual
Change-Id: I308a14c2e234cdba4207157b634ab6b8bc539dd9
diff --git a/libc/bionic/NetdClientDispatch.cpp b/libc/bionic/NetdClientDispatch.cpp
index 463ef36..e6f4a97 100644
--- a/libc/bionic/NetdClientDispatch.cpp
+++ b/libc/bionic/NetdClientDispatch.cpp
@@ -18,6 +18,8 @@
#include <sys/socket.h>
+#include "private/bionic_fdtrack.h"
+
#ifdef __i386__
#define __socketcall __attribute__((__cdecl__))
#else
@@ -53,7 +55,7 @@
};
int accept4(int fd, sockaddr* addr, socklen_t* addr_length, int flags) {
- return __netdClientDispatch.accept4(fd, addr, addr_length, flags);
+ return FDTRACK_CREATE(__netdClientDispatch.accept4(fd, addr, addr_length, flags));
}
int connect(int fd, const sockaddr* addr, socklen_t addr_length) {
@@ -74,5 +76,5 @@
}
int socket(int domain, int type, int protocol) {
- return __netdClientDispatch.socket(domain, type, protocol);
+ return FDTRACK_CREATE(__netdClientDispatch.socket(domain, type, protocol));
}
diff --git a/libc/bionic/dup2.cpp b/libc/bionic/dup.cpp
similarity index 83%
rename from libc/bionic/dup2.cpp
rename to libc/bionic/dup.cpp
index 98c5646..d9e89a5 100644
--- a/libc/bionic/dup2.cpp
+++ b/libc/bionic/dup.cpp
@@ -29,6 +29,15 @@
#include <fcntl.h>
#include <unistd.h>
+#include "private/bionic_fdtrack.h"
+
+extern "C" int __dup(int old_fd);
+extern "C" int __dup3(int old_fd, int new_fd, int flags);
+
+int dup(int old_fd) {
+ return FDTRACK_CREATE(__dup(old_fd));
+}
+
int dup2(int old_fd, int new_fd) {
// If old_fd is equal to new_fd and a valid file descriptor, dup2 returns
// old_fd without closing it. This is not true of dup3, so we have to
@@ -40,5 +49,9 @@
return old_fd;
}
- return dup3(old_fd, new_fd, 0);
+ return FDTRACK_CREATE(__dup3(old_fd, new_fd, 0));
+}
+
+int dup3(int old_fd, int new_fd, int flags) {
+ return FDTRACK_CREATE(__dup3(old_fd, new_fd, flags));
}
diff --git a/libc/bionic/fcntl.cpp b/libc/bionic/fcntl.cpp
new file mode 100644
index 0000000..c508131
--- /dev/null
+++ b/libc/bionic/fcntl.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#include <fcntl.h>
+
+#include "private/bionic_fdtrack.h"
+
+#if defined(__LP64__)
+
+extern "C" int __fcntl(int fd, int cmd, ...);
+
+int fcntl(int fd, int cmd, ...) {
+ va_list args;
+ va_start(args, cmd);
+
+ // This is a bit sketchy, especially because arg can be an int, but all of our
+ // supported 64-bit ABIs pass arg in a register.
+ void* arg = va_arg(args, void*);
+ va_end(args);
+
+ int rc = __fcntl(fd, cmd, arg);
+ if (cmd == F_DUPFD) {
+ return FDTRACK_CREATE_NAME("F_DUPFD", rc);
+ } else if (cmd == F_DUPFD_CLOEXEC) {
+ return FDTRACK_CREATE_NAME("F_DUPFD_CLOEXEC", rc);
+ }
+ return rc;
+}
+
+#else
+
+extern "C" int __fcntl64(int, int, ...);
+
+// For fcntl we use the fcntl64 system call to signal that we're using struct flock64.
+int fcntl(int fd, int cmd, ...) {
+ va_list ap;
+
+ va_start(ap, cmd);
+ void* arg = va_arg(ap, void*);
+ va_end(ap);
+
+ if (cmd == F_DUPFD) {
+ return FDTRACK_CREATE_NAME("F_DUPFD", __fcntl64(fd, cmd, arg));
+ } else if (cmd == F_DUPFD_CLOEXEC) {
+ return FDTRACK_CREATE_NAME("F_DUPFD_CLOEXEC", __fcntl64(fd, cmd, arg));
+ }
+ return __fcntl64(fd, cmd, arg);
+}
+
+#endif
diff --git a/libc/bionic/fdsan.cpp b/libc/bionic/fdsan.cpp
index d4ac71c..ebc680f 100644
--- a/libc/bionic/fdsan.cpp
+++ b/libc/bionic/fdsan.cpp
@@ -43,6 +43,7 @@
#include <platform/bionic/reserved_signals.h>
#include <sys/system_properties.h>
+#include "private/bionic_fdtrack.h"
#include "private/bionic_globals.h"
#include "private/bionic_inline_raise.h"
#include "pthread_internal.h"
@@ -245,6 +246,7 @@
}
int android_fdsan_close_with_tag(int fd, uint64_t expected_tag) {
+ FDTRACK_CLOSE(fd);
FdEntry* fde = GetFdEntry(fd);
if (!fde) {
return __close(fd);
diff --git a/libc/bionic/fdtrack.cpp b/libc/bionic/fdtrack.cpp
new file mode 100644
index 0000000..38eb219
--- /dev/null
+++ b/libc/bionic/fdtrack.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdatomic.h>
+
+#include <android/fdtrack.h>
+
+#include <platform/bionic/reserved_signals.h>
+
+#include "private/bionic_fdtrack.h"
+#include "private/bionic_tls.h"
+#include "private/bionic_globals.h"
+
+_Atomic(android_fdtrack_hook_t) __android_fdtrack_hook;
+
+bool android_fdtrack_get_enabled() {
+ return !__get_bionic_tls().fdtrack_disabled;
+}
+
+bool android_fdtrack_set_enabled(bool new_value) {
+ auto& tls = __get_bionic_tls();
+ bool prev = !tls.fdtrack_disabled;
+ tls.fdtrack_disabled = !new_value;
+ return prev;
+}
+
+bool android_fdtrack_compare_exchange_hook(android_fdtrack_hook_t* expected,
+ android_fdtrack_hook_t value) {
+ return atomic_compare_exchange_strong(&__android_fdtrack_hook, expected, value);
+}
+
+void __libc_init_fdtrack() {
+ // Register a no-op signal handler.
+ signal(BIONIC_SIGNAL_FDTRACK, [](int) {});
+}
diff --git a/libc/bionic/legacy_32_bit_support.cpp b/libc/bionic/legacy_32_bit_support.cpp
index 2de1bc7..f08e582 100644
--- a/libc/bionic/legacy_32_bit_support.cpp
+++ b/libc/bionic/legacy_32_bit_support.cpp
@@ -37,27 +37,17 @@
#include <sys/vfs.h>
#include <unistd.h>
+#include "private/bionic_fdtrack.h"
+
#if defined(__LP64__)
#error This code is only needed on 32-bit systems!
#endif
// System calls we need.
-extern "C" int __fcntl64(int, int, void*);
extern "C" int __llseek(int, unsigned long, unsigned long, off64_t*, int);
extern "C" int __preadv64(int, const struct iovec*, int, long, long);
extern "C" int __pwritev64(int, const struct iovec*, int, long, long);
-// For fcntl we use the fcntl64 system call to signal that we're using struct flock64.
-int fcntl(int fd, int cmd, ...) {
- va_list ap;
-
- va_start(ap, cmd);
- void* arg = va_arg(ap, void*);
- va_end(ap);
-
- return __fcntl64(fd, cmd, arg);
-}
-
// For lseek64 we need to use the llseek system call which splits the off64_t in two and
// returns the off64_t result via a pointer because 32-bit kernels can't return 64-bit results.
off64_t lseek64(int fd, off64_t off, int whence) {
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index e4106e9..12628f7 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -104,6 +104,7 @@
__system_properties_init(); // Requires 'environ'.
__libc_init_fdsan(); // Requires system properties (for debug.fdsan).
+ __libc_init_fdtrack();
SetDefaultHeapTaggingLevel();
}
diff --git a/libc/bionic/open.cpp b/libc/bionic/open.cpp
index 222e5d3..bd8685a 100644
--- a/libc/bionic/open.cpp
+++ b/libc/bionic/open.cpp
@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <unistd.h>
+#include "private/bionic_fdtrack.h"
#include "private/bionic_fortify.h"
extern "C" int __openat(int, const char*, int, int);
@@ -62,13 +63,13 @@
va_end(args);
}
- return __openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), mode);
+ return FDTRACK_CREATE(__openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), mode));
}
__strong_alias(open64, open);
int __open_2(const char* pathname, int flags) {
if (needs_mode(flags)) __fortify_fatal("open: called with O_CREAT/O_TMPFILE but no mode");
- return __openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), 0);
+ return FDTRACK_CREATE_NAME("open", __openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), 0));
}
int openat(int fd, const char *pathname, int flags, ...) {
@@ -81,11 +82,11 @@
va_end(args);
}
- return __openat(fd, pathname, force_O_LARGEFILE(flags), mode);
+ return FDTRACK_CREATE_NAME("openat", __openat(fd, pathname, force_O_LARGEFILE(flags), mode));
}
__strong_alias(openat64, openat);
int __openat_2(int fd, const char* pathname, int flags) {
if (needs_mode(flags)) __fortify_fatal("open: called with O_CREAT/O_TMPFILE but no mode");
- return __openat(fd, pathname, force_O_LARGEFILE(flags), 0);
+ return FDTRACK_CREATE_NAME("openat", __openat(fd, pathname, force_O_LARGEFILE(flags), 0));
}
diff --git a/libc/bionic/recvmsg.cpp b/libc/bionic/recvmsg.cpp
new file mode 100644
index 0000000..003f43d
--- /dev/null
+++ b/libc/bionic/recvmsg.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <sys/socket.h>
+
+#include <async_safe/log.h>
+
+#include "private/bionic_fdtrack.h"
+
+extern "C" ssize_t __recvmsg(int __fd, struct msghdr* __msg, int __flags);
+extern "C" int __recvmmsg(int __fd, struct mmsghdr* __msgs, unsigned int __msg_count, int __flags,
+ const struct timespec* __timeout);
+
+static inline __attribute__((artificial)) __attribute__((always_inline)) void track_fds(
+ struct msghdr* msg, const char* function_name) {
+ if (!__android_fdtrack_hook) {
+ return;
+ }
+
+ for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_type != SCM_RIGHTS) {
+ continue;
+ }
+
+ if (cmsg->cmsg_len <= sizeof(struct cmsghdr)) {
+ continue;
+ }
+
+ size_t data_length = cmsg->cmsg_len - sizeof(struct cmsghdr);
+ if (data_length % sizeof(int) != 0) {
+ async_safe_fatal("invalid cmsg length: %zu", data_length);
+ }
+
+ for (size_t offset = 0; offset < data_length; offset += sizeof(int)) {
+ int fd;
+ memcpy(&fd, CMSG_DATA(cmsg) + offset, sizeof(int));
+ FDTRACK_CREATE_NAME(function_name, fd);
+ }
+ }
+}
+
+ssize_t recvmsg(int __fd, struct msghdr* __msg, int __flags) {
+ ssize_t rc = __recvmsg(__fd, __msg, __flags);
+ if (rc == -1) {
+ return -1;
+ }
+ track_fds(__msg, "recvmsg");
+ return rc;
+}
+
+int recvmmsg(int __fd, struct mmsghdr* __msgs, unsigned int __msg_count, int __flags,
+ const struct timespec* __timeout) {
+ int rc = __recvmmsg(__fd, __msgs, __msg_count, __flags, __timeout);
+ if (rc == -1) {
+ return -1;
+ }
+ for (int i = 0; i < rc; ++i) {
+ track_fds(&__msgs[i].msg_hdr, "recvmmsg");
+ }
+ return rc;
+}