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
This commit is contained in:
Josh Gao 2019-11-06 13:15:00 -08:00
parent 15b4f27853
commit b7eccd4b15
23 changed files with 713 additions and 29 deletions

View file

@ -1103,7 +1103,7 @@ cc_library_static {
"bionic/clone.cpp",
"bionic/ctype.cpp",
"bionic/dirent.cpp",
"bionic/dup2.cpp",
"bionic/dup.cpp",
"bionic/environ.cpp",
"bionic/error.cpp",
"bionic/eventfd_read.cpp",
@ -1112,7 +1112,9 @@ cc_library_static {
"bionic/faccessat.cpp",
"bionic/fchmod.cpp",
"bionic/fchmodat.cpp",
"bionic/fcntl.cpp",
"bionic/fdsan.cpp",
"bionic/fdtrack.cpp",
"bionic/ffs.cpp",
"bionic/fgetxattr.cpp",
"bionic/flistxattr.cpp",
@ -1181,6 +1183,7 @@ cc_library_static {
"bionic/realpath.cpp",
"bionic/reboot.cpp",
"bionic/recv.cpp",
"bionic/recvmsg.cpp",
"bionic/rename.cpp",
"bionic/rmdir.cpp",
"bionic/scandir.cpp",
@ -1814,7 +1817,7 @@ cc_library_headers {
name: "bionic_libc_platform_headers",
visibility: [
"//art:__subpackages__",
"//bionic/libc:__subpackages__",
"//bionic:__subpackages__",
"//frameworks:__subpackages__",
"//external/perfetto:__subpackages__",
"//external/scudo:__subpackages__",

View file

@ -123,12 +123,12 @@ int __ioctl:ioctl(int, int, void*) all
ssize_t readv(int, const struct iovec*, int) all
ssize_t writev(int, const struct iovec*, int) all
int __fcntl64:fcntl64(int, int, void*) lp32
int fcntl(int, int, void*) lp64
int __fcntl:fcntl(int, int, void*) lp64
int flock(int, int) all
int __fchmod:fchmod(int, mode_t) all
int dup(int) all
int pipe2(int*, int) all
int dup3(int, int, int) all
int __dup:dup(int) all
int __dup3:dup3(int, int, int) all
int fsync(int) all
int fdatasync(int) all
int fchown:fchown32(int, uid_t, gid_t) arm,x86
@ -254,9 +254,9 @@ ssize_t recvfrom(int, void*, size_t, unsigned int, struct sockaddr*, sockl
int shutdown(int, int) arm,arm64,mips,mips64,x86_64
int setsockopt(int, int, int, const void*, socklen_t) arm,arm64,mips,mips64,x86_64
int getsockopt(int, int, int, void*, socklen_t*) arm,arm64,mips,mips64,x86_64
ssize_t recvmsg(int, struct msghdr*, unsigned int) arm,arm64,mips,mips64,x86_64
ssize_t __recvmsg:recvmsg(int, struct msghdr*, unsigned int) arm,arm64,mips,mips64,x86_64
ssize_t __sendmsg:sendmsg(int, const struct msghdr*, unsigned int) arm,arm64,mips,mips64,x86_64
int recvmmsg(int, struct mmsghdr*, unsigned int, int, const struct timespec*) arm,arm64,mips,mips64,x86_64
int __recvmmsg:recvmmsg(int, struct mmsghdr*, unsigned int, int, const struct timespec*) arm,arm64,mips,mips64,x86_64
int __sendmmsg:sendmmsg(int, struct mmsghdr*, unsigned int, int) arm,arm64,mips,mips64,x86_64
# sockets for x86. These are done as an "indexed" call to socketcall syscall.
@ -273,9 +273,9 @@ int shutdown:socketcall:13(int, int) x86
int setsockopt:socketcall:14(int, int, int, const void*, socklen_t) x86
int getsockopt:socketcall:15(int, int, int, void*, socklen_t*) x86
int __sendmsg:socketcall:16(int, const struct msghdr*, unsigned int) x86
int recvmsg:socketcall:17(int, struct msghdr*, unsigned int) x86
int __recvmsg:socketcall:17(int, struct msghdr*, unsigned int) x86
int __accept4:socketcall:18(int, struct sockaddr*, socklen_t*, int) x86
int recvmmsg:socketcall:19(int, struct mmsghdr*, unsigned int, int, const struct timespec*) x86
int __recvmmsg:socketcall:19(int, struct mmsghdr*, unsigned int, int, const struct timespec*) x86
int __sendmmsg:socketcall:20(int, struct mmsghdr*, unsigned int, int) x86
# scheduler & real-time

View file

@ -18,6 +18,8 @@
#include <sys/socket.h>
#include "private/bionic_fdtrack.h"
#ifdef __i386__
#define __socketcall __attribute__((__cdecl__))
#else
@ -53,7 +55,7 @@ __LIBC_HIDDEN__ NetdClientDispatch __netdClientDispatch __attribute__((aligned(3
};
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 @@ ssize_t sendto(int fd, const void* buf, size_t n, int flags,
}
int socket(int domain, int type, int protocol) {
return __netdClientDispatch.socket(domain, type, protocol);
return FDTRACK_CREATE(__netdClientDispatch.socket(domain, type, protocol));
}

View file

@ -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 @@ int dup2(int old_fd, int new_fd) {
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));
}

76
libc/bionic/fcntl.cpp Normal file
View file

@ -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

View file

@ -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 @@ uint64_t android_fdsan_get_tag_value(uint64_t tag) {
}
int android_fdsan_close_with_tag(int fd, uint64_t expected_tag) {
FDTRACK_CLOSE(fd);
FdEntry* fde = GetFdEntry(fd);
if (!fde) {
return __close(fd);

60
libc/bionic/fdtrack.cpp Normal file
View file

@ -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) {});
}

View file

@ -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) {

View file

@ -104,6 +104,7 @@ void __libc_init_common() {
__system_properties_init(); // Requires 'environ'.
__libc_init_fdsan(); // Requires system properties (for debug.fdsan).
__libc_init_fdtrack();
SetDefaultHeapTaggingLevel();
}

View file

@ -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 @@ int open(const char* pathname, int flags, ...) {
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 @@ int openat(int fd, const char *pathname, int flags, ...) {
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));
}

87
libc/bionic/recvmsg.cpp Normal file
View file

@ -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;
}

View file

@ -0,0 +1,73 @@
/*
* 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.
*/
#pragma once
#include <sys/cdefs.h>
#include <stdbool.h>
#include <stdint.h>
__BEGIN_DECLS
// Types of an android_fdtrack_event.
enum android_fdtrack_event_type {
// File descriptor creation: create is the active member of android_fdtrack_event::data.
ANDROID_FDTRACK_EVENT_TYPE_CREATE,
// File descriptor closed.
ANDROID_FDTRACK_EVENT_TYPE_CLOSE,
};
struct android_fdtrack_event {
// File descriptor for which this event occurred.
int fd;
// Type of event: this is one of the enumerators of android_fdtrack_event_type.
uint8_t type;
// Data for the event.
union {
struct {
const char* function_name;
} create;
} data;
};
// Callback invoked upon file descriptor creation/closure.
typedef void (*android_fdtrack_hook_t)(struct android_fdtrack_event*);
// Register a hook which is called to track fd lifecycle events.
bool android_fdtrack_compare_exchange_hook(android_fdtrack_hook_t* expected, android_fdtrack_hook_t value) __INTRODUCED_IN(30);
// Enable/disable fdtrack *on the current thread*.
// This is primarily useful when performing operations which you don't want to track
// (e.g. when emitting already-recorded information).
bool android_fdtrack_get_enabled() __INTRODUCED_IN(30);
bool android_fdtrack_set_enabled(bool new_value) __INTRODUCED_IN(30);
__END_DECLS

View file

@ -1484,6 +1484,9 @@ LIBC_R { # introduced=R
global:
__mempcpy_chk;
__tls_get_addr; # arm64
android_fdtrack_compare_exchange_hook;
android_fdtrack_get_enabled;
android_fdtrack_set_enabled;
call_once;
cnd_broadcast;
cnd_destroy;

View file

@ -42,14 +42,16 @@
// 36 (__SIGRTMIN + 4) platform profilers (heapprofd, traced_perf)
// 37 (__SIGRTMIN + 5) coverage (libprofile-extras)
// 38 (__SIGRTMIN + 6) heapprofd ART managed heap dumps
// 39 (__SIGRTMIN + 7) fdtrack
//
// If you change this, also change __ndk_legacy___libc_current_sigrtmin
// in <android/legacy_signal_inlines.h> to match.
#define BIONIC_SIGNAL_DEBUGGER __SIGRTMIN + 3
#define BIONIC_SIGNAL_PROFILER __SIGRTMIN + 4
#define BIONIC_SIGNAL_FDTRACK __SIGRTMIN + 7
#define __SIGRT_RESERVED 7
#define __SIGRT_RESERVED 8
static inline __always_inline sigset64_t filter_reserved_signals(sigset64_t sigset, int how) {
int (*block)(sigset64_t*, int);
int (*unblock)(sigset64_t*, int);
@ -77,5 +79,6 @@ static inline __always_inline sigset64_t filter_reserved_signals(sigset64_t sigs
unblock(&sigset, __SIGRTMIN + 4);
unblock(&sigset, __SIGRTMIN + 5);
unblock(&sigset, __SIGRTMIN + 6);
unblock(&sigset, __SIGRTMIN + 7);
return sigset;
}

View file

@ -0,0 +1,93 @@
/*
* 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.
*/
#pragma once
#include <sys/cdefs.h>
#include <stdatomic.h>
#include <android/fdtrack.h>
#include "bionic/pthread_internal.h"
#include "private/bionic_tls.h"
#include "private/ErrnoRestorer.h"
extern "C" _Atomic(android_fdtrack_hook_t) __android_fdtrack_hook;
// Macro to record file descriptor creation.
// e.g.:
// int socket(int domain, int type, int protocol) {
// return FDTRACK_CREATE_NAME("socket", __socket(domain, type, protocol));
// }
#define FDTRACK_CREATE_NAME(name, fd_value) \
({ \
int __fd = (fd_value); \
if (__fd != -1 && __predict_false(__android_fdtrack_hook)) { \
bionic_tls& tls = __get_bionic_tls(); \
/* fdtrack_disabled is only true during reentrant calls. */ \
if (!__predict_false(tls.fdtrack_disabled)) { \
ErrnoRestorer r; \
tls.fdtrack_disabled = true; \
android_fdtrack_event event; \
event.fd = __fd; \
event.type = ANDROID_FDTRACK_EVENT_TYPE_CREATE; \
event.data.create.function_name = name; \
__android_fdtrack_hook(&event); \
tls.fdtrack_disabled = false; \
} \
} \
__fd; \
})
// Macro to record file descriptor creation, with the current function's name.
// e.g.:
// int socket(int domain, int type, int protocol) {
// return FDTRACK_CREATE_NAME(__socket(domain, type, protocol));
// }
#define FDTRACK_CREATE(fd_value) FDTRACK_CREATE_NAME(__func__, (fd_value))
// Macro to record file descriptor closure.
// Note that this does not actually close the file descriptor.
#define FDTRACK_CLOSE(fd_value) \
({ \
int __fd = (fd_value); \
if (__fd != -1 && __predict_false(__android_fdtrack_hook)) { \
bionic_tls& tls = __get_bionic_tls(); \
if (!__predict_false(tls.fdtrack_disabled)) { \
int saved_errno = errno; \
tls.fdtrack_disabled = true; \
android_fdtrack_event event; \
event.fd = __fd; \
event.type = ANDROID_FDTRACK_EVENT_TYPE_CLOSE; \
__android_fdtrack_hook(&event); \
tls.fdtrack_disabled = false; \
errno = saved_errno; \
} \
} \
__fd; \
})

View file

@ -100,6 +100,7 @@ struct libc_shared_globals {
__LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals();
__LIBC_HIDDEN__ void __libc_init_fdsan();
__LIBC_HIDDEN__ void __libc_init_fdtrack();
__LIBC_HIDDEN__ void __libc_init_profiling_handlers();
__LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals);

View file

@ -128,6 +128,9 @@ struct bionic_tls {
group_state_t group;
passwd_state_t passwd;
char fdtrack_disabled;
char padding[3];
// Initialize the main thread's final object using its bootstrap object.
void copy_from_bootstrap(const bionic_tls* boot __attribute__((unused))) {
// Nothing in bionic_tls needs to be preserved in the transition to the

1
libfdtrack/.clang-format Symbolic link
View file

@ -0,0 +1 @@
../.clang-format-2

18
libfdtrack/Android.bp Normal file
View file

@ -0,0 +1,18 @@
cc_library_shared {
name: "libfdtrack",
srcs: ["fdtrack.cpp"],
stl: "libc++_static",
header_libs: ["bionic_libc_platform_headers"],
static_libs: [
"libasync_safe",
"libbase",
"libunwindstack",
"liblzma",
"liblog",
],
version_script: "libfdtrack.map.txt",
allow_undefined_symbols: true,
recovery_available: true,
}

127
libfdtrack/fdtrack.cpp Normal file
View file

@ -0,0 +1,127 @@
/*
* 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 <inttypes.h>
#include <array>
#include <mutex>
#include <vector>
#include <android/fdtrack.h>
#include <android-base/no_destructor.h>
#include <android-base/thread_annotations.h>
#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
#include <unwindstack/LocalUnwinder.h>
struct FdEntry {
std::mutex mutex;
std::vector<unwindstack::LocalFrameData> backtrace GUARDED_BY(mutex);
};
extern "C" void fdtrack_dump();
static void fd_hook(android_fdtrack_event* event);
// Backtraces for the first 4k file descriptors ought to be enough to diagnose an fd leak.
static constexpr size_t kFdTableSize = 4096;
static constexpr size_t kStackDepth = 10;
static bool installed = false;
static std::array<FdEntry, kFdTableSize> stack_traces;
static unwindstack::LocalUnwinder& Unwinder() {
static android::base::NoDestructor<unwindstack::LocalUnwinder> unwinder;
return *unwinder.get();
}
__attribute__((constructor)) static void ctor() {
signal(BIONIC_SIGNAL_FDTRACK, [](int) { fdtrack_dump(); });
if (Unwinder().Init()) {
android_fdtrack_hook_t expected = nullptr;
installed = android_fdtrack_compare_exchange_hook(&expected, &fd_hook);
}
}
__attribute__((destructor)) static void dtor() {
if (installed) {
android_fdtrack_hook_t expected = &fd_hook;
android_fdtrack_compare_exchange_hook(&expected, nullptr);
}
}
FdEntry* GetFdEntry(int fd) {
if (fd >= 0 && fd < static_cast<int>(kFdTableSize)) {
return &stack_traces[fd];
}
return nullptr;
}
static void fd_hook(android_fdtrack_event* event) {
if (event->type == ANDROID_FDTRACK_EVENT_TYPE_CREATE) {
if (FdEntry* entry = GetFdEntry(event->fd); entry) {
std::lock_guard<std::mutex> lock(entry->mutex);
entry->backtrace.clear();
Unwinder().Unwind(&entry->backtrace, kStackDepth);
}
} else if (event->type == ANDROID_FDTRACK_EVENT_TYPE_CLOSE) {
if (FdEntry* entry = GetFdEntry(event->fd); entry) {
std::lock_guard<std::mutex> lock(entry->mutex);
entry->backtrace.clear();
}
}
}
void fdtrack_dump() {
if (!installed) {
async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fdtrack not installed");
} else {
async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fdtrack dumping...");
}
bool prev = android_fdtrack_set_enabled(false);
for (int fd = 0; fd < static_cast<int>(stack_traces.size()); ++fd) {
FdEntry* entry = GetFdEntry(fd);
if (!entry) {
continue;
}
std::lock_guard<std::mutex> lock(entry->mutex);
if (entry->backtrace.empty()) {
continue;
}
async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fd %d:", fd);
const size_t frame_skip = 2;
for (size_t i = frame_skip; i < entry->backtrace.size(); ++i) {
auto& frame = entry->backtrace[i];
async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", " %zu: %s+%" PRIu64, i - frame_skip,
frame.function_name.c_str(), frame.function_offset);
}
}
android_fdtrack_set_enabled(prev);
}

View file

@ -0,0 +1,6 @@
LIBFDTRACK {
global:
fdtrack_dump;
local:
*;
};

View file

@ -91,6 +91,7 @@ cc_test_library {
"eventfd_test.cpp",
"fcntl_test.cpp",
"fdsan_test.cpp",
"fdtrack_test.cpp",
"fenv_test.cpp",
"float_test.cpp",
"ftw_test.cpp",

120
tests/fdtrack_test.cpp Normal file
View file

@ -0,0 +1,120 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#if defined(__BIONIC__)
#include <android/fdtrack.h>
#endif
#include <vector>
#include <android-base/unique_fd.h>
#if defined(__BIONIC__)
std::vector<android_fdtrack_event> FdtrackRun(void (*func)()) {
// Each bionic test is run in separate process, so we can safely use a static here.
static std::vector<android_fdtrack_event> events;
events.clear();
android_fdtrack_hook_t previous = nullptr;
android_fdtrack_hook_t hook = [](android_fdtrack_event* event) {
events.push_back(*event);
};
if (!android_fdtrack_compare_exchange_hook(&previous, hook)) {
errx(1, "failed to exchange hook: previous hook was %p", previous);
}
if (previous) {
errx(1, "hook was already registered?");
abort();
}
func();
if (!android_fdtrack_compare_exchange_hook(&hook, nullptr)) {
errx(1, "failed to reset hook");
}
return std::move(events);
}
#endif
TEST(fdtrack, open) {
#if defined(__BIONIC__)
static int fd = -1;
auto events = FdtrackRun([]() { fd = open("/dev/null", O_WRONLY | O_CLOEXEC); });
ASSERT_NE(-1, fd);
ASSERT_EQ(1U, events.size());
ASSERT_EQ(fd, events[0].fd);
ASSERT_EQ(ANDROID_FDTRACK_EVENT_TYPE_CREATE, events[0].type);
ASSERT_STREQ("open", events[0].data.create.function_name);
#endif
}
TEST(fdtrack, close) {
#if defined(__BIONIC__)
static int fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
ASSERT_NE(-1, fd);
auto events = FdtrackRun([]() { close(fd); });
ASSERT_EQ(1U, events.size());
ASSERT_EQ(fd, events[0].fd);
ASSERT_EQ(ANDROID_FDTRACK_EVENT_TYPE_CLOSE, events[0].type);
#endif
}
TEST(fdtrack, enable_disable) {
#if defined(__BIONIC__)
static int fd1 = -1;
static int fd2 = -1;
static int fd3 = -1;
auto events = FdtrackRun([]() {
if (!android_fdtrack_get_enabled()) {
errx(1, "fdtrack is disabled");
}
fd1 = open("/dev/null", O_WRONLY | O_CLOEXEC);
android_fdtrack_set_enabled(false);
fd2 = open("/dev/null", O_WRONLY | O_CLOEXEC);
android_fdtrack_set_enabled(true);
fd3 = open("/dev/null", O_WRONLY | O_CLOEXEC);
});
if (fd1 == -1 || fd2 == -1 || fd3 == -1) {
errx(1, "failed to open /dev/null");
}
ASSERT_EQ(2U, events.size());
ASSERT_EQ(fd1, events[0].fd);
ASSERT_EQ(ANDROID_FDTRACK_EVENT_TYPE_CREATE, events[0].type);
ASSERT_STREQ("open", events[0].data.create.function_name);
ASSERT_EQ(fd3, events[1].fd);
ASSERT_EQ(ANDROID_FDTRACK_EVENT_TYPE_CREATE, events[1].type);
ASSERT_STREQ("open", events[1].data.create.function_name);
#endif
}