summaryrefslogtreecommitdiff
path: root/libc
diff options
context:
space:
mode:
authorSteven Laver <lavers@google.com>2019-11-14 08:37:29 -0800
committerSteven Laver <lavers@google.com>2019-11-14 08:37:29 -0800
commit8f54dd5edaa4ba55451f3602e5890b6f1ab807e6 (patch)
tree709208dd25e59100a8d3d9a7038a5c7ee1c8a50b /libc
parent19c0c9cd4892927004dac3252b67aac89e462c5c (diff)
parentcb88137aebba97024bee4fff130f131924556ee5 (diff)
Merge RP1A.191114.001
Change-Id: I19fb768a647d471d430af4b5c3f519d4125fdeee
Diffstat (limited to 'libc')
-rw-r--r--libc/Android.bp97
-rw-r--r--libc/arch-x86_64/string/cache.h10
-rw-r--r--libc/bionic/__libc_init_main_thread.cpp24
-rw-r--r--libc/bionic/bionic_call_ifunc_resolver.cpp22
-rw-r--r--libc/bionic/bionic_netlink.cpp14
-rw-r--r--libc/bionic/bionic_netlink.h4
-rw-r--r--libc/bionic/fchmodat.cpp13
-rw-r--r--libc/bionic/getauxval.cpp1
-rw-r--r--libc/bionic/getentropy.cpp14
-rw-r--r--libc/bionic/grp_pwd_file.cpp10
-rw-r--r--libc/bionic/jemalloc.h2
-rw-r--r--libc/bionic/jemalloc_wrapper.cpp22
-rw-r--r--libc/bionic/libc_init_static.cpp3
-rw-r--r--libc/bionic/malloc_common.cpp11
-rw-r--r--libc/bionic/malloc_common.h6
-rw-r--r--libc/bionic/malloc_common_dynamic.cpp5
-rw-r--r--libc/bionic/malloc_heapprofd.cpp2
-rw-r--r--libc/bionic/malloc_limit.cpp4
-rw-r--r--libc/bionic/net_if.cpp30
-rw-r--r--libc/bionic/pthread_create.cpp19
-rw-r--r--libc/bionic/scudo.h2
-rw-r--r--libc/bionic/scudo_wrapper.cpp2
-rw-r--r--libc/bionic/system_property_api.cpp8
-rw-r--r--libc/bionic/system_property_set.cpp25
-rw-r--r--libc/dns/net/getaddrinfo.c2
-rw-r--r--libc/include/bits/sys_statvfs_inlines.h28
-rw-r--r--libc/include/fcntl.h144
-rw-r--r--libc/include/search.h189
-rw-r--r--libc/include/sys/xattr.h137
-rw-r--r--libc/libc.map.txt8
-rw-r--r--libc/malloc_debug/PointerData.cpp32
-rw-r--r--libc/malloc_debug/PointerData.h2
-rw-r--r--libc/malloc_debug/exported32.map2
-rw-r--r--libc/malloc_debug/exported64.map2
-rw-r--r--libc/malloc_debug/malloc_debug.cpp53
-rw-r--r--libc/malloc_debug/tests/malloc_debug_system_tests.cpp52
-rw-r--r--libc/malloc_debug/tests/malloc_debug_unit_tests.cpp196
-rw-r--r--libc/malloc_hooks/exported32.map2
-rw-r--r--libc/malloc_hooks/exported64.map2
-rw-r--r--libc/malloc_hooks/malloc_hooks.cpp4
-rw-r--r--libc/private/MallocXmlElem.h19
-rw-r--r--libc/private/ScopedFd.h64
-rw-r--r--libc/private/bionic_malloc_dispatch.h2
-rw-r--r--libc/system_properties/include/system_properties/prop_area.h17
-rw-r--r--libc/system_properties/include/system_properties/system_properties.h3
-rw-r--r--libc/system_properties/system_properties.cpp149
46 files changed, 1082 insertions, 377 deletions
diff --git a/libc/Android.bp b/libc/Android.bp
index 3a607c987..697399ddb 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -42,7 +42,6 @@ libc_common_flags = [
"-Wno-deprecated-declarations",
"-Wno-gcc-compat",
"-Wframe-larger-than=2048",
- "-Wimplicit-fallthrough",
// Try to catch typical 32-bit assumptions that break with 64-bit pointers.
"-Werror=pointer-to-int-cast",
@@ -83,6 +82,12 @@ cc_defaults {
// lld complains about duplicate symbols in libcrt and libgcc. Suppress the
// warning since this is intended right now.
ldflags: ["-Wl,-z,muldefs"],
+
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
}
// Defaults for native allocator libs/includes to make it
@@ -136,19 +141,21 @@ cc_defaults {
}
// ========================================================
-// libc_stack_protector.a - stack protector code
+// libc_bootstrap.a - -fno-stack-protector and -ffreestanding
// ========================================================
//
-// Code that implements the stack protector (or that runs
-// before TLS has been set up) needs to be compiled with
-// -fno-stack-protector, since it accesses the stack canary
-// TLS slot.
+// Code that implements the stack protector (or that runs before TLS has been set up) needs to be
+// compiled with -fno-stack-protector, since it accesses the stack canary TLS slot. In the linker,
+// some of this code runs before ifunc resolvers have made string.h functions work, so compile with
+// -ffreestanding.
cc_library_static {
srcs: [
"bionic/__libc_init_main_thread.cpp",
"bionic/__stack_chk_fail.cpp",
+ "bionic/bionic_call_ifunc_resolver.cpp",
+ "bionic/getauxval.cpp",
],
arch: {
arm64: {
@@ -166,20 +173,25 @@ cc_library_static {
},
defaults: ["libc_defaults"],
- cflags: ["-fno-stack-protector"],
- name: "libc_stack_protector",
+ cflags: ["-fno-stack-protector", "-ffreestanding"],
+ name: "libc_bootstrap",
}
-// libc_init_static.cpp also needs to be built without stack protector,
-// because it's responsible for setting up TLS for static executables.
-// This isn't the case for dynamic executables because the dynamic linker
-// has already set up the main thread's TLS.
+// libc_init_static.cpp and libc_init_dynamic.cpp need to be built without stack protector.
+// libc_init_static.cpp sets up TLS for static executables, and libc_init_dynamic.cpp initializes
+// the stack protector global variable.
cc_library_static {
name: "libc_init_static",
defaults: ["libc_defaults"],
srcs: ["bionic/libc_init_static.cpp"],
- cflags: ["-fno-stack-protector"],
+ cflags: [
+ "-fno-stack-protector",
+
+ // Compile libc_init_static.cpp with -ffreestanding, because some of its code is called
+ // from the linker before ifunc resolvers have made string.h functions available.
+ "-ffreestanding",
+ ],
}
cc_library_static {
@@ -794,12 +806,6 @@ cc_library_static {
cc_library_static {
defaults: ["libc_defaults"],
srcs: [
- // The data that backs getauxval is initialized in the libc init
- // functions which are invoked by the linker. If this file is included
- // in libc_ndk.a, only one of the copies of the global data will be
- // initialized, resulting in nullptr dereferences.
- "bionic/getauxval.cpp",
-
// These require getauxval, which isn't available on older platforms.
"bionic/sysconf.cpp",
"bionic/vdso.cpp",
@@ -1120,7 +1126,6 @@ cc_library_static {
"bionic/atof.cpp",
"bionic/bionic_allocator.cpp",
"bionic/bionic_arc4random.cpp",
- "bionic/bionic_call_ifunc_resolver.cpp",
"bionic/bionic_futex.cpp",
"bionic/bionic_netlink.cpp",
"bionic/bionic_systrace.cpp",
@@ -1463,6 +1468,7 @@ cc_library_static {
whole_static_libs: [
"libc_bionic_ndk",
+ "libc_bootstrap",
"libc_fortify",
"libc_freebsd",
"libc_freebsd_large_stack",
@@ -1470,7 +1476,6 @@ cc_library_static {
"libc_netbsd",
"libc_openbsd_large_stack",
"libc_openbsd_ndk",
- "libc_stack_protector",
"libc_syscalls",
"libc_tzcode",
"libm",
@@ -1494,6 +1499,7 @@ cc_library_static {
whole_static_libs: [
"libc_bionic",
"libc_bionic_ndk",
+ "libc_bootstrap",
"libc_dns",
"libc_fortify",
"libc_freebsd",
@@ -1503,7 +1509,6 @@ cc_library_static {
"libc_openbsd",
"libc_openbsd_large_stack",
"libc_openbsd_ndk",
- "libc_stack_protector",
"libc_syscalls",
"libc_tzcode",
"libstdc++",
@@ -1531,11 +1536,11 @@ cc_library_static {
}
// ========================================================
-// libc_common_static.a For static binaries.
+// libc_static_dispatch.a
// ========================================================
cc_library_static {
defaults: ["libc_defaults"],
- name: "libc_common_static",
+ name: "libc_static_dispatch",
arch: {
x86: {
@@ -1548,20 +1553,17 @@ cc_library_static {
srcs: ["arch-arm64/static_function_dispatch.S"],
},
},
-
- whole_static_libs: [
- "libc_common",
- ],
}
// ========================================================
-// libc_common_shared.a For shared libraries.
+// libc_dynamic_dispatch.a
// ========================================================
cc_library_static {
defaults: ["libc_defaults"],
- name: "libc_common_shared",
+ name: "libc_dynamic_dispatch",
cflags: [
+ "-ffreestanding",
"-fno-stack-protector",
"-fno-jump-tables",
],
@@ -1576,9 +1578,31 @@ cc_library_static {
srcs: ["arch-arm64/dynamic_function_dispatch.cpp"],
},
},
+}
+
+// ========================================================
+// libc_common_static.a For static binaries.
+// ========================================================
+cc_library_static {
+ defaults: ["libc_defaults"],
+ name: "libc_common_static",
whole_static_libs: [
"libc_common",
+ "libc_static_dispatch",
+ ],
+}
+
+// ========================================================
+// libc_common_shared.a For shared libraries.
+// ========================================================
+cc_library_static {
+ defaults: ["libc_defaults"],
+ name: "libc_common_shared",
+
+ whole_static_libs: [
+ "libc_common",
+ "libc_dynamic_dispatch",
],
}
@@ -1602,19 +1626,16 @@ cc_library_static {
// libc_nomalloc.a
// ========================================================
//
-// This is a version of the static C library that does not
-// include malloc. It's useful in situations when the user wants
-// to provide their own malloc implementation, or wants to
-// explicitly disallow the use of malloc, such as in the
-// dynamic linker.
+// This is a version of the static C library used by the dynamic linker that exclude malloc. It also
+// excludes functions selected using ifunc's (e.g. for string.h). Link in either
+// libc_static_dispatch or libc_dynamic_dispatch to provide those functions.
cc_library_static {
name: "libc_nomalloc",
defaults: ["libc_defaults"],
- cflags: ["-DLIBC_STATIC"],
whole_static_libs: [
- "libc_common_static",
+ "libc_common",
"libc_init_static",
"libc_unwind_static",
],
@@ -1731,6 +1752,8 @@ cc_library {
strip: {
keep_symbols_and_debug_frame: true,
},
+
+ whole_static_libs: [ "libgcc_stripped" ],
},
arm64: {
version_script: ":libc.arm64.map",
diff --git a/libc/arch-x86_64/string/cache.h b/libc/arch-x86_64/string/cache.h
index 38acc6e7f..4131509fb 100644
--- a/libc/arch-x86_64/string/cache.h
+++ b/libc/arch-x86_64/string/cache.h
@@ -28,9 +28,9 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/* Values are optimized for Silvermont */
-#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
-#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
+/* Values are optimized for Core Architecture */
+#define SHARED_CACHE_SIZE (4096*1024) /* Core Architecture L2 Cache */
+#define DATA_CACHE_SIZE (24*1024) /* Core Architecture L1 Data Cache */
-#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
-#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
+#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
+#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
diff --git a/libc/bionic/__libc_init_main_thread.cpp b/libc/bionic/__libc_init_main_thread.cpp
index 6e1b0de25..94cf1f8e3 100644
--- a/libc/bionic/__libc_init_main_thread.cpp
+++ b/libc/bionic/__libc_init_main_thread.cpp
@@ -57,7 +57,9 @@ static pthread_internal_t main_thread;
//
// This is in a file by itself because it needs to be built with
// -fno-stack-protector because it's responsible for setting up the main
-// thread's TLS (which stack protector relies on).
+// thread's TLS (which stack protector relies on). It's also built with
+// -ffreestanding because the early init function runs in the linker before
+// ifunc resolvers have run.
// Do enough setup to:
// - Let the dynamic linker invoke system calls (and access errno)
@@ -65,7 +67,8 @@ static pthread_internal_t main_thread;
// - Allow the stack protector to work (with a zero cookie)
// Avoid doing much more because, when this code is called within the dynamic
// linker, the linker binary hasn't been relocated yet, so certain kinds of code
-// are hazardous, such as accessing non-hidden global variables.
+// are hazardous, such as accessing non-hidden global variables or calling
+// string.h functions.
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
extern "C" void __libc_init_main_thread_early(const KernelArgumentBlock& args,
bionic_tcb* temp_tcb) {
@@ -80,6 +83,23 @@ extern "C" void __libc_init_main_thread_early(const KernelArgumentBlock& args,
main_thread.set_cached_pid(main_thread.tid);
}
+// This code is used both by each new pthread and the code that initializes the main thread.
+void __init_tcb(bionic_tcb* tcb, pthread_internal_t* thread) {
+#ifdef TLS_SLOT_SELF
+ // On x86, slot 0 must point to itself so code can read the thread pointer by
+ // loading %fs:0 or %gs:0.
+ tcb->tls_slot(TLS_SLOT_SELF) = &tcb->tls_slot(TLS_SLOT_SELF);
+#endif
+ tcb->tls_slot(TLS_SLOT_THREAD_ID) = thread;
+}
+
+void __init_tcb_dtv(bionic_tcb* tcb) {
+ // Initialize the DTV slot to a statically-allocated empty DTV. The first
+ // access to a dynamic TLS variable allocates a new DTV.
+ static const TlsDtv zero_dtv = {};
+ __set_tcb_dtv(tcb, const_cast<TlsDtv*>(&zero_dtv));
+}
+
// Finish initializing the main thread.
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
extern "C" void __libc_init_main_thread_late() {
diff --git a/libc/bionic/bionic_call_ifunc_resolver.cpp b/libc/bionic/bionic_call_ifunc_resolver.cpp
index 85228359e..437de78ce 100644
--- a/libc/bionic/bionic_call_ifunc_resolver.cpp
+++ b/libc/bionic/bionic_call_ifunc_resolver.cpp
@@ -30,14 +30,32 @@
#include <sys/auxv.h>
#include <sys/ifunc.h>
+#include "private/bionic_auxv.h"
+
+// This code is called in the linker before it has been relocated, so minimize calls into other
+// parts of Bionic. In particular, we won't ever have two ifunc resolvers called concurrently, so
+// initializing the ifunc resolver argument doesn't need to be thread-safe.
+
ElfW(Addr) __bionic_call_ifunc_resolver(ElfW(Addr) resolver_addr) {
#if defined(__aarch64__)
typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, __ifunc_arg_t*);
- static __ifunc_arg_t arg = { sizeof(__ifunc_arg_t), getauxval(AT_HWCAP), getauxval(AT_HWCAP2) };
+ static __ifunc_arg_t arg;
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ arg._size = sizeof(__ifunc_arg_t);
+ arg._hwcap = getauxval(AT_HWCAP);
+ arg._hwcap2 = getauxval(AT_HWCAP2);
+ }
return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(arg._hwcap | _IFUNC_ARG_HWCAP, &arg);
#elif defined(__arm__)
typedef ElfW(Addr) (*ifunc_resolver_t)(unsigned long);
- static unsigned long hwcap = getauxval(AT_HWCAP);
+ static unsigned long hwcap;
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ hwcap = getauxval(AT_HWCAP);
+ }
return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(hwcap);
#else
typedef ElfW(Addr) (*ifunc_resolver_t)(void);
diff --git a/libc/bionic/bionic_netlink.cpp b/libc/bionic/bionic_netlink.cpp
index f2449dc9a..5d5a02624 100644
--- a/libc/bionic/bionic_netlink.cpp
+++ b/libc/bionic/bionic_netlink.cpp
@@ -39,8 +39,6 @@
#include "private/ErrnoRestorer.h"
NetlinkConnection::NetlinkConnection() {
- fd_ = -1;
-
// The kernel keeps packets under 8KiB (NLMSG_GOODSIZE),
// but that's a bit too large to go on the stack.
size_ = 8192;
@@ -48,8 +46,6 @@ NetlinkConnection::NetlinkConnection() {
}
NetlinkConnection::~NetlinkConnection() {
- ErrnoRestorer errno_restorer;
- if (fd_ != -1) close(fd_);
delete[] data_;
}
@@ -59,9 +55,9 @@ bool NetlinkConnection::SendRequest(int type) {
if (data_ == nullptr) return false;
// Did we open a netlink socket yet?
- if (fd_ == -1) {
- fd_ = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
- if (fd_ == -1) return false;
+ if (fd_.get() == -1) {
+ fd_.reset(socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE));
+ if (fd_.get() == -1) return false;
}
// Construct and send the message.
@@ -74,13 +70,13 @@ bool NetlinkConnection::SendRequest(int type) {
request.hdr.nlmsg_type = type;
request.hdr.nlmsg_len = sizeof(request);
request.msg.rtgen_family = AF_UNSPEC; // All families.
- return (TEMP_FAILURE_RETRY(send(fd_, &request, sizeof(request), 0)) == sizeof(request));
+ return (TEMP_FAILURE_RETRY(send(fd_.get(), &request, sizeof(request), 0)) == sizeof(request));
}
bool NetlinkConnection::ReadResponses(void callback(void*, nlmsghdr*), void* context) {
// Read through all the responses, handing interesting ones to the callback.
ssize_t bytes_read;
- while ((bytes_read = TEMP_FAILURE_RETRY(recv(fd_, data_, size_, 0))) > 0) {
+ while ((bytes_read = TEMP_FAILURE_RETRY(recv(fd_.get(), data_, size_, 0))) > 0) {
nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(data_);
for (; NLMSG_OK(hdr, static_cast<size_t>(bytes_read)); hdr = NLMSG_NEXT(hdr, bytes_read)) {
if (hdr->nlmsg_type == NLMSG_DONE) return true;
diff --git a/libc/bionic/bionic_netlink.h b/libc/bionic/bionic_netlink.h
index a21200e64..fc1bd0fb0 100644
--- a/libc/bionic/bionic_netlink.h
+++ b/libc/bionic/bionic_netlink.h
@@ -33,6 +33,8 @@
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include "private/ScopedFd.h"
+
struct nlmsghdr;
class NetlinkConnection {
@@ -44,7 +46,7 @@ class NetlinkConnection {
bool ReadResponses(void callback(void*, nlmsghdr*), void* context);
private:
- int fd_;
+ ScopedFd fd_;
char* data_;
size_t size_;
};
diff --git a/libc/bionic/fchmodat.cpp b/libc/bionic/fchmodat.cpp
index c28e15ac7..632d2ac85 100644
--- a/libc/bionic/fchmodat.cpp
+++ b/libc/bionic/fchmodat.cpp
@@ -32,7 +32,7 @@
#include <errno.h>
#include <unistd.h>
-#include "private/ErrnoRestorer.h"
+#include "private/ScopedFd.h"
extern "C" int __fchmodat(int, const char*, mode_t);
@@ -47,20 +47,15 @@ int fchmodat(int dirfd, const char* pathname, mode_t mode, int flags) {
// at https://sourceware.org/bugzilla/show_bug.cgi?id=14578
// comment #10
- int fd = openat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC);
- if (fd == -1) {
- return -1; // returns errno from openat
- }
+ ScopedFd fd(openat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC));
+ if (fd.get() == -1) return -1;
// POSIX requires that ENOTSUP be returned when the system
// doesn't support setting the mode of a symbolic link.
// This is true for all Linux kernels.
// We rely on the O_PATH compatibility layer added in the
// fchmod() function to get errno correct.
- int result = fchmod(fd, mode);
- ErrnoRestorer errno_restorer; // don't let close() clobber errno
- close(fd);
- return result;
+ return fchmod(fd.get(), mode);
}
return __fchmodat(dirfd, pathname, mode);
diff --git a/libc/bionic/getauxval.cpp b/libc/bionic/getauxval.cpp
index c8f867b64..f865f97b4 100644
--- a/libc/bionic/getauxval.cpp
+++ b/libc/bionic/getauxval.cpp
@@ -36,7 +36,6 @@
// This function needs to be safe to call before TLS is set up, so it can't
// access errno or the stack protector.
-__attribute__((no_stack_protector))
__LIBC_HIDDEN__ unsigned long __bionic_getauxval(unsigned long type, bool& exists) {
for (ElfW(auxv_t)* v = __libc_shared_globals()->auxv; v->a_type != AT_NULL; ++v) {
if (v->a_type == type) {
diff --git a/libc/bionic/getentropy.cpp b/libc/bionic/getentropy.cpp
index 2c6e417b8..9c93e713b 100644
--- a/libc/bionic/getentropy.cpp
+++ b/libc/bionic/getentropy.cpp
@@ -31,22 +31,20 @@
#include <sys/random.h>
#include <unistd.h>
+#include "private/ScopedFd.h"
+
static int getentropy_urandom(void* buffer, size_t buffer_size, int saved_errno) {
- int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_NOFOLLOW | O_CLOEXEC, 0));
- if (fd == -1) return -1;
+ ScopedFd fd(TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_NOFOLLOW | O_CLOEXEC, 0)));
+ if (fd.get() == -1) return -1;
size_t collected = 0;
while (collected < buffer_size) {
- ssize_t count = TEMP_FAILURE_RETRY(read(fd, static_cast<char*>(buffer) + collected,
+ ssize_t count = TEMP_FAILURE_RETRY(read(fd.get(), static_cast<char*>(buffer) + collected,
buffer_size - collected));
- if (count == -1) {
- close(fd);
- return -1;
- }
+ if (count == -1) return -1;
collected += count;
}
- close(fd);
errno = saved_errno;
return 0;
}
diff --git a/libc/bionic/grp_pwd_file.cpp b/libc/bionic/grp_pwd_file.cpp
index e13604e68..81cf8936d 100644
--- a/libc/bionic/grp_pwd_file.cpp
+++ b/libc/bionic/grp_pwd_file.cpp
@@ -37,6 +37,7 @@
#include <async_safe/log.h>
#include "private/ErrnoRestorer.h"
+#include "private/ScopedFd.h"
// This file mmap's /*/etc/passwd and /*/etc/group in order to return their contents without any
// allocations. Note that these files and the strings contained within them are explicitly not
@@ -230,19 +231,16 @@ bool MmapFile::GetFile(const char** start, const char** end) {
}
bool MmapFile::DoMmap() {
- int fd = open(filename_, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
+ ScopedFd fd(open(filename_, O_CLOEXEC | O_NOFOLLOW | O_RDONLY));
struct stat fd_stat;
- if (fstat(fd, &fd_stat) == -1) {
- close(fd);
+ if (fstat(fd.get(), &fd_stat) == -1) {
return false;
}
auto mmap_size = fd_stat.st_size;
- void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
- close(fd);
-
+ void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd.get(), 0);
if (map_result == MAP_FAILED) {
return false;
}
diff --git a/libc/bionic/jemalloc.h b/libc/bionic/jemalloc.h
index ef77c9c99..4ce51c0a8 100644
--- a/libc/bionic/jemalloc.h
+++ b/libc/bionic/jemalloc.h
@@ -29,7 +29,7 @@
__BEGIN_DECLS
void* je_aligned_alloc_wrapper(size_t, size_t);
-int je_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
+int je_malloc_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
int je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) __attribute__((nothrow));
struct mallinfo je_mallinfo();
void je_malloc_disable();
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index 7d0445784..ef488eecc 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -140,30 +140,32 @@ int je_malloc_info(int options, FILE* fp) {
return -1;
}
- MallocXmlElem root(fp, "malloc", "version=\"jemalloc-1\"");
+ fflush(fp);
+ int fd = fileno(fp);
+ MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\"");
// Dump all of the large allocations in the arenas.
for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
struct mallinfo mi = je_mallinfo_arena_info(i);
if (mi.hblkhd != 0) {
- MallocXmlElem arena_elem(fp, "heap", "nr=\"%d\"", i);
+ MallocXmlElem arena_elem(fd, "heap", "nr=\"%d\"", i);
{
- MallocXmlElem(fp, "allocated-large").Contents("%zu", mi.ordblks);
- MallocXmlElem(fp, "allocated-huge").Contents("%zu", mi.uordblks);
- MallocXmlElem(fp, "allocated-bins").Contents("%zu", mi.fsmblks);
+ MallocXmlElem(fd, "allocated-large").Contents("%zu", mi.ordblks);
+ MallocXmlElem(fd, "allocated-huge").Contents("%zu", mi.uordblks);
+ MallocXmlElem(fd, "allocated-bins").Contents("%zu", mi.fsmblks);
size_t total = 0;
for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
struct mallinfo mi = je_mallinfo_bin_info(i, j);
if (mi.ordblks != 0) {
- MallocXmlElem bin_elem(fp, "bin", "nr=\"%d\"", j);
- MallocXmlElem(fp, "allocated").Contents("%zu", mi.ordblks);
- MallocXmlElem(fp, "nmalloc").Contents("%zu", mi.uordblks);
- MallocXmlElem(fp, "ndalloc").Contents("%zu", mi.fordblks);
+ MallocXmlElem bin_elem(fd, "bin", "nr=\"%d\"", j);
+ MallocXmlElem(fd, "allocated").Contents("%zu", mi.ordblks);
+ MallocXmlElem(fd, "nmalloc").Contents("%zu", mi.uordblks);
+ MallocXmlElem(fd, "ndalloc").Contents("%zu", mi.fordblks);
total += mi.ordblks;
}
}
- MallocXmlElem(fp, "bins-total").Contents("%zu", total);
+ MallocXmlElem(fd, "bins-total").Contents("%zu", total);
}
}
}
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 0b740232a..28c0b0c95 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -231,6 +231,9 @@ extern "C" void android_set_application_target_sdk_version(int target) {
g_target_sdk_version = target;
}
+// This function is called in the dynamic linker before ifunc resolvers have run, so this file is
+// compiled with -ffreestanding to avoid implicit string.h function calls. (It shouldn't strictly
+// be necessary, though.)
__LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals() {
static libc_shared_globals globals;
return &globals;
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 96e614048..a0da3dbff 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -215,9 +215,9 @@ extern "C" int malloc_iterate(uintptr_t base, size_t size,
void (*callback)(uintptr_t base, size_t size, void* arg), void* arg) {
auto dispatch_table = GetDispatchTable();
if (__predict_false(dispatch_table != nullptr)) {
- return dispatch_table->iterate(base, size, callback, arg);
+ return dispatch_table->malloc_iterate(base, size, callback, arg);
}
- return Malloc(iterate)(base, size, callback, arg);
+ return Malloc(malloc_iterate)(base, size, callback, arg);
}
// Disable calls to malloc so malloc_iterate gets a consistent view of
@@ -247,9 +247,10 @@ extern "C" ssize_t malloc_backtrace(void*, uintptr_t*, size_t) {
#if __has_feature(hwaddress_sanitizer)
// FIXME: implement these in HWASan allocator.
-extern "C" int __sanitizer_iterate(uintptr_t base __unused, size_t size __unused,
- void (*callback)(uintptr_t base, size_t size, void* arg) __unused,
- void* arg __unused) {
+extern "C" int __sanitizer_malloc_iterate(uintptr_t base __unused, size_t size __unused,
+ void (*callback)(uintptr_t base, size_t size, void* arg)
+ __unused,
+ void* arg __unused) {
return 0;
}
diff --git a/libc/bionic/malloc_common.h b/libc/bionic/malloc_common.h
index 2176e634d..89dccc31a 100644
--- a/libc/bionic/malloc_common.h
+++ b/libc/bionic/malloc_common.h
@@ -42,9 +42,9 @@
__BEGIN_DECLS
// FIXME: implement these in HWASan allocator.
-int __sanitizer_iterate(uintptr_t base, size_t size,
- void (*callback)(uintptr_t base, size_t size, void* arg),
- void* arg);
+int __sanitizer_malloc_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg),
+ void* arg);
void __sanitizer_malloc_disable();
void __sanitizer_malloc_enable();
int __sanitizer_malloc_info(int options, FILE* fp);
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
index 9ad79a0d0..0ac3f62e6 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -95,7 +95,7 @@ static constexpr MallocDispatch __libc_malloc_default_dispatch
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
Malloc(valloc),
#endif
- Malloc(iterate),
+ Malloc(malloc_iterate),
Malloc(malloc_disable),
Malloc(malloc_enable),
Malloc(mallopt),
@@ -184,7 +184,8 @@ static bool InitMallocFunctions(void* impl_handler, MallocDispatch* table, const
if (!InitMallocFunction<MallocRealloc>(impl_handler, &table->realloc, prefix, "realloc")) {
return false;
}
- if (!InitMallocFunction<MallocIterate>(impl_handler, &table->iterate, prefix, "iterate")) {
+ if (!InitMallocFunction<MallocIterate>(impl_handler, &table->malloc_iterate, prefix,
+ "malloc_iterate")) {
return false;
}
if (!InitMallocFunction<MallocMallocDisable>(impl_handler, &table->malloc_disable, prefix,
diff --git a/libc/bionic/malloc_heapprofd.cpp b/libc/bionic/malloc_heapprofd.cpp
index 62249fb1d..586022200 100644
--- a/libc/bionic/malloc_heapprofd.cpp
+++ b/libc/bionic/malloc_heapprofd.cpp
@@ -104,7 +104,7 @@ static constexpr MallocDispatch __heapprofd_init_dispatch
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
Malloc(valloc),
#endif
- Malloc(iterate),
+ Malloc(malloc_iterate),
Malloc(malloc_disable),
Malloc(malloc_enable),
Malloc(mallopt),
diff --git a/libc/bionic/malloc_limit.cpp b/libc/bionic/malloc_limit.cpp
index 6a67cae95..d8c0ebe54 100644
--- a/libc/bionic/malloc_limit.cpp
+++ b/libc/bionic/malloc_limit.cpp
@@ -350,9 +350,9 @@ static struct mallinfo LimitMallinfo() {
static int LimitIterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*), void* arg) {
auto dispatch_table = GetDefaultDispatchTable();
if (__predict_false(dispatch_table != nullptr)) {
- return dispatch_table->iterate(base, size, callback, arg);
+ return dispatch_table->malloc_iterate(base, size, callback, arg);
}
- return Malloc(iterate)(base, size, callback, arg);
+ return Malloc(malloc_iterate)(base, size, callback, arg);
}
static void LimitMallocDisable() {
diff --git a/libc/bionic/net_if.cpp b/libc/bionic/net_if.cpp
index db9c9ea22..ad53364c9 100644
--- a/libc/bionic/net_if.cpp
+++ b/libc/bionic/net_if.cpp
@@ -40,37 +40,27 @@
#include <sys/socket.h>
#include <unistd.h>
-#include "private/ErrnoRestorer.h"
+#include "private/ScopedFd.h"
#include "bionic_netlink.h"
char* if_indextoname(unsigned ifindex, char* ifname) {
- int s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
- if (s == -1) return nullptr;
+ ScopedFd s(socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0));
+ if (s.get() == -1) return nullptr;
- struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = ifindex;
-
- int rc = ioctl(s, SIOCGIFNAME, &ifr);
- ErrnoRestorer errno_restorer;
- close(s);
- return (rc == -1) ? nullptr : strncpy(ifname, ifr.ifr_name, IFNAMSIZ);
+ ifreq ifr = {.ifr_ifindex = static_cast<int>(ifindex)};
+ return (ioctl(s.get(), SIOCGIFNAME, &ifr) == -1) ? nullptr
+ : strncpy(ifname, ifr.ifr_name, IFNAMSIZ);
}
unsigned if_nametoindex(const char* ifname) {
- int s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
- if (s == -1) return 0;
+ ScopedFd s(socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0));
+ if (s.get() == -1) return 0;
- struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
+ ifreq ifr = {};
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_name[IFNAMSIZ - 1] = 0;
-
- int rc = ioctl(s, SIOCGIFINDEX, &ifr);
- ErrnoRestorer errno_restorer;
- close(s);
- return (rc == -1) ? 0 : ifr.ifr_ifindex;
+ return (ioctl(s.get(), SIOCGIFINDEX, &ifr) == -1) ? 0 : ifr.ifr_ifindex;
}
struct if_list {
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 1dc10667b..03af2d94a 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -54,31 +54,12 @@
void __init_user_desc(struct user_desc*, bool, void*);
#endif
-// This code is used both by each new pthread and the code that initializes the main thread.
-__attribute__((no_stack_protector))
-void __init_tcb(bionic_tcb* tcb, pthread_internal_t* thread) {
-#ifdef TLS_SLOT_SELF
- // On x86, slot 0 must point to itself so code can read the thread pointer by
- // loading %fs:0 or %gs:0.
- tcb->tls_slot(TLS_SLOT_SELF) = &tcb->tls_slot(TLS_SLOT_SELF);
-#endif
- tcb->tls_slot(TLS_SLOT_THREAD_ID) = thread;
-}
-
__attribute__((no_stack_protector))
void __init_tcb_stack_guard(bionic_tcb* tcb) {
// GCC looks in the TLS for the stack guard on x86, so copy it there from our global.
tcb->tls_slot(TLS_SLOT_STACK_GUARD) = reinterpret_cast<void*>(__stack_chk_guard);
}
-__attribute__((no_stack_protector))
-void __init_tcb_dtv(bionic_tcb* tcb) {
- // Initialize the DTV slot to a statically-allocated empty DTV. The first
- // access to a dynamic TLS variable allocates a new DTV.
- static const TlsDtv zero_dtv = {};
- __set_tcb_dtv(tcb, const_cast<TlsDtv*>(&zero_dtv));
-}
-
void __init_bionic_tls_ptrs(bionic_tcb* tcb, bionic_tls* tls) {
tcb->thread()->bionic_tls = tls;
tcb->tls_slot(TLS_SLOT_BIONIC_TLS) = tls;
diff --git a/libc/bionic/scudo.h b/libc/bionic/scudo.h
index d9933c4d0..a80d75418 100644
--- a/libc/bionic/scudo.h
+++ b/libc/bionic/scudo.h
@@ -52,7 +52,7 @@ void* scudo_pvalloc(size_t);
void* scudo_valloc(size_t);
#endif
-int scudo_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
+int scudo_malloc_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
void scudo_malloc_disable();
void scudo_malloc_enable();
diff --git a/libc/bionic/scudo_wrapper.cpp b/libc/bionic/scudo_wrapper.cpp
index e17f20ba5..debd8d996 100644
--- a/libc/bionic/scudo_wrapper.cpp
+++ b/libc/bionic/scudo_wrapper.cpp
@@ -48,7 +48,7 @@ int scudo_malloc_info(int /*options*/, FILE* /*fp*/) {
return -1;
}
-int scudo_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*) {
+int scudo_malloc_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*) {
return 0;
}
diff --git a/libc/bionic/system_property_api.cpp b/libc/bionic/system_property_api.cpp
index 051bc4c4f..a641f12a8 100644
--- a/libc/bionic/system_property_api.cpp
+++ b/libc/bionic/system_property_api.cpp
@@ -100,7 +100,13 @@ int __system_property_add(const char* name, unsigned int namelen, const char* va
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
uint32_t __system_property_serial(const prop_info* pi) {
- return system_properties.Serial(pi);
+ // N.B. a previous version of this function was much heavier-weight
+ // and enforced acquire semantics, so give our load here acquire
+ // semantics just in case somebody depends on
+ // __system_property_serial enforcing memory order, e.g., in case
+ // someone spins on the result of this function changing before
+ // loading some value.
+ return atomic_load_explicit(&pi->serial, memory_order_acquire);
}
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
diff --git a/libc/bionic/system_property_set.cpp b/libc/bionic/system_property_set.cpp
index c508db162..e981a58d9 100644
--- a/libc/bionic/system_property_set.cpp
+++ b/libc/bionic/system_property_set.cpp
@@ -46,6 +46,7 @@
#include "private/bionic_defs.h"
#include "private/bionic_macros.h"
+#include "private/ScopedFd.h"
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
static const char* kServiceVersionPropertyName = "ro.property_service.version";
@@ -53,8 +54,8 @@ static const char* kServiceVersionPropertyName = "ro.property_service.version";
class PropertyServiceConnection {
public:
PropertyServiceConnection() : last_error_(0) {
- socket_ = ::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
- if (socket_ == -1) {
+ socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ if (socket_.get() == -1) {
last_error_ = errno;
return;
}
@@ -66,15 +67,15 @@ class PropertyServiceConnection {
addr.sun_family = AF_LOCAL;
socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
- if (TEMP_FAILURE_RETRY(connect(socket_, reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
+ if (TEMP_FAILURE_RETRY(connect(socket_.get(),
+ reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
last_error_ = errno;
- close(socket_);
- socket_ = -1;
+ socket_.reset();
}
}
bool IsValid() {
- return socket_ != -1;
+ return socket_.get() != -1;
}
int GetLastError() {
@@ -82,18 +83,12 @@ class PropertyServiceConnection {
}
bool RecvInt32(int32_t* value) {
- int result = TEMP_FAILURE_RETRY(recv(socket_, value, sizeof(*value), MSG_WAITALL));
+ int result = TEMP_FAILURE_RETRY(recv(socket_.get(), value, sizeof(*value), MSG_WAITALL));
return CheckSendRecvResult(result, sizeof(*value));
}
int socket() {
- return socket_;
- }
-
- ~PropertyServiceConnection() {
- if (socket_ != -1) {
- close(socket_);
- }
+ return socket_.get();
}
private:
@@ -109,7 +104,7 @@ class PropertyServiceConnection {
return last_error_ == 0;
}
- int socket_;
+ ScopedFd socket_;
int last_error_;
friend class SocketWriter;
diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c
index 4e1aa61d5..d0c11d2b0 100644
--- a/libc/dns/net/getaddrinfo.c
+++ b/libc/dns/net/getaddrinfo.c
@@ -470,7 +470,7 @@ android_getaddrinfo_proxy(
break;
}
- struct addrinfo* ai = calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+ ai = calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
if (ai == NULL) {
break;
}
diff --git a/libc/include/bits/sys_statvfs_inlines.h b/libc/include/bits/sys_statvfs_inlines.h
index fd4578cd5..991fac716 100644
--- a/libc/include/bits/sys_statvfs_inlines.h
+++ b/libc/include/bits/sys_statvfs_inlines.h
@@ -38,20 +38,20 @@ __BEGIN_DECLS
#if defined(__BIONIC_NEED_STATVFS_INLINES)
-static __inline void __bionic_statfs_to_statvfs(const struct statfs* __in,
- struct statvfs* __out) {
- __out->f_bsize = __in->f_bsize;
- __out->f_frsize = __in->f_frsize;
- __out->f_blocks = __in->f_blocks;
- __out->f_bfree = __in->f_bfree;
- __out->f_bavail = __in->f_bavail;
- __out->f_files = __in->f_files;
- __out->f_ffree = __in->f_ffree;
- __out->f_favail = __in->f_ffree;
- __out->f_fsid = __in->f_fsid.__val[0] |
- __BIONIC_CAST(static_cast, uint64_t, __in->f_fsid.__val[1]) << 32;
- __out->f_flag = __in->f_flags;
- __out->f_namemax = __in->f_namelen;
+static __inline void __bionic_statfs_to_statvfs(const struct statfs* __src,
+ struct statvfs* __dst) {
+ __dst->f_bsize = __src->f_bsize;
+ __dst->f_frsize = __src->f_frsize;
+ __dst->f_blocks = __src->f_blocks;
+ __dst->f_bfree = __src->f_bfree;
+ __dst->f_bavail = __src->f_bavail;
+ __dst->f_files = __src->f_files;
+ __dst->f_ffree = __src->f_ffree;
+ __dst->f_favail = __src->f_ffree;
+ __dst->f_fsid = __src->f_fsid.__val[0] |
+ __BIONIC_CAST(static_cast, uint64_t, __src->f_fsid.__val[1]) << 32;
+ __dst->f_flag = __src->f_flags;
+ __dst->f_namemax = __src->f_namelen;
}
__BIONIC_SYS_STATVFS_INLINE int statvfs(const char* __path,
diff --git a/libc/include/fcntl.h b/libc/include/fcntl.h
index 1c5d64c19..c45c91ff4 100644
--- a/libc/include/fcntl.h
+++ b/libc/include/fcntl.h
@@ -26,9 +26,14 @@
* SUCH DAMAGE.
*/
-#ifndef _FCNTL_H
+#pragma once
#define _FCNTL_H
+/**
+ * @file fcntl.h
+ * @brief File control operations.
+ */
+
#include <sys/cdefs.h>
#include <sys/types.h>
#include <linux/fadvise.h>
@@ -48,47 +53,180 @@ __BEGIN_DECLS
#ifdef __LP64__
/* LP64 kernels don't have F_*64 defines because their flock is 64-bit. */
+/** Flag for flock(). */
#define F_GETLK64 F_GETLK
+/** Flag for flock(). */
#define F_SETLK64 F_SETLK
+/** Flag for flock(). */
#define F_SETLKW64 F_SETLKW
#endif
+/** Flag for open(). */
#define O_ASYNC FASYNC
+/** Flag for open(). */
#define O_RSYNC O_SYNC
#if __ANDROID_API__ >= __ANDROID_API_L__
+/** Flag for splice(). */
#define SPLICE_F_MOVE 1
+/** Flag for splice(). */
#define SPLICE_F_NONBLOCK 2
+/** Flag for splice(). */
#define SPLICE_F_MORE 4
+/** Flag for splice(). */
#define SPLICE_F_GIFT 8
#endif
#if __ANDROID_API__ >= __ANDROID_API_O__
+/** Flag for sync_file_range(). */
#define SYNC_FILE_RANGE_WAIT_BEFORE 1
+/** Flag for sync_file_range(). */
#define SYNC_FILE_RANGE_WRITE 2
+/** Flag for sync_file_range(). */
#define SYNC_FILE_RANGE_WAIT_AFTER 4
#endif
+/**
+ * [creat(2)](http://man7.org/linux/man-pages/man2/creat.2.html)
+ * creates a file.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
int creat(const char* __path, mode_t __mode);
+/** See creat(). */
int creat64(const char* __path, mode_t __mode) __INTRODUCED_IN(21);
+
+/**
+ * [openat(2)](http://man7.org/linux/man-pages/man2/openat.2.html)
+ * opens (and possibly creates) a file.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
int openat(int __dir_fd, const char* __path, int __flags, ...);
+/** See openat(). */
int openat64(int __dir_fd, const char* __path, int __flags, ...) __INTRODUCED_IN(21);
+
+/**
+ * [open(2)](http://man7.org/linux/man-pages/man2/open.2.html)
+ * opens (and possibly creates) a file.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
int open(const char* __path, int __flags, ...);
+/** See open(). */
int open64(const char* __path, int __flags, ...) __INTRODUCED_IN(21);
+
+/**
+ * [splice(2)](http://man7.org/linux/man-pages/man2/splice.2.html)
+ * splices data to/from a pipe.
+ *
+ * Valid flags are `SPLICE_F_MOVE`, `SPLICE_F_NONBLOCK`, `SPLICE_F_MORE`, and
+ * `SPLICE_F_GIFT`.
+ *
+ * Returns the number of bytes spliced on success and returns -1 and sets
+ * `errno` on failure.
+ *
+ * Available since API level 21.
+ */
ssize_t splice(int __in_fd, off64_t* __in_offset, int __out_fd, off64_t* __out_offset, size_t __length, unsigned int __flags) __INTRODUCED_IN(21);
+
+/**
+ * [tee(2)](http://man7.org/linux/man-pages/man2/tee.2.html)
+ * duplicates data from one pipe to another.
+ *
+ * Valid flags are `SPLICE_F_MOVE`, `SPLICE_F_NONBLOCK`, `SPLICE_F_MORE`, and
+ * `SPLICE_F_GIFT`.
+ *
+ * Returns the number of bytes duplicated on success and returns -1 and sets
+ * `errno` on failure.
+ *
+ * Available since API level 21.
+ */
ssize_t tee(int __in_fd, int __out_fd, size_t __length, unsigned int __flags) __INTRODUCED_IN(21);
+
+/**
+ * [vmsplice(2)](http://man7.org/linux/man-pages/man2/vmsplice.2.html)
+ * splices data to/from a pipe.
+ *
+ * Valid flags are `SPLICE_F_MOVE`, `SPLICE_F_NONBLOCK`, `SPLICE_F_MORE`, and
+ * `SPLICE_F_GIFT`.
+ *
+ * Returns the number of bytes spliced on success and returns -1 and sets
+ * `errno` on failure.
+ *
+ * Available since API level 21.
+ */
ssize_t vmsplice(int __fd, const struct iovec* __iov, size_t __count, unsigned int __flags) __INTRODUCED_IN(21);
+/**
+ * [fallocate(2)](http://man7.org/linux/man-pages/man2/fallocate.2.html)
+ * is a Linux-specific extension of posix_fallocate().
+ *
+ * Valid flags are `FALLOC_FL_KEEP_SIZE`, `FALLOC_FL_PUNCH_HOLE`,
+ * `FALLOC_FL_NO_HIDE_STALE`, `FALLOC_FL_COLLAPSE_RANGE`,
+ * `FALLOC_FL_ZERO_RANGE`, `FALLOC_FL_INSERT_RANGE`, and
+ * `FALLOC_FL_UNSHARE_RANGE`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ *
+ * Available since API level 21.
+ */
int fallocate(int __fd, int __mode, off_t __offset, off_t __length) __RENAME_IF_FILE_OFFSET64(fallocate64) __INTRODUCED_IN(21);
+/** See fallocate(). */
int fallocate64(int __fd, int __mode, off64_t __offset, off64_t __length) __INTRODUCED_IN(21);
+
+/**
+ * [posix_fadvise(2)](http://man7.org/linux/man-pages/man2/posix_fadvise.2.html)
+ * declares an expected access pattern for file data.
+ *
+ * Valid flags are `POSIX_FADV_NORMAL`, `POSIX_FADV_RANDOM`,
+ * `POSIX_FADV_SEQUENTIAL`, `POSIX_FADV_WILLNEED`, `POSIX_FADV_DONTNEED`,
+ * and `POSIX_FADV_NOREUSE`.
+ *
+ * Returns 0 on success and returns an error number on failure.
+ *
+ * Available since API level 21.
+ */
int posix_fadvise(int __fd, off_t __offset, off_t __length, int __advice) __RENAME_IF_FILE_OFFSET64(posix_fadvise64) __INTRODUCED_IN(21);
+/** See posix_fadvise(). */
int posix_fadvise64(int __fd, off64_t __offset, off64_t __length, int __advice) __INTRODUCED_IN(21);
+
+/**
+ * [posix_fallocate(2)](http://man7.org/linux/man-pages/man2/posix_fallocate.2.html)
+ * allocates file space.
+ *
+ * Returns 0 on success and returns an error number on failure.
+ *
+ * Available since API level 21.
+ */
int posix_fallocate(int __fd, off_t __offset, off_t __length) __RENAME_IF_FILE_OFFSET64(posix_fallocate64) __INTRODUCED_IN(21);
+/** See posix_fallocate(). */
int posix_fallocate64(int __fd, off64_t __offset, off64_t __length) __INTRODUCED_IN(21);
#if defined(__USE_GNU)
-ssize_t readahead(int __fd, off64_t __offset, size_t __length) __INTRODUCED_IN(16);
+
+/**
+ * [readahead(2)](http://man7.org/linux/man-pages/man2/readahead.2.html)
+ * initiates readahead for the given file.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+ssize_t readahead(int __fd, off64_t __offset, size_t __length);
+
+/**
+ * [sync_file_range(2)](http://man7.org/linux/man-pages/man2/sync_file_range.2.html)
+ * syncs part of a file with disk.
+ *
+ * Valid flags are `SYNC_FILE_RANGE_WAIT_BEFORE`, `SYNC_FILE_RANGE_WRITE`, and
+ * `SYNC_FILE_RANGE_WAIT_AFTER`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
int sync_file_range(int __fd, off64_t __offset, off64_t __length, unsigned int __flags) __INTRODUCED_IN(26);
+
#endif
#if defined(__BIONIC_INCLUDE_FORTIFY_HEADERS)
@@ -96,5 +234,3 @@ int sync_file_range(int __fd, off64_t __offset, off64_t __length, unsigned int _
#endif
__END_DECLS
-
-#endif
diff --git a/libc/include/search.h b/libc/include/search.h
index 97fdedaeb..7a75404c9 100644
--- a/libc/include/search.h
+++ b/libc/include/search.h
@@ -6,30 +6,56 @@
* $FreeBSD: release/9.0.0/include/search.h 105250 2002-10-16 14:29:23Z robert $
*/
-#ifndef _SEARCH_H_
-#define _SEARCH_H_
+#pragma once
+
+/**
+ * @file search.h
+ * @brief Queues, hash tables, trees, and linear array searches.
+ */
#include <sys/cdefs.h>
#include <sys/types.h>
+/** See hsearch()/hsearch_r(). */
typedef enum {
FIND,
ENTER
} ACTION;
+/** See hsearch()/hsearch_r(). */
typedef struct entry {
+ /** The string key. */
char* key;
+ /** The associated data. */
void* data;
} ENTRY;
+/**
+ * Constants given to the twalk() visitor.
+ * Note that the constant names are misleading.
+ */
typedef enum {
+ /**
+ * If this is the first visit to a non-leaf node.
+ * Use this for *preorder* traversal.
+ */
preorder,
+ /**
+ * If this is the second visit to a non-leaf node.
+ * Use this for *inorder* traversal.
+ */
postorder,
+ /**
+ * If this is the third visit to a non-leaf node.
+ * Use this for *postorder* traversal.
+ */
endorder,
+ /** If this is the first and only visit to a leaf node. */
leaf
} VISIT;
#if defined(__USE_BSD) || defined(__USE_GNU)
+/** The hash table type for hcreate_r()/hdestroy_r()/hsearch_r(). */
struct hsearch_data {
struct __hsearch* __hsearch;
};
@@ -37,30 +63,157 @@ struct hsearch_data {
__BEGIN_DECLS
+/**
+ * [insque(3)](http://man7.org/linux/man-pages/man3/insque.3.html) inserts
+ * an item in a queue (an intrusive doubly-linked list).
+ *
+ * Available since API level 21.
+ */
void insque(void* __element, void* __previous) __INTRODUCED_IN(21);
+
+/**
+ * [remque(3)](http://man7.org/linux/man-pages/man3/remque.3.html) removes
+ * an item from a queue (an intrusive doubly-linked list).
+ *
+ * Available since API level 21.
+ */
void remque(void* __element) __INTRODUCED_IN(21);
-int hcreate(size_t) __INTRODUCED_IN(28);
+/**
+ * [hcreate(3)](http://man7.org/linux/man-pages/man3/hcreate.3.html)
+ * initializes the global hash table, with space for at least `__n` elements.
+ *
+ * See hcreate_r() if you need more than one hash table.
+ *
+ * Returns *non-zero* on success and returns 0 and sets `errno` on failure.
+ *
+ * Available since API level 28.
+ */
+int hcreate(size_t __n) __INTRODUCED_IN(28);
+
+/**
+ * [hdestroy(3)](http://man7.org/linux/man-pages/man3/hdestroy.3.html) destroys
+ * the global hash table.
+ *
+ * See hdestroy_r() if you need more than one hash table.
+ *
+ * Available since API level 28.
+ */
void hdestroy(void) __INTRODUCED_IN(28);
-ENTRY* hsearch(ENTRY, ACTION) __INTRODUCED_IN(28);
+
+/**
+ * [hsearch(3)](http://man7.org/linux/man-pages/man3/hsearch.3.html) finds or
+ * inserts `__entry` in the global hash table, based on `__action`.
+ *
+ * See hsearch_r() if you need more than one hash table.
+ *
+ * Returns a pointer to the entry on success, and returns NULL and sets
+ * `errno` on failure.
+ *
+ * Available since API level 28.
+ */
+ENTRY* hsearch(ENTRY __entry, ACTION __action) __INTRODUCED_IN(28);
#if defined(__USE_BSD) || defined(__USE_GNU)
-int hcreate_r(size_t, struct hsearch_data*) __INTRODUCED_IN(28);
-void hdestroy_r(struct hsearch_data*) __INTRODUCED_IN(28);
-int hsearch_r(ENTRY, ACTION, ENTRY**, struct hsearch_data*) __INTRODUCED_IN(28);
-#endif
-void* lfind(const void* __key, const void* __base, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*))
- __INTRODUCED_IN(21);
-void* lsearch(const void* __key, void* __base, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*))
- __INTRODUCED_IN(21);
+/**
+ * [hcreate_r(3)](http://man7.org/linux/man-pages/man3/hcreate_r.3.html)
+ * initializes a hash table `__table` with space for at least `__n` elements.
+ *
+ * Returns *non-zero* on success and returns 0 and sets `errno` on failure.
+ *
+ * Available since API level 28.
+ */
+int hcreate_r(size_t __n, struct hsearch_data* __table) __INTRODUCED_IN(28);
-void* tdelete(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(16);
-void tdestroy(void* __root, void (*__free_fn)(void*)) __INTRODUCED_IN(16);
-void* tfind(const void* __key, void* const* __root_ptr, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(16);
-void* tsearch(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(16);
-void twalk(const void* __root, void (*__visitor)(const void*, VISIT, int)) __INTRODUCED_IN(21);
+/**
+ * [hdestroy_r(3)](http://man7.org/linux/man-pages/man3/hdestroy_r.3.html) destroys
+ * the hash table `__table`.
+ *
+ * Available since API level 28.
+ */
+void hdestroy_r(struct hsearch_data* __table) __INTRODUCED_IN(28);
-__END_DECLS
+/**
+ * [hsearch_r(3)](http://man7.org/linux/man-pages/man3/hsearch_r.3.html) finds or
+ * inserts `__entry` in the hash table `__table`, based on `__action`.
+ *
+ * Returns *non-zero* on success and returns 0 and sets `errno` on failure.
+ * A pointer to the entry is returned in `*__result`.
+ *
+ * Available since API level 28.
+ */
+int hsearch_r(ENTRY __entry, ACTION __action, ENTRY** __result, struct hsearch_data* __table) __INTRODUCED_IN(28);
#endif
+
+/**
+ * [lfind(3)](http://man7.org/linux/man-pages/man3/lfind.3.html) brute-force
+ * searches the unsorted array `__array` (of `__count` items each of size `__size`)
+ * for `__key`, using `__comparator`.
+ *
+ * See bsearch() if you have a sorted array.
+ *
+ * Returns a pointer to the matching element on success, or NULL on failure.
+ *
+ * Available since API level 21.
+ */
+void* lfind(const void* __key, const void* __array, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(21);
+
+/**
+ * [lsearch(3)](http://man7.org/linux/man-pages/man3/lsearch.3.html) brute-force
+ * searches the unsorted array `__array` (of `__count` items each of size `__size`)
+ * for `__key`, using `__comparator`.
+ *
+ * Unlike lfind(), on failure lsearch() will *insert* `__key` at the end of
+ * `__array` and increment `*__count`.
+ *
+ * Returns a pointer to the matching element on success, or to the newly-added
+ * element on failure.
+ *
+ * Available since API level 21.
+ */
+void* lsearch(const void* __key, void* __array, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(21);
+
+/**
+ * [tdelete(3)](http://man7.org/linux/man-pages/man3/tdelete.3.html) searches
+ * for and removes an element in the tree `*__root_ptr`. The search is performed
+ * using `__comparator`.
+ *
+ * Returns a pointer to the parent of the deleted node, or NULL on failure.
+ */
+void* tdelete(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*));
+
+/**
+ * [tdestroy(3)](http://man7.org/linux/man-pages/man3/tdestroy.3.html) destroys
+ * the hash table `__root` using `__free_fn` on each node.
+ */
+void tdestroy(void* __root, void (*__free_fn)(void*));
+
+/**
+ * [tfind(3)](http://man7.org/linux/man-pages/man3/tfind.3.html) searches
+ * for an element in the tree `*__root_ptr`. The search is performed using
+ * `__comparator`.
+ *
+ * Returns a pointer to the matching node, or NULL on failure.
+ */
+void* tfind(const void* __key, void* const* __root_ptr, int (*__comparator)(const void*, const void*));
+
+/**
+ * [tsearch(3)](http://man7.org/linux/man-pages/man3/tsearch.3.html) searches
+ * for an element in the tree `*__root_ptr`. The search is performed using
+ * `__comparator`.
+ *
+ * Unlike tfind(), on failure tsearch() will *insert* `__key` into the tree.
+ *
+ * Returns a pointer to the matching node, or to the newly-added node.
+ */
+void* tsearch(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*));
+
+/**
+ * [twalk(3)](http://man7.org/linux/man-pages/man3/twalk.3.html) calls
+ * `__visitor` on every node in the tree.
+ */
+void twalk(const void* __root, void (*__visitor)(const void*, VISIT, int));
+
+__END_DECLS
diff --git a/libc/include/sys/xattr.h b/libc/include/sys/xattr.h
index d15b3fce9..dc58026eb 100644
--- a/libc/include/sys/xattr.h
+++ b/libc/include/sys/xattr.h
@@ -26,8 +26,12 @@
* SUCH DAMAGE.
*/
-#ifndef _SYS_XATTR_H_
-#define _SYS_XATTR_H_
+#pragma once
+
+/**
+ * @file sys/xattr.h
+ * @brief Extended attribute functions.
+ */
#include <linux/xattr.h>
#include <sys/cdefs.h>
@@ -35,25 +39,120 @@
__BEGIN_DECLS
-int fsetxattr(int __fd, const char* __name, const void* __value, size_t __size, int __flags)
- __INTRODUCED_IN(16);
-int setxattr(const char* __path, const char* __name, const void* __value, size_t __size, int __flags)
- __INTRODUCED_IN(16);
-int lsetxattr(const char* __path, const char* __name, const void* __value, size_t __size, int __flags)
- __INTRODUCED_IN(16);
+/**
+ * [fsetxattr(2)](http://man7.org/linux/man-pages/man2/fsetxattr.2.html)
+ * sets an extended attribute on the file referred to by the given file
+ * descriptor.
+ *
+ * Valid flags are `XATTR_CREATE` and `XATTR_REPLACE`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int fsetxattr(int __fd, const char* __name, const void* __value, size_t __size, int __flags);
-ssize_t fgetxattr(int __fd, const char* __name, void* __value, size_t __size) __INTRODUCED_IN(16);
-ssize_t getxattr(const char* __path, const char* __name, void* __value, size_t __size) __INTRODUCED_IN(16);
-ssize_t lgetxattr(const char* __path, const char* __name, void* __value, size_t __size) __INTRODUCED_IN(16);
+/**
+ * [setxattr(2)](http://man7.org/linux/man-pages/man2/setxattr.2.html)
+ * sets an extended attribute on the file referred to by the given path.
+ *
+ * Valid flags are `XATTR_CREATE` and `XATTR_REPLACE`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int setxattr(const char* __path, const char* __name, const void* __value, size_t __size, int __flags);
-ssize_t listxattr(const char* __path, char* __list, size_t __size) __INTRODUCED_IN(16);
-ssize_t llistxattr(const char* __path, char* __list, size_t __size) __INTRODUCED_IN(16);
-ssize_t flistxattr(int __fd, char* __list, size_t __size) __INTRODUCED_IN(16);
+/**
+ * [lsetxattr(2)](http://man7.org/linux/man-pages/man2/lsetxattr.2.html)
+ * sets an extended attribute on the file referred to by the given path, which
+ * is the link itself rather than its target in the case of a symbolic link.
+ *
+ * Valid flags are `XATTR_CREATE` and `XATTR_REPLACE`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int lsetxattr(const char* __path, const char* __name, const void* __value, size_t __size, int __flags);
-int removexattr(const char* __path, const char* __name) __INTRODUCED_IN(16);
-int lremovexattr(const char* __path, const char* __name) __INTRODUCED_IN(16);
-int fremovexattr(int __fd, const char* __name) __INTRODUCED_IN(16);
+/**
+ * [fgetxattr(2)](http://man7.org/linux/man-pages/man2/fgetxattr.2.html)
+ * gets an extended attribute on the file referred to by the given file
+ * descriptor.
+ *
+ * Returns the non-negative length of the value on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t fgetxattr(int __fd, const char* __name, void* __value, size_t __size);
-__END_DECLS
+/**
+ * [getxattr(2)](http://man7.org/linux/man-pages/man2/getxattr.2.html)
+ * gets an extended attribute on the file referred to by the given path.
+ *
+ * Returns the non-negative length of the value on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t getxattr(const char* __path, const char* __name, void* __value, size_t __size);
+
+/**
+ * [lgetxattr(2)](http://man7.org/linux/man-pages/man2/lgetxattr.2.html)
+ * gets an extended attribute on the file referred to by the given path, which
+ * is the link itself rather than its target in the case of a symbolic link.
+ *
+ * Returns the non-negative length of the value on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t lgetxattr(const char* __path, const char* __name, void* __value, size_t __size);
-#endif
+/**
+ * [flistxattr(2)](http://man7.org/linux/man-pages/man2/flistxattr.2.html)
+ * lists the extended attributes on the file referred to by the given file
+ * descriptor.
+ *
+ * Returns the non-negative length of the list on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t flistxattr(int __fd, char* __list, size_t __size);
+
+/**
+ * [listxattr(2)](http://man7.org/linux/man-pages/man2/listxattr.2.html)
+ * lists the extended attributes on the file referred to by the given path.
+ *
+ * Returns the non-negative length of the list on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t listxattr(const char* __path, char* __list, size_t __size);
+
+/**
+ * [llistxattr(2)](http://man7.org/linux/man-pages/man2/llistxattr.2.html)
+ * lists the extended attributes on the file referred to by the given path, which
+ * is the link itself rather than its target in the case of a symbolic link.
+ *
+ * Returns the non-negative length of the list on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t llistxattr(const char* __path, char* __list, size_t __size);
+
+/**
+ * [fremovexattr(2)](http://man7.org/linux/man-pages/man2/fremovexattr.2.html)
+ * removes an extended attribute on the file referred to by the given file
+ * descriptor.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int fremovexattr(int __fd, const char* __name);
+
+/**
+ * [lremovexattr(2)](http://man7.org/linux/man-pages/man2/lremovexattr.2.html)
+ * removes an extended attribute on the file referred to by the given path, which
+ * is the link itself rather than its target in the case of a symbolic link.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int lremovexattr(const char* __path, const char* __name);
+
+/**
+ * [removexattr(2)](http://man7.org/linux/man-pages/man2/removexattr.2.html)
+ * removes an extended attribute on the file referred to by the given path.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int removexattr(const char* __path, const char* __name);
+
+__END_DECLS
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index a4ab600fa..8e843af39 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1468,10 +1468,10 @@ LIBC_Q { # introduced=Q
__system_properties_init; # apex
# Used by libmemunreachable
- malloc_backtrace; # apex vndk
- malloc_disable; # apex vndk
- malloc_enable; # apex vndk
- malloc_iterate; # apex vndk
+ malloc_backtrace; # apex llndk
+ malloc_disable; # apex llndk
+ malloc_enable; # apex llndk
+ malloc_iterate; # apex llndk
# Used by libandroid_net
android_getaddrinfofornet; # apex
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index b5219a172..43bc4b61a 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -557,7 +557,7 @@ bool PointerData::Exists(const void* ptr) {
return pointers_.count(pointer) != 0;
}
-void PointerData::DumpLiveToFile(FILE* fp) {
+void PointerData::DumpLiveToFile(int fd) {
std::vector<ListInfoType> list;
std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
@@ -569,13 +569,13 @@ void PointerData::DumpLiveToFile(FILE* fp) {
total_memory += info.size * info.num_allocations;
}
- fprintf(fp, "Total memory: %zu\n", total_memory);
- fprintf(fp, "Allocation records: %zd\n", list.size());
- fprintf(fp, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
- fprintf(fp, "\n");
+ dprintf(fd, "Total memory: %zu\n", total_memory);
+ dprintf(fd, "Allocation records: %zd\n", list.size());
+ dprintf(fd, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
+ dprintf(fd, "\n");
for (const auto& info : list) {
- fprintf(fp, "z %d sz %8zu num %zu bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
+ dprintf(fd, "z %d sz %8zu num %zu bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
info.num_allocations);
FrameInfoType* frame_info = info.frame_info;
if (frame_info != nullptr) {
@@ -583,22 +583,22 @@ void PointerData::DumpLiveToFile(FILE* fp) {
if (frame_info->frames[i] == 0) {
break;
}
- fprintf(fp, " %" PRIxPTR, frame_info->frames[i]);
+ dprintf(fd, " %" PRIxPTR, frame_info->frames[i]);
}
}
- fprintf(fp, "\n");
+ dprintf(fd, "\n");
if (info.backtrace_info != nullptr) {
- fprintf(fp, " bt_info");
+ dprintf(fd, " bt_info");
for (const auto& frame : *info.backtrace_info) {
- fprintf(fp, " {");
+ dprintf(fd, " {");
if (frame.map_info != nullptr && !frame.map_info->name.empty()) {
- fprintf(fp, "\"%s\"", frame.map_info->name.c_str());
+ dprintf(fd, "\"%s\"", frame.map_info->name.c_str());
} else {
- fprintf(fp, "\"\"");
+ dprintf(fd, "\"\"");
}
- fprintf(fp, " %" PRIx64, frame.rel_pc);
+ dprintf(fd, " %" PRIx64, frame.rel_pc);
if (frame.function_name.empty()) {
- fprintf(fp, " \"\" 0}");
+ dprintf(fd, " \"\" 0}");
} else {
char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr,
nullptr);
@@ -608,11 +608,11 @@ void PointerData::DumpLiveToFile(FILE* fp) {
} else {
name = frame.function_name.c_str();
}
- fprintf(fp, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
+ dprintf(fd, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
free(demangled_name);
}
}
- fprintf(fp, "\n");
+ dprintf(fd, "\n");
}
}
}
diff --git a/libc/malloc_debug/PointerData.h b/libc/malloc_debug/PointerData.h
index c7958f369..78f0ed862 100644
--- a/libc/malloc_debug/PointerData.h
+++ b/libc/malloc_debug/PointerData.h
@@ -152,7 +152,7 @@ class PointerData : public OptionData {
static void GetAllocList(std::vector<ListInfoType>* list);
static void LogLeaks();
- static void DumpLiveToFile(FILE* fp);
+ static void DumpLiveToFile(int fd);
static void GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory,
size_t* backtrace_size);
diff --git a/libc/malloc_debug/exported32.map b/libc/malloc_debug/exported32.map
index 8ed37faa2..f75d173df 100644
--- a/libc/malloc_debug/exported32.map
+++ b/libc/malloc_debug/exported32.map
@@ -8,13 +8,13 @@ LIBC_MALLOC_DEBUG {
debug_free_malloc_leak_info;
debug_get_malloc_leak_info;
debug_initialize;
- debug_iterate;
debug_mallinfo;
debug_malloc;
debug_malloc_backtrace;
debug_malloc_disable;
debug_malloc_enable;
debug_malloc_info;
+ debug_malloc_iterate;
debug_malloc_usable_size;
debug_mallopt;
debug_memalign;
diff --git a/libc/malloc_debug/exported64.map b/libc/malloc_debug/exported64.map
index cdff88ba1..6dea58c42 100644
--- a/libc/malloc_debug/exported64.map
+++ b/libc/malloc_debug/exported64.map
@@ -8,13 +8,13 @@ LIBC_MALLOC_DEBUG {
debug_free_malloc_leak_info;
debug_get_malloc_leak_info;
debug_initialize;
- debug_iterate;
debug_mallinfo;
debug_malloc;
debug_malloc_backtrace;
debug_malloc_disable;
debug_malloc_enable;
debug_malloc_info;
+ debug_malloc_iterate;
debug_malloc_usable_size;
debug_mallopt;
debug_memalign;
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index d2679ca4c..3da694c75 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -97,8 +97,8 @@ struct mallinfo debug_mallinfo();
int debug_mallopt(int param, int value);
int debug_malloc_info(int options, FILE* fp);
int debug_posix_memalign(void** memptr, size_t alignment, size_t size);
-int debug_iterate(uintptr_t base, size_t size,
- void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+int debug_malloc_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
void debug_malloc_disable();
void debug_malloc_enable();
@@ -808,16 +808,23 @@ int debug_malloc_info(int options, FILE* fp) {
if (DebugCallsDisabled() || !g_debug->TrackPointers()) {
return g_dispatch->malloc_info(options, fp);
}
+
+ // Make sure any pending output is written to the file.
+ fflush(fp);
+
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
- MallocXmlElem root(fp, "malloc", "version=\"debug-malloc-1\"");
+ // Avoid any issues where allocations are made that will be freed
+ // in the fclose.
+ int fd = fileno(fp);
+ MallocXmlElem root(fd, "malloc", "version=\"debug-malloc-1\"");
std::vector<ListInfoType> list;
PointerData::GetAllocList(&list);
size_t alloc_num = 0;
for (size_t i = 0; i < list.size(); i++) {
- MallocXmlElem alloc(fp, "allocation", "nr=\"%zu\"", alloc_num);
+ MallocXmlElem alloc(fd, "allocation", "nr=\"%zu\"", alloc_num);
size_t total = 1;
size_t size = list[i].size;
@@ -825,8 +832,8 @@ int debug_malloc_info(int options, FILE* fp) {
i++;
total++;
}
- MallocXmlElem(fp, "size").Contents("%zu", list[i].size);
- MallocXmlElem(fp, "total").Contents("%zu", total);
+ MallocXmlElem(fd, "size").Contents("%zu", list[i].size);
+ MallocXmlElem(fd, "total").Contents("%zu", total);
alloc_num++;
}
return 0;
@@ -857,7 +864,7 @@ int debug_posix_memalign(void** memptr, size_t alignment, size_t size) {
return (*memptr != nullptr) ? 0 : ENOMEM;
}
-int debug_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*),
+int debug_malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*),
void* arg) {
ScopedConcurrentLock lock;
if (g_debug->TrackPointers()) {
@@ -870,7 +877,7 @@ int debug_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_
// An option that adds a header will add pointer tracking, so no need to
// check if headers are enabled.
- return g_dispatch->iterate(base, size, callback, arg);
+ return g_dispatch->malloc_iterate(base, size, callback, arg);
}
void debug_malloc_disable() {
@@ -928,25 +935,28 @@ void* debug_valloc(size_t size) {
static std::mutex g_dump_lock;
-static void write_dump(FILE* fp) {
- fprintf(fp, "Android Native Heap Dump v1.2\n\n");
+static void write_dump(int fd) {
+ dprintf(fd, "Android Native Heap Dump v1.2\n\n");
std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "unknown");
- fprintf(fp, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
+ dprintf(fd, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
- PointerData::DumpLiveToFile(fp);
+ PointerData::DumpLiveToFile(fd);
- fprintf(fp, "MAPS\n");
+ dprintf(fd, "MAPS\n");
std::string content;
if (!android::base::ReadFileToString("/proc/self/maps", &content)) {
- fprintf(fp, "Could not open /proc/self/maps\n");
+ dprintf(fd, "Could not open /proc/self/maps\n");
} else {
- fprintf(fp, "%s", content.c_str());
+ dprintf(fd, "%s", content.c_str());
}
- fprintf(fp, "END\n");
+ dprintf(fd, "END\n");
}
bool debug_write_malloc_leak_info(FILE* fp) {
+ // Make sure any pending output is written to the file.
+ fflush(fp);
+
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
@@ -956,7 +966,8 @@ bool debug_write_malloc_leak_info(FILE* fp) {
return false;
}
- write_dump(fp);
+ write_dump(fileno(fp));
+
return true;
}
@@ -966,13 +977,13 @@ void debug_dump_heap(const char* file_name) {
std::lock_guard<std::mutex> guard(g_dump_lock);
- FILE* fp = fopen(file_name, "w+e");
- if (fp == nullptr) {
+ int fd = open(file_name, O_RDWR | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0644);
+ if (fd == -1) {
error_log("Unable to create file: %s", file_name);
return;
}
error_log("Dumping to file: %s\n", file_name);
- write_dump(fp);
- fclose(fp);
+ write_dump(fd);
+ close(fd);
}
diff --git a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
index 071675875..67bb8d936 100644
--- a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
@@ -37,6 +37,7 @@
#include <time.h>
#include <unistd.h>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -174,6 +175,7 @@ static void GetLogStr(pid_t pid, std::string* log_str, log_id log = LOG_ID_MAIN)
}
static void FindStrings(pid_t pid, std::vector<const char*> match_strings,
+ std::vector<const char*> no_match_strings = std::vector<const char*>{},
time_t timeout_seconds = kTimeoutSeconds) {
std::string log_str;
time_t start = time(nullptr);
@@ -181,12 +183,18 @@ static void FindStrings(pid_t pid, std::vector<const char*> match_strings,
while (true) {
GetLogStr(pid, &log_str);
found_all = true;
+ // Look for the expected strings.
for (auto str : match_strings) {
if (log_str.find(str) == std::string::npos) {
found_all = false;
break;
}
}
+
+ // Verify the unexpected strings are not present.
+ for (auto str : no_match_strings) {
+ ASSERT_TRUE(log_str.find(str) == std::string::npos) << "Unexpectedly found '" << str << "' in log output:\n" << log_str;
+ }
if (found_all) {
return;
}
@@ -194,7 +202,7 @@ static void FindStrings(pid_t pid, std::vector<const char*> match_strings,
break;
}
}
- ASSERT_TRUE(found_all) << "Didn't find expected log output:\n" + log_str;
+ ASSERT_TRUE(found_all) << "Didn't find expected log output:\n" << log_str;
}
TEST(MallocTests, DISABLED_smoke) {}
@@ -464,3 +472,45 @@ TEST(MallocDebugSystemTest, exit_while_threads_allocating) {
<< "Found crash in log.\nLog message: " << log_str;
}
}
+
+TEST(MallocTests, DISABLED_write_leak_info) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ FILE* fp = fdopen(tf.fd, "w+");
+ if (fp == nullptr) {
+ printf("Unable to create %s\n", tf.path);
+ _exit(1);
+ }
+ tf.release();
+
+ void* ptr = malloc(1000);
+ if (ptr == nullptr) {
+ printf("malloc failed\n");
+ _exit(1);
+ }
+ memset(ptr, 0, 1000);
+
+ android_mallopt(M_WRITE_MALLOC_LEAK_INFO_TO_FILE, fp, sizeof(fp));
+
+ fclose(fp);
+
+ free(ptr);
+}
+
+TEST(MallocDebugSystemTest, write_leak_info_no_header) {
+ pid_t pid;
+ ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_write_leak_info", "verbose backtrace", &pid, 0));
+
+ ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"},
+
+ std::vector<const char*>{" HAS INVALID TAG ", "USED AFTER FREE ", "UNKNOWN POINTER "}));
+}
+
+TEST(MallocDebugSystemTest, write_leak_info_header) {
+ pid_t pid;
+ ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_write_leak_info", "verbose backtrace guard", &pid, 0));
+
+ ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"},
+ std::vector<const char*>{" HAS INVALID TAG ", "USED AFTER FREE ", "UNKNOWN POINTER "}));
+}
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 0238d1049..70457b9ad 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -27,6 +27,8 @@
#include <algorithm>
#include <memory>
+#include <string>
+#include <string_view>
#include <thread>
#include <vector>
#include <utility>
@@ -73,6 +75,9 @@ void* debug_pvalloc(size_t);
void* debug_valloc(size_t);
#endif
+bool debug_write_malloc_leak_info(FILE*);
+void debug_dump_heap(const char*);
+
__END_DECLS
constexpr char DIVIDER[] =
@@ -1302,6 +1307,10 @@ TEST_F(MallocDebugTest, get_malloc_backtrace_with_header) {
}
static std::string SanitizeHeapData(const std::string& data) {
+ if (data.empty()) {
+ return data;
+ }
+
// Remove the map data since it's not consistent.
std::string sanitized;
bool skip_map_data = false;
@@ -1329,7 +1338,7 @@ static std::string SanitizeHeapData(const std::string& data) {
sanitized += line + '\n';
}
}
- return sanitized;
+ return android::base::Trim(sanitized);
}
void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) {
@@ -1402,9 +1411,7 @@ z 1 sz 100 num 1 bt 100 200
z 1 sz 40 num 1 bt 300 400
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1463,9 +1470,7 @@ z 0 sz 400 num 1 bt a000 b000
z 0 sz 300 num 1 bt 100 200
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1513,9 +1518,7 @@ z 0 sz 400 num 1 bt a000 b000 c000
z 0 sz 300 num 2 bt 100 200
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1575,9 +1578,7 @@ z 0 sz 300 num 1 bt 1100 1200
bt_info {"" 100 "fake1" a} {"" 200 "fake2" 14}
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -2472,15 +2473,19 @@ TEST_F(MallocDebugTest, abort_on_error_header_tag_corrupted) {
TEST_F(MallocDebugTest, malloc_info_no_pointer_tracking) {
Init("fill");
- char* buffer;
- size_t size;
- FILE* memstream = open_memstream(&buffer, &size);
- ASSERT_TRUE(memstream != nullptr);
- ASSERT_EQ(0, debug_malloc_info(0, memstream));
- ASSERT_EQ(0, fclose(memstream));
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
+ ASSERT_EQ(0, debug_malloc_info(0, fp));
+ ASSERT_EQ(0, fclose(fp));
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
auto root = doc.FirstChildElement();
ASSERT_TRUE(root != nullptr);
ASSERT_STREQ("malloc", root->Name());
@@ -2501,17 +2506,21 @@ TEST_F(MallocDebugTest, malloc_info_with_pointer_tracking) {
std::unique_ptr<void, decltype(debug_free)*> ptr4(debug_malloc(1200), debug_free);
ASSERT_TRUE(ptr4.get() != nullptr);
- char* buffer;
- size_t size;
- FILE* memstream = open_memstream(&buffer, &size);
- ASSERT_TRUE(memstream != nullptr);
- ASSERT_EQ(0, debug_malloc_info(0, memstream));
- ASSERT_EQ(0, fclose(memstream));
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
+ ASSERT_EQ(0, debug_malloc_info(0, fp));
+ ASSERT_EQ(0, fclose(fp));
- SCOPED_TRACE(testing::Message() << "Output:\n" << buffer);
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+
+ SCOPED_TRACE(testing::Message() << "Output:\n" << contents);
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
auto root = doc.FirstChildElement();
ASSERT_TRUE(root != nullptr);
ASSERT_STREQ("malloc", root->Name());
@@ -2548,3 +2557,134 @@ TEST_F(MallocDebugTest, malloc_info_with_pointer_tracking) {
ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("total")->QueryIntText(&val));
ASSERT_EQ(1, val);
}
+
+static void AllocPtrsWithBacktrace(std::vector<void*>* ptrs) {
+ backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd, 0xc});
+ void* ptr = debug_malloc(1024);
+ ASSERT_TRUE(ptr != nullptr);
+ memset(ptr, 0, 1024);
+ ptrs->push_back(ptr);
+
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xbc001, 0xbc002});
+ ptr = debug_malloc(500);
+ ASSERT_TRUE(ptr != nullptr);
+ memset(ptr, 0, 500);
+ ptrs->push_back(ptr);
+
+ backtrace_fake_add(std::vector<uintptr_t> {0x104});
+ ptr = debug_malloc(100);
+ ASSERT_TRUE(ptr != nullptr);
+ memset(ptr, 0, 100);
+ ptrs->push_back(ptr);
+}
+
+static constexpr std::string_view kDumpInfo = R"(Android Native Heap Dump v1.2
+
+Build fingerprint: ''
+
+Total memory: 1624
+Allocation records: 3
+Backtrace size: 16
+
+z 0 sz 1024 num 1 bt f e d c
+z 0 sz 500 num 1 bt bc000 bc001 bc002
+z 0 sz 100 num 1 bt 104
+MAPS
+MAP_DATA
+END)";
+
+TEST_F(MallocDebugTest, debug_write_malloc_leak_info) {
+ Init("backtrace=16");
+
+ std::vector<void*> ptrs;
+ AllocPtrsWithBacktrace(&ptrs);
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.release();
+ FILE* fp = fopen(tf.path, "w+");
+ ASSERT_TRUE(fp != nullptr);
+
+ ASSERT_TRUE(debug_write_malloc_leak_info(fp));
+
+ fclose(fp);
+
+ for (auto ptr : ptrs) {
+ debug_free(ptr);
+ }
+ ptrs.clear();
+
+ std::string expected(kDumpInfo);
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+ contents = SanitizeHeapData(contents);
+ ASSERT_EQ(expected, contents);
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, debug_write_malloc_leak_info_extra_data) {
+ Init("backtrace=16");
+
+ std::vector<void*> ptrs;
+ AllocPtrsWithBacktrace(&ptrs);
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.release();
+ FILE* fp = fopen(tf.path, "w+");
+ ASSERT_TRUE(fp != nullptr);
+
+ fprintf(fp, "This message should appear before the output.\n");
+ ASSERT_TRUE(debug_write_malloc_leak_info(fp));
+ fprintf(fp, "This message should appear after the output.\n");
+
+ fclose(fp);
+
+ for (auto ptr : ptrs) {
+ debug_free(ptr);
+ }
+ ptrs.clear();
+
+ std::string expected = "This message should appear before the output.\n"
+ + std::string(kDumpInfo)
+ + "\nThis message should appear after the output.";
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+ contents = SanitizeHeapData(contents);
+ ASSERT_EQ(expected, contents);
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, dump_heap) {
+ Init("backtrace=16");
+
+ std::vector<void*> ptrs;
+ AllocPtrsWithBacktrace(&ptrs);
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.release();
+ debug_dump_heap(tf.path);
+
+ for (auto ptr : ptrs) {
+ debug_free(ptr);
+ }
+ ptrs.clear();
+
+ std::string expected(kDumpInfo);
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+ contents = SanitizeHeapData(contents);
+ ASSERT_EQ(expected, contents);
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string expected_log = std::string("6 malloc_debug Dumping to file: ") + tf.path + "\n\n";
+ ASSERT_EQ(expected_log, getFakeLogPrint());
+}
diff --git a/libc/malloc_hooks/exported32.map b/libc/malloc_hooks/exported32.map
index 293d9ac15..2b028062b 100644
--- a/libc/malloc_hooks/exported32.map
+++ b/libc/malloc_hooks/exported32.map
@@ -7,13 +7,13 @@ LIBC_MALLOC_HOOKS {
hooks_free_malloc_leak_info;
hooks_get_malloc_leak_info;
hooks_initialize;
- hooks_iterate;
hooks_mallinfo;
hooks_malloc;
hooks_malloc_backtrace;
hooks_malloc_disable;
hooks_malloc_enable;
hooks_malloc_info;
+ hooks_malloc_iterate;
hooks_malloc_usable_size;
hooks_mallopt;
hooks_memalign;
diff --git a/libc/malloc_hooks/exported64.map b/libc/malloc_hooks/exported64.map
index 340106b2e..59ec1f01f 100644
--- a/libc/malloc_hooks/exported64.map
+++ b/libc/malloc_hooks/exported64.map
@@ -7,13 +7,13 @@ LIBC_MALLOC_HOOKS {
hooks_free_malloc_leak_info;
hooks_get_malloc_leak_info;
hooks_initialize;
- hooks_iterate;
hooks_mallinfo;
hooks_malloc;
hooks_malloc_backtrace;
hooks_malloc_disable;
hooks_malloc_enable;
hooks_malloc_info;
+ hooks_malloc_iterate;
hooks_malloc_usable_size;
hooks_mallopt;
hooks_memalign;
diff --git a/libc/malloc_hooks/malloc_hooks.cpp b/libc/malloc_hooks/malloc_hooks.cpp
index b1c1d5060..1ba869698 100644
--- a/libc/malloc_hooks/malloc_hooks.cpp
+++ b/libc/malloc_hooks/malloc_hooks.cpp
@@ -67,7 +67,7 @@ void* hooks_calloc(size_t nmemb, size_t bytes);
struct mallinfo hooks_mallinfo();
int hooks_mallopt(int param, int value);
int hooks_posix_memalign(void** memptr, size_t alignment, size_t size);
-int hooks_iterate(uintptr_t base, size_t size,
+int hooks_malloc_iterate(uintptr_t base, size_t size,
void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
void hooks_malloc_disable();
void hooks_malloc_enable();
@@ -209,7 +209,7 @@ int hooks_posix_memalign(void** memptr, size_t alignment, size_t size) {
return g_dispatch->posix_memalign(memptr, alignment, size);
}
-int hooks_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*) {
+int hooks_malloc_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*) {
return 0;
}
diff --git a/libc/private/MallocXmlElem.h b/libc/private/MallocXmlElem.h
index 04d3eeea6..a36797261 100644
--- a/libc/private/MallocXmlElem.h
+++ b/libc/private/MallocXmlElem.h
@@ -18,38 +18,39 @@
#include <stdarg.h>
#include <stdio.h>
+#include <unistd.h>
#include <private/bionic_macros.h>
class MallocXmlElem {
public:
// Name must be valid throughout lifetime of the object.
- explicit MallocXmlElem(FILE* fp, const char* name,
- const char* attr_fmt = nullptr, ...) : fp_(fp), name_(name) {
- fprintf(fp, "<%s", name_);
+ explicit MallocXmlElem(int fd, const char* name,
+ const char* attr_fmt = nullptr, ...) : fd_(fd), name_(name) {
+ dprintf(fd_, "<%s", name_);
if (attr_fmt != nullptr) {
va_list args;
va_start(args, attr_fmt);
- fputc(' ', fp_);
- vfprintf(fp_, attr_fmt, args);
+ write(fd_, " ", 1);
+ vdprintf(fd_, attr_fmt, args);
va_end(args);
}
- fputc('>', fp_);
+ write(fd_, ">", 1);
}
~MallocXmlElem() noexcept {
- fprintf(fp_, "</%s>", name_);
+ dprintf(fd_, "</%s>", name_);
}
void Contents(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
- vfprintf(fp_, fmt, args);
+ vdprintf(fd_, fmt, args);
va_end(args);
}
private:
- FILE* fp_;
+ int fd_;
const char* name_;
BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(MallocXmlElem);
diff --git a/libc/private/ScopedFd.h b/libc/private/ScopedFd.h
new file mode 100644
index 000000000..1cec91683
--- /dev/null
+++ b/libc/private/ScopedFd.h
@@ -0,0 +1,64 @@
+/*
+ * 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 <unistd.h>
+
+#include "private/bionic_macros.h"
+#include "private/ErrnoRestorer.h"
+
+class ScopedFd final {
+ public:
+ explicit ScopedFd(int fd) : fd_(fd) {
+ }
+
+ ScopedFd() : fd_(-1) {
+ }
+
+ ~ScopedFd() {
+ reset(-1);
+ }
+
+ void reset(int fd = -1) {
+ if (fd_ != -1) {
+ ErrnoRestorer e;
+ close(fd_);
+ }
+ fd_ = fd;
+ }
+
+ int get() const {
+ return fd_;
+ }
+
+ private:
+ int fd_;
+
+ BIONIC_DISALLOW_COPY_AND_ASSIGN(ScopedFd);
+};
diff --git a/libc/private/bionic_malloc_dispatch.h b/libc/private/bionic_malloc_dispatch.h
index aea3a1c09..52d857317 100644
--- a/libc/private/bionic_malloc_dispatch.h
+++ b/libc/private/bionic_malloc_dispatch.h
@@ -70,7 +70,7 @@ struct MallocDispatch {
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
MallocValloc valloc;
#endif
- MallocIterate iterate;
+ MallocIterate malloc_iterate;
MallocMallocDisable malloc_disable;
MallocMallocEnable malloc_enable;
MallocMallopt mallopt;
diff --git a/libc/system_properties/include/system_properties/prop_area.h b/libc/system_properties/include/system_properties/prop_area.h
index a69f90eb1..53b27457e 100644
--- a/libc/system_properties/include/system_properties/prop_area.h
+++ b/libc/system_properties/include/system_properties/prop_area.h
@@ -106,6 +106,20 @@ class prop_area {
memset(reserved_, 0, sizeof(reserved_));
// Allocate enough space for the root node.
bytes_used_ = sizeof(prop_bt);
+ // To make property reads wait-free, we reserve a
+ // PROP_VALUE_MAX-sized block of memory, the "dirty backup area",
+ // just after the root node. When we're about to modify a
+ // property, we copy the old value into the dirty backup area and
+ // copy the new value into the prop_info structure. Before
+ // starting the latter copy, we mark the property's serial as
+ // being dirty. If a reader comes along while we're doing the
+ // property update and sees a dirty serial, the reader copies from
+ // the dirty backup area instead of the property value
+ // proper. After the copy, the reader checks whether the property
+ // serial is the same: if it is, the dirty backup area hasn't been
+ // reused for something else and we can complete the
+ // read immediately.
+ bytes_used_ += __BIONIC_ALIGN(PROP_VALUE_MAX, sizeof(uint_least32_t));
}
const prop_info* find(const char* name);
@@ -122,6 +136,9 @@ class prop_area {
uint32_t version() const {
return version_;
}
+ char* dirty_backup_area() {
+ return data_ + sizeof (prop_bt);
+ }
private:
static prop_area* map_fd_ro(const int fd);
diff --git a/libc/system_properties/include/system_properties/system_properties.h b/libc/system_properties/include/system_properties/system_properties.h
index cad29cc72..0666e2803 100644
--- a/libc/system_properties/include/system_properties/system_properties.h
+++ b/libc/system_properties/include/system_properties/system_properties.h
@@ -66,7 +66,6 @@ class SystemProperties {
int Get(const char* name, char* value);
int Update(prop_info* pi, const char* value, unsigned int len);
int Add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen);
- uint32_t Serial(const prop_info* pi);
uint32_t WaitAny(uint32_t old_serial);
bool Wait(const prop_info* pi, uint32_t old_serial, uint32_t* new_serial_ptr,
const timespec* relative_timeout);
@@ -74,6 +73,8 @@ class SystemProperties {
int Foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie);
private:
+ uint32_t ReadMutablePropertyValue(const prop_info* pi, char* value);
+
// We don't want to use new or malloc in properties (b/31659220), and we don't want to waste a
// full page by using mmap(), so we set aside enough space to create any context of the three
// contexts.
diff --git a/libc/system_properties/system_properties.cpp b/libc/system_properties/system_properties.cpp
index d7c441b9b..840477858 100644
--- a/libc/system_properties/system_properties.cpp
+++ b/libc/system_properties/system_properties.cpp
@@ -140,42 +140,58 @@ static bool is_read_only(const char* name) {
return strncmp(name, "ro.", 3) == 0;
}
-int SystemProperties::Read(const prop_info* pi, char* name, char* value) {
- while (true) {
- uint32_t serial = Serial(pi); // acquire semantics
- size_t len = SERIAL_VALUE_LEN(serial);
- memcpy(value, pi->value, len + 1);
- // TODO: Fix the synchronization scheme here.
- // There is no fully supported way to implement this kind
- // of synchronization in C++11, since the memcpy races with
- // updates to pi, and the data being accessed is not atomic.
- // The following fence is unintuitive, but would be the
- // correct one if memcpy used memory_order_relaxed atomic accesses.
- // In practice it seems unlikely that the generated code would
- // would be any different, so this should be OK.
+uint32_t SystemProperties::ReadMutablePropertyValue(const prop_info* pi, char* value) {
+ // We assume the memcpy below gets serialized by the acquire fence.
+ uint32_t new_serial = load_const_atomic(&pi->serial, memory_order_acquire);
+ uint32_t serial;
+ unsigned int len;
+ for (;;) {
+ serial = new_serial;
+ len = SERIAL_VALUE_LEN(serial);
+ if (__predict_false(SERIAL_DIRTY(serial))) {
+ // See the comment in the prop_area constructor.
+ prop_area* pa = contexts_->GetPropAreaForName(pi->name);
+ memcpy(value, pa->dirty_backup_area(), len + 1);
+ } else {
+ memcpy(value, pi->value, len + 1);
+ }
atomic_thread_fence(memory_order_acquire);
- if (serial == load_const_atomic(&(pi->serial), memory_order_relaxed)) {
- if (name != nullptr) {
- size_t namelen = strlcpy(name, pi->name, PROP_NAME_MAX);
- if (namelen >= PROP_NAME_MAX) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc",
- "The property name length for \"%s\" is >= %d;"
- " please use __system_property_read_callback"
- " to read this property. (the name is truncated to \"%s\")",
- pi->name, PROP_NAME_MAX - 1, name);
- }
- }
- if (is_read_only(pi->name) && pi->is_long()) {
- async_safe_format_log(
- ANDROID_LOG_ERROR, "libc",
- "The property \"%s\" has a value with length %zu that is too large for"
- " __system_property_get()/__system_property_read(); use"
- " __system_property_read_callback() instead.",
- pi->name, strlen(pi->long_value()));
- }
- return len;
+ new_serial = load_const_atomic(&pi->serial, memory_order_relaxed);
+ if (__predict_true(serial == new_serial)) {
+ break;
+ }
+ // We need another fence here because we want to ensure that the memcpy in the
+ // next iteration of the loop occurs after the load of new_serial above. We could
+ // get this guarantee by making the load_const_atomic of new_serial
+ // memory_order_acquire instead of memory_order_relaxed, but then we'd pay the
+ // penalty of the memory_order_acquire even in the overwhelmingly common case
+ // that the serial number didn't change.
+ atomic_thread_fence(memory_order_acquire);
+ }
+ return serial;
+}
+
+int SystemProperties::Read(const prop_info* pi, char* name, char* value) {
+ uint32_t serial = ReadMutablePropertyValue(pi, value);
+ if (name != nullptr) {
+ size_t namelen = strlcpy(name, pi->name, PROP_NAME_MAX);
+ if (namelen >= PROP_NAME_MAX) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "The property name length for \"%s\" is >= %d;"
+ " please use __system_property_read_callback"
+ " to read this property. (the name is truncated to \"%s\")",
+ pi->name, PROP_NAME_MAX - 1, name);
}
}
+ if (is_read_only(pi->name) && pi->is_long()) {
+ async_safe_format_log(
+ ANDROID_LOG_ERROR, "libc",
+ "The property \"%s\" has a value with length %zu that is too large for"
+ " __system_property_get()/__system_property_read(); use"
+ " __system_property_read_callback() instead.",
+ pi->name, strlen(pi->long_value()));
+ }
+ return SERIAL_VALUE_LEN(serial);
}
void SystemProperties::ReadCallback(const prop_info* pi,
@@ -183,9 +199,9 @@ void SystemProperties::ReadCallback(const prop_info* pi,
const char* value, uint32_t serial),
void* cookie) {
// Read only properties don't need to copy the value to a temporary buffer, since it can never
- // change.
+ // change. We use relaxed memory order on the serial load for the same reason.
if (is_read_only(pi->name)) {
- uint32_t serial = Serial(pi);
+ uint32_t serial = load_const_atomic(&pi->serial, memory_order_relaxed);
if (pi->is_long()) {
callback(cookie, pi->name, pi->long_value(), serial);
} else {
@@ -194,21 +210,9 @@ void SystemProperties::ReadCallback(const prop_info* pi,
return;
}
- while (true) {
- uint32_t serial = Serial(pi); // acquire semantics
- size_t len = SERIAL_VALUE_LEN(serial);
- char value_buf[len + 1];
-
- memcpy(value_buf, pi->value, len);
- value_buf[len] = '\0';
-
- // TODO: see todo in Read function
- atomic_thread_fence(memory_order_acquire);
- if (serial == load_const_atomic(&(pi->serial), memory_order_relaxed)) {
- callback(cookie, pi->name, value_buf, serial);
- return;
- }
- }
+ char value_buf[PROP_VALUE_MAX];
+ uint32_t serial = ReadMutablePropertyValue(pi, value_buf);
+ callback(cookie, pi->name, value_buf, serial);
}
int SystemProperties::Get(const char* name, char* value) {
@@ -231,26 +235,37 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len)
return -1;
}
- prop_area* pa = contexts_->GetSerialPropArea();
- if (!pa) {
+ prop_area* serial_pa = contexts_->GetSerialPropArea();
+ if (!serial_pa) {
+ return -1;
+ }
+ prop_area* pa = contexts_->GetPropAreaForName(pi->name);
+ if (__predict_false(!pa)) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Could not find area for \"%s\"", pi->name);
return -1;
}
uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
+ unsigned int old_len = SERIAL_VALUE_LEN(serial);
+
+ // The contract with readers is that whenever the dirty bit is set, an undamaged copy
+ // of the pre-dirty value is available in the dirty backup area. The fence ensures
+ // that we publish our dirty area update before allowing readers to see a
+ // dirty serial.
+ memcpy(pa->dirty_backup_area(), pi->value, old_len + 1);
+ atomic_thread_fence(memory_order_release);
serial |= 1;
atomic_store_explicit(&pi->serial, serial, memory_order_relaxed);
- // The memcpy call here also races. Again pretend it
- // used memory_order_relaxed atomics, and use the analogous
- // counterintuitive fence.
- atomic_thread_fence(memory_order_release);
strlcpy(pi->value, value, len + 1);
-
- atomic_store_explicit(&pi->serial, (len << 24) | ((serial + 1) & 0xffffff), memory_order_release);
- __futex_wake(&pi->serial, INT32_MAX);
-
- atomic_store_explicit(pa->serial(), atomic_load_explicit(pa->serial(), memory_order_relaxed) + 1,
+ // Now the primary value property area is up-to-date. Let readers know that they should
+ // look at the property value instead of the backup area.
+ atomic_thread_fence(memory_order_release);
+ atomic_store_explicit(&pi->serial, (len << 24) | ((serial + 1) & 0xffffff), memory_order_relaxed);
+ __futex_wake(&pi->serial, INT32_MAX); // Fence by side effect
+ atomic_store_explicit(serial_pa->serial(),
+ atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1,
memory_order_release);
- __futex_wake(pa->serial(), INT32_MAX);
+ __futex_wake(serial_pa->serial(), INT32_MAX);
return 0;
}
@@ -294,16 +309,6 @@ int SystemProperties::Add(const char* name, unsigned int namelen, const char* va
return 0;
}
-// Wait for non-locked serial, and retrieve it with acquire semantics.
-uint32_t SystemProperties::Serial(const prop_info* pi) {
- uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire);
- while (SERIAL_DIRTY(serial)) {
- __futex_wait(const_cast<_Atomic(uint_least32_t)*>(&pi->serial), serial, nullptr);
- serial = load_const_atomic(&pi->serial, memory_order_acquire);
- }
- return serial;
-}
-
uint32_t SystemProperties::WaitAny(uint32_t old_serial) {
uint32_t new_serial;
Wait(nullptr, old_serial, &new_serial, nullptr);