Merge "Use the ANDROID_TZDATA_ROOT environment variable"
diff --git a/Android.bp b/Android.bp
deleted file mode 100644
index 72ab6c0..0000000
--- a/Android.bp
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-bionic_mountpoint {
-    name: "libc.mountpoint",
-    stem: "libc.so",
-    src: "dummy_mountpoint",
-    library: true,
-    symlinks: ["libc.so"],
-    mountsource: "libc",
-}
-
-bionic_mountpoint {
-    name: "libdl.mountpoint",
-    stem: "libdl.so",
-    src: "dummy_mountpoint",
-    library: true,
-    symlinks: ["libdl.so"],
-    mountsource: "libdl",
-}
-
-bionic_mountpoint {
-    name: "libm.mountpoint",
-    stem: "libm.so",
-    src: "dummy_mountpoint",
-    library: true,
-    symlinks: ["libm.so"],
-    mountsource: "libm",
-}
-
-bionic_mountpoint {
-    name: "linker.mountpoint",
-    stem: "linker",
-    multilib: {
-        lib64: {
-            suffix: "64",
-        },
-    },
-    src: "dummy_mountpoint",
-    binary: true,
-    symlinks: ["linker", "linker_asan"],
-    mountsource: "linker",
-}
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 9421e26..8865723 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -53,6 +53,10 @@
 $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib/libGLES*)
 $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib64/libGLES*)
 
+# /bionic is removed
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/bionic)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/bionic)
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/build/Android.bp b/build/Android.bp
deleted file mode 100644
index acd0ee2..0000000
--- a/build/Android.bp
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-
-bootstrap_go_package {
-    name: "soong-bionic",
-    pkgPath: "android/soong/bionic",
-    deps: [
-        "blueprint",
-        "blueprint-pathtools",
-        "blueprint-proptools",
-        "soong",
-        "soong-android",
-        "soong-cc",
-    ],
-    srcs: [
-        "bionic.go",
-    ],
-    pluginFor: ["soong_build"],
-}
diff --git a/build/bionic.go b/build/bionic.go
deleted file mode 100644
index 54ad10b..0000000
--- a/build/bionic.go
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package bionic
-
-import (
-	"fmt"
-	"io"
-	"strings"
-
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
-
-	"android/soong/android"
-	"android/soong/cc"
-)
-
-// bionic_mountpoint is a module type that is specialized to create
-// mount points for Bionic files (libc, libdl, libm, and linker).
-//
-// With following description,
-//
-// bionic_mountpoint {
-//    name: "libc.mountpoint",
-//    stem: "libc.so",
-//    src: "dummy_mountpoint",
-//    library: true,
-//    symlinks: ["libc.so"],
-// }
-//
-// , the build system does following jobs:
-//
-// A mount point /bionic/lib[64]/libc.so is created. Its content
-// is from the file 'dummy_mountpoint'.
-//
-// Then a symlink is created at /system/lib[64]/libc.so which points to
-// the created mountpoint.
-//
-// At runtime, on the mount point, either bootstrap Bionic or default Bionic
-// (which is from the runtime APEX) is mounted by the init process. The
-// symlink exists to provide consistent legacy path for compatibility
-// reason.
-func init() {
-	android.RegisterModuleType("bionic_mountpoint", bionicMountpointFactory)
-}
-
-type bionicMountpoint struct {
-	android.ModuleBase
-	properties bionicMountpointProperties
-
-	outputFile android.Path
-	pathInPartition string
-	stem string
-	unstrippedOutputFile android.Path
-}
-
-type bionicMountpointProperties struct {
-	// The file that is installed as the mount point
-	Src *string
-
-	// TODO(jiyong) remove these two properties (probably Stem and Suffix
-	// as well, as they can be inteffered from Mountsource
-
-	// True if the mount point is for a Bionic library such libc.so
-	Library *bool
-	// True if the mount point is for a Bionic binary such as linker
-	Binary *bool
-
-	// The module that this module is a mount point for
-	Mountsource *string
-
-	// Base name of the mount point
-	Stem *string `android:"arch_variant"`
-
-	// Append to the name of the output
-	Suffix *string `android:"arch_variant"`
-
-	// Symlinks to the mountpoints from the system and recovery partitions
-	// Symlinks names will have the same suffix as the mount point
-	Symlinks []string
-
-	// List of sanitizer names that this APEX is enabled for
-	SanitizerNames []string `blueprint:"mutated"`
-}
-
-type dependencyTag struct {
-	blueprint.BaseDependencyTag
-	name string
-}
-
-var mountsourceTag = dependencyTag{name: "mountsource"}
-
-
-func (m *bionicMountpoint) EnableSanitizer(sanitizerName string) {
-	if !android.InList(sanitizerName, m.properties.SanitizerNames) {
-		m.properties.SanitizerNames = append(m.properties.SanitizerNames, sanitizerName)
-	}
-}
-
-func (m *bionicMountpoint) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool {
-	if android.InList(sanitizerName, m.properties.SanitizerNames) {
-		return true
-	}
-
-	// Then follow the global setting
-	globalSanitizerNames := []string{}
-	if m.Host() {
-		globalSanitizerNames = ctx.Config().SanitizeHost()
-	} else {
-		arches := ctx.Config().SanitizeDeviceArch()
-		if len(arches) == 0 || android.InList(m.Arch().ArchType.Name, arches) {
-			globalSanitizerNames = ctx.Config().SanitizeDevice()
-		}
-	}
-	return android.InList(sanitizerName, globalSanitizerNames)
-}
-
-func (m *bionicMountpoint) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if Bool(m.properties.Library) == Bool(m.properties.Binary) {
-		ctx.ModuleErrorf("either binary or library must be set to true")
-		return
-	}
-	if m.properties.Stem == nil {
-		ctx.PropertyErrorf("stem", "stem must be set")
-		return
-	}
-	if m.properties.Src == nil {
-		ctx.PropertyErrorf("src", "src must be set")
-	}
-	android.ExtractSourceDeps(ctx, m.properties.Src)
-
-	if m.properties.Mountsource == nil {
-		ctx.PropertyErrorf("mountsource", "mountsource must be set")
-		return
-	}
-
-	ctx.AddFarVariationDependencies([]blueprint.Variation{
-		{Mutator: "arch", Variation: ctx.Target().String()},
-		{Mutator: "image", Variation: "core"},
-		{Mutator: "link", Variation: "shared"},
-	}, mountsourceTag, String(m.properties.Mountsource))
-}
-
-func (m *bionicMountpoint) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if Bool(m.properties.Library) {
-		m.pathInPartition = "lib"
-		if m.Arch().ArchType.Multilib == "lib64" {
-			m.pathInPartition = "lib64"
-		}
-	} else if Bool(m.properties.Binary) {
-		m.pathInPartition = "bin"
-	}
-
-	m.stem = String(m.properties.Stem) + String(m.properties.Suffix)
-
-	m.outputFile = ctx.ExpandSource(String(m.properties.Src), "src")
-
-	ctx.VisitDirectDepsWithTag(mountsourceTag, func(module android.Module) {
-		if cc, ok := module.(*cc.Module); ok {
-			m.unstrippedOutputFile = cc.UnstrippedOutputFile()
-		}
-	})
-}
-
-func (m *bionicMountpoint) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData {
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			if !m.Arch().Native {
-				return
-			}
-			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
-			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
-			fmt.Fprintln(w, "LOCAL_MODULE :=", name)
-			fmt.Fprintln(w, "LOCAL_USE_CLANG_LLD := false")
-			fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
-			if Bool(m.properties.Library) {
-				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := SHARED_LIBRARIES")
-			} else if Bool(m.properties.Binary) {
-				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := EXECUTABLES")
-			}
-			fmt.Fprintln(w, "LOCAL_MODULE_TAGS := optional")
-			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", m.outputFile.String())
-			fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", m.Arch().ArchType.String())
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", "$(TARGET_ROOT_OUT)/bionic/" + m.pathInPartition)
-			fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", m.stem)
-
-			if len(m.properties.Symlinks) > 0 {
-				symlink_dir_in_system := "$(TARGET_OUT)/" + m.pathInPartition + "/"
-				symlink_dir_in_recovery := "$(TARGET_RECOVERY_ROOT_OUT)/system/" + m.pathInPartition + "/"
-				symlink_target := "/bionic/" + m.pathInPartition + "/" + m.stem
-				cmds := []string{}
-				cmds = append(cmds, "$(hide) mkdir -p " + symlink_dir_in_system)
-				cmds = append(cmds, "mkdir -p " + symlink_dir_in_recovery)
-				for _, s := range m.properties.Symlinks {
-					symlink := s + String(m.properties.Suffix)
-					cmds = append(cmds, "ln -sf " + symlink_target + " " + symlink_dir_in_system +  symlink)
-					cmds = append(cmds, "ln -sf " + symlink_target + " " + symlink_dir_in_recovery +  symlink)
-				}
-				fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD := " + strings.Join(cmds, " && "))
-			}
-			if m.unstrippedOutputFile != nil {
-				fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", m.unstrippedOutputFile.String())
-			}
-			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
-		},
-	}
-}
-
-func bionicMountpointFactory() android.Module {
-	m := &bionicMountpoint{}
-        m.AddProperties(&m.properties)
-	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibBoth)
-	return m
-}
-
-var Bool = proptools.Bool
-var String = proptools.String
diff --git a/build/run-on-host.sh b/build/run-on-host.sh
deleted file mode 100644
index c3a2751..0000000
--- a/build/run-on-host.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/bin/bash -e
-
-source ${ANDROID_BUILD_TOP}/build/envsetup.sh
-
-TARGET_ARCH=$(get_build_var TARGET_ARCH)
-TARGET_OUT=$(get_build_var TARGET_OUT)
-TARGET_OUT_EXECUTABLES=$(get_build_var TARGET_OUT_EXECUTABLES)
-TARGET_OUT_DATA=$(get_build_var TARGET_OUT_DATA)
-HOST_OS=$(get_build_var HOST_OS)
-HOST_ARCH=$(get_build_var HOST_ARCH)
-HOST_OUT=$(get_build_var HOST_OUT)
-
-function prepare()
-{
-    BITS=$1
-    shift
-
-    NATIVETEST=${TARGET_OUT_DATA}/nativetest
-    if [ "${BITS}" = 64 ]; then
-        NATIVETEST=${NATIVETEST}64
-    fi
-
-    if [ ${TARGET_ARCH} = arm -o ${TARGET_ARCH} = mips -o ${TARGET_ARCH} = x86 ]; then
-        LINKER=${TARGET_OUT_EXECUTABLES}/linker
-    else
-        LINKER="${TARGET_OUT_EXECUTABLES}/linker64 ${TARGET_OUT_EXECUTABLES}/linker"
-    fi
-
-    if [ ${TARGET_ARCH} = x86 -o ${TARGET_ARCH} = x86_64 ]; then
-        m -j ${LINKER} ${TARGET_OUT}/etc/hosts ${TARGET_OUT_EXECUTABLES}/sh $@
-
-        if [ ! -d /system ]; then
-            echo "Attempting to create /system";
-            sudo mkdir -p -m 0777 /system;
-        fi
-        (
-            cd ${ANDROID_BUILD_TOP}
-            mkdir -p ${TARGET_OUT_DATA}/local/tmp
-            ln -fs `realpath ${TARGET_OUT}/bin` /system/
-            ln -fs `realpath ${TARGET_OUT}/etc` /system/
-            ln -fs `realpath ${TARGET_OUT}/lib` /system/
-            if [ -d "${TARGET_OUT}/lib64" ]; then
-                ln -fs `realpath ${TARGET_OUT}/lib64` /system/;
-            fi
-        )
-    fi
-}
diff --git a/docs/status.md b/docs/status.md
index 85f9b60..0cbcb47 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -49,6 +49,11 @@
   * `popen` now always uses `O_CLOEXEC`, not just with the `e` extension
   * Bug fixes to handling of UTF-8 U+fffe/U+ffff and code points above U+10ffff
   * `aligned_alloc` correctly verifies that `size` is a multiple of `alignment`
+  * Using `%n` with the printf family is now reported as a FORTIFY failure.
+    Previous versions of Android would ignore the `%n` but not consume the
+    corresponding pointer argument, leading to obscure errors. The scanf family
+    is unchanged.
+  * [fdsan](fdsan.md) detects common file descriptor errors at runtime.
 
 New libc functions in P (API level 28):
   * `aligned_alloc`
@@ -72,6 +77,10 @@
   * `%C` and `%S` support in the printf family (previously only the wprintf family supported these)
   * `%mc`/`%ms`/`%m[` support in the scanf family
   * `%s` support in strptime (strftime already supported it)
+  * Using a `pthread_mutex_t` after it's been destroyed will be detected at
+    runtime and reported as a FORTIFY failure.
+  * Passing a null `FILE*` or `DIR*` to libc is now detected at runtime and
+    reported as a FORTIFY failure.
 
 New libc functions in O (API level 26):
   * `sendto` FORTIFY support
@@ -96,6 +105,11 @@
   * `strtod_l`/`strtof_l`/`strtol_l`/`strtoul_l`
   * <wctype.h> `towctrans`/`towctrans_l`/`wctrans`/`wctrans_l`
 
+New libc behavior in O (API level 26):
+  * Passing an invalid `pthread_t` to libc is now detected at runtime and
+    reported as a FORTIFY failure. Most commonly this is a result of confusing
+    `pthread_t` and `pid_t`.
+
 New libc functions in N (API level 24):
   * more FORTIFY support functions (`fread`/`fwrite`/`getcwd`/`pwrite`/`write`)
   * all remaining `_FILE_OFFSET_BITS=64` functions, completing `_FILE_OFFSET_BITS=64` support in bionic (8)
@@ -108,6 +122,9 @@
   * GNU extensions `fileno_unlocked`/`strchrnul`
   * 32-bit `prlimit`
 
+New libc behavior in N (API level 24):
+  * `sem_wait` now returns EINTR when interrupted by a signal.
+
 New libc functions in M (API level 23):
   * <dirent.h> `telldir`, `seekdir`.
   * <malloc.h> `malloc_info`.
diff --git a/dummy_mountpoint b/dummy_mountpoint
deleted file mode 100644
index 2d13c55..0000000
--- a/dummy_mountpoint
+++ /dev/null
@@ -1 +0,0 @@
-This file serves as a mount point for bionic files either from /system partition or from /apex/com.android.runtime. This file is never meant to be accessed directly.
diff --git a/libc/Android.bp b/libc/Android.bp
index 0e902c1..a27b1ce 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1310,6 +1310,7 @@
     defaults: ["libc_defaults"],
     srcs: libc_common_src_files + [
         "bionic/malloc_common.cpp",
+        "bionic/malloc_limit.cpp",
     ],
     multilib: {
         lib32: {
@@ -1505,6 +1506,7 @@
         "bionic/malloc_common.cpp",
         "bionic/malloc_common_dynamic.cpp",
         "bionic/malloc_heapprofd.cpp",
+        "bionic/malloc_limit.cpp",
         "bionic/NetdClient.cpp",
         "arch-common/bionic/crtend_so.S",
     ],
@@ -1515,6 +1517,7 @@
     srcs: [
         "bionic/dl_iterate_phdr_static.cpp",
         "bionic/malloc_common.cpp",
+        "bionic/malloc_limit.cpp",
     ],
 }
 
@@ -1560,7 +1563,7 @@
         ],
     },
 
-    required: ["tzdata", "libc.mountpoint"],
+    required: ["tzdata"],
 
     // Leave the symbols in the shared library so that stack unwinders can produce
     // meaningful name resolution.
diff --git a/libc/arch-arm64/bionic/vfork.S b/libc/arch-arm64/bionic/vfork.S
index 6acd64b..6c01572 100644
--- a/libc/arch-arm64/bionic/vfork.S
+++ b/libc/arch-arm64/bionic/vfork.S
@@ -51,5 +51,26 @@
     cneg    x0, x0, hi
     b.hi    __set_errno_internal
 
+#if __has_feature(hwaddress_sanitizer)
+    cbz x0, .L_exit
+
+    // Clean up stack shadow in the parent process.
+    // https://github.com/google/sanitizers/issues/925
+    stp x0, x30, [sp, #-16]!
+    .cfi_adjust_cfa_offset 16
+    .cfi_rel_offset x0, 0
+    .cfi_rel_offset x30, 8
+
+    add x0, sp, #16
+    bl __hwasan_handle_vfork
+
+    ldp x0, x30, [sp], #16
+    .cfi_adjust_cfa_offset -16
+    .cfi_restore x0
+    .cfi_restore x30
+
+#endif
+
+.L_exit:
     ret
 END(vfork)
diff --git a/libc/arch-x86/dynamic_function_dispatch.cpp b/libc/arch-x86/dynamic_function_dispatch.cpp
index 6624385..70f4b3e 100644
--- a/libc/arch-x86/dynamic_function_dispatch.cpp
+++ b/libc/arch-x86/dynamic_function_dispatch.cpp
@@ -30,33 +30,6 @@
 
 extern "C" {
 
-struct __processor_model {
-    unsigned int __cpu_vendor;
-    unsigned int __cpu_type;
-    unsigned int __cpu_subtype;
-    unsigned int __cpu_features[1];
-};
-
-__attribute__((visibility("hidden")))
-extern struct __processor_model __cpu_model;
-
-// These definitions have to match the values in
-// llvm/include/llvm/Support/X86TargetParser.def
-static constexpr int SSSE3  = 6;
-static constexpr int SSE4_1 = 7;
-static constexpr int ATOM   = 1;
-
-// __builtin_cpu_supports and __builtin_cpu_is can not be used here. They
-// don't access __cpu_model directly but use GOT.
-// See https://reviews.llvm.org/D53850
-static bool cpu_supports(unsigned int feature) {
-    return (__cpu_model.__cpu_features[0] & (1U << feature)) != 0;
-}
-
-static bool cpu_is(unsigned int type) {
-    return (__cpu_model.__cpu_type == type);
-}
-
 #define DEFINE_IFUNC_FOR(name) \
     name##_func name __attribute__((ifunc(#name "_resolver"))); \
     __attribute__((visibility("hidden"))) \
@@ -74,29 +47,29 @@
 typedef int memcmp_func(const void* __lhs, const void* __rhs, size_t __n);
 DEFINE_IFUNC_FOR(memcmp) {
     __builtin_cpu_init();
-    if (cpu_is(ATOM)) RETURN_FUNC(memcmp_func, memcmp_atom);
-    if (cpu_supports(SSE4_1)) RETURN_FUNC(memcmp_func, memcmp_sse4);
+    if (__builtin_cpu_is("atom")) RETURN_FUNC(memcmp_func, memcmp_atom);
+    if (__builtin_cpu_supports("sse4.1")) RETURN_FUNC(memcmp_func, memcmp_sse4);
     RETURN_FUNC(memcmp_func, memcmp_generic);
 }
 
 typedef void* memset_func(void* __dst, int __ch, size_t __n);
 DEFINE_IFUNC_FOR(memset) {
     __builtin_cpu_init();
-    if (cpu_is(ATOM)) RETURN_FUNC(memset_func, memset_atom);
+    if (__builtin_cpu_is("atom")) RETURN_FUNC(memset_func, memset_atom);
     RETURN_FUNC(memset_func, memset_generic);
 }
 
 typedef void* __memset_chk_func(void *s, int c, size_t n, size_t n2);
 DEFINE_IFUNC_FOR(__memset_chk) {
     __builtin_cpu_init();
-    if (cpu_is(ATOM)) RETURN_FUNC(__memset_chk_func, __memset_chk_atom);
+    if (__builtin_cpu_is("atom")) RETURN_FUNC(__memset_chk_func, __memset_chk_atom);
     RETURN_FUNC(__memset_chk_func, __memset_chk_generic);
 }
 
 typedef void* memmove_func(void* __dst, const void* __src, size_t __n);
 DEFINE_IFUNC_FOR(memmove) {
     __builtin_cpu_init();
-    if (cpu_is(ATOM)) RETURN_FUNC(memmove_func, memmove_atom);
+    if (__builtin_cpu_is("atom")) RETURN_FUNC(memmove_func, memmove_atom);
     RETURN_FUNC(memmove_func, memmove_generic);
 }
 
@@ -108,85 +81,85 @@
 typedef char* strcpy_func(char* __dst, const char* __src);
 DEFINE_IFUNC_FOR(strcpy) {
     __builtin_cpu_init();
-    if (cpu_is(ATOM)) RETURN_FUNC(strcpy_func, strcpy_atom);
+    if (__builtin_cpu_is("atom")) RETURN_FUNC(strcpy_func, strcpy_atom);
     RETURN_FUNC(strcpy_func, strcpy_generic);
 }
 
 typedef char* strncpy_func(char* __dst, const char* __src, size_t __n);
 DEFINE_IFUNC_FOR(strncpy) {
     __builtin_cpu_init();
-    if (cpu_is(ATOM)) RETURN_FUNC(strncpy_func, strncpy_atom);
+    if (__builtin_cpu_is("atom")) RETURN_FUNC(strncpy_func, strncpy_atom);
     RETURN_FUNC(strncpy_func, strncpy_generic);
 }
 
 typedef size_t strlen_func(const char* __s);
 DEFINE_IFUNC_FOR(strlen) {
     __builtin_cpu_init();
-    if (cpu_is(ATOM)) RETURN_FUNC(strlen_func, strlen_atom);
+    if (__builtin_cpu_is("atom")) RETURN_FUNC(strlen_func, strlen_atom);
     RETURN_FUNC(strlen_func, strlen_generic);
 }
 
 typedef int wmemcmp_func(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
 DEFINE_IFUNC_FOR(wmemcmp) {
     __builtin_cpu_init();
-    if (cpu_supports(SSE4_1)) RETURN_FUNC(wmemcmp_func, wmemcmp_sse4);
-    if (cpu_is(ATOM)) RETURN_FUNC(wmemcmp_func, wmemcmp_atom);
+    if (__builtin_cpu_supports("sse4.1")) RETURN_FUNC(wmemcmp_func, wmemcmp_sse4);
+    if (__builtin_cpu_is("atom")) RETURN_FUNC(wmemcmp_func, wmemcmp_atom);
     RETURN_FUNC(wmemcmp_func, wmemcmp_freebsd);
 }
 
 typedef int strcmp_func(const char* __lhs, const char* __rhs);
 DEFINE_IFUNC_FOR(strcmp) {
     __builtin_cpu_init();
-    if (cpu_supports(SSSE3)) RETURN_FUNC(strcmp_func, strcmp_ssse3);
+    if (__builtin_cpu_supports("ssse3")) RETURN_FUNC(strcmp_func, strcmp_ssse3);
     RETURN_FUNC(strcmp_func, strcmp_generic);
 }
 
 typedef int strncmp_func(const char* __lhs, const char* __rhs, size_t __n);
 DEFINE_IFUNC_FOR(strncmp) {
     __builtin_cpu_init();
-    if (cpu_supports(SSSE3)) RETURN_FUNC(strncmp_func, strncmp_ssse3);
+    if (__builtin_cpu_supports("ssse3")) RETURN_FUNC(strncmp_func, strncmp_ssse3);
     RETURN_FUNC(strncmp_func, strncmp_generic);
 }
 
 typedef char* strcat_func(char* __dst, const char* __src);
 DEFINE_IFUNC_FOR(strcat) {
     __builtin_cpu_init();
-    if (cpu_supports(SSSE3)) RETURN_FUNC(strcat_func, strcat_ssse3);
+    if (__builtin_cpu_supports("ssse3")) RETURN_FUNC(strcat_func, strcat_ssse3);
     RETURN_FUNC(strcat_func, strcat_generic);
 }
 
 typedef char* strncat_func(char* __dst, const char* __src, size_t __n);
 DEFINE_IFUNC_FOR(strncat) {
     __builtin_cpu_init();
-    if (cpu_supports(SSSE3)) RETURN_FUNC(strncat_func, strncat_ssse3);
+    if (__builtin_cpu_supports("ssse3")) RETURN_FUNC(strncat_func, strncat_ssse3);
     RETURN_FUNC(strncat_func, strncat_openbsd);
 }
 
 typedef size_t strlcat_func(char *dst, const char *src, size_t dsize);
 DEFINE_IFUNC_FOR(strlcat) {
     __builtin_cpu_init();
-    if (cpu_supports(SSSE3)) RETURN_FUNC(strlcat_func, strlcat_ssse3);
+    if (__builtin_cpu_supports("ssse3")) RETURN_FUNC(strlcat_func, strlcat_ssse3);
     RETURN_FUNC(strlcat_func, strlcat_openbsd);
 }
 
 typedef size_t strlcpy_func(char *dst, const char *src, size_t dsize);
 DEFINE_IFUNC_FOR(strlcpy) {
     __builtin_cpu_init();
-    if (cpu_supports(SSSE3)) RETURN_FUNC(strlcpy_func, strlcpy_ssse3);
+    if (__builtin_cpu_supports("ssse3")) RETURN_FUNC(strlcpy_func, strlcpy_ssse3);
     RETURN_FUNC(strlcpy_func, strlcpy_openbsd);
 }
 
 typedef wchar_t* wcscat_func(wchar_t *s1, const wchar_t *s2);
 DEFINE_IFUNC_FOR(wcscat) {
     __builtin_cpu_init();
-    if (cpu_supports(SSSE3)) RETURN_FUNC(wcscat_func, wcscat_ssse3);
+    if (__builtin_cpu_supports("ssse3")) RETURN_FUNC(wcscat_func, wcscat_ssse3);
     RETURN_FUNC(wcscat_func, wcscat_freebsd);
 }
 
 typedef wchar_t* wcscpy_func(wchar_t *s1, const wchar_t *s2);
 DEFINE_IFUNC_FOR(wcscpy) {
     __builtin_cpu_init();
-    if (cpu_supports(SSSE3)) RETURN_FUNC(wcscpy_func, wcscpy_ssse3);
+    if (__builtin_cpu_supports("ssse3")) RETURN_FUNC(wcscpy_func, wcscpy_ssse3);
     RETURN_FUNC(wcscpy_func, wcscpy_freebsd);
 }
 
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 41f100d..b4bddce 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -202,7 +202,7 @@
   exit(slingshot(args.argc, args.argv, args.envp));
 }
 
-extern "C" void __hwasan_init();
+extern "C" void __hwasan_init_static();
 
 __attribute__((no_sanitize("hwaddress")))
 __noreturn void __libc_init(void* raw_args,
@@ -214,8 +214,9 @@
   // Install main thread TLS early. It will be initialized later in __libc_init_main_thread. For now
   // all we need is access to TLS_SLOT_SANITIZER.
   __set_tls(&temp_tcb.tls_slot(0));
-  // Initialize HWASan. This sets up TLS_SLOT_SANITIZER, among other things.
-  __hwasan_init();
+  // Initialize HWASan enough to run instrumented code. This sets up TLS_SLOT_SANITIZER, among other
+  // things.
+  __hwasan_init_static();
   // We are ready to run HWASan-instrumented code, proceed with libc initialization...
 #endif
   __real_libc_init(raw_args, onexit, slingshot, structors, &temp_tcb);
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index bb3aade..f817281 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -46,8 +46,10 @@
 #include <stdio.h>
 
 #include <private/bionic_config.h>
+#include <private/bionic_malloc.h>
 
 #include "malloc_common.h"
+#include "malloc_limit.h"
 
 // =============================================================================
 // Global variables instantations.
@@ -278,8 +280,10 @@
 // Platform-internal mallopt variant.
 // =============================================================================
 #if defined(LIBC_STATIC)
-extern "C" bool android_mallopt(int, void*, size_t) {
-  // There are no options supported on static executables.
+extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) {
+  if (opcode == M_SET_ALLOCATION_LIMIT_BYTES) {
+    return LimitEnable(arg, arg_size);
+  }
   errno = ENOTSUP;
   return false;
 }
diff --git a/libc/bionic/malloc_common.h b/libc/bionic/malloc_common.h
index 7bfa33a..a40501d 100644
--- a/libc/bionic/malloc_common.h
+++ b/libc/bionic/malloc_common.h
@@ -66,6 +66,10 @@
   return atomic_load_explicit(&__libc_globals->current_dispatch_table, memory_order_acquire);
 }
 
+static inline const MallocDispatch* GetDefaultDispatchTable() {
+  return atomic_load_explicit(&__libc_globals->default_dispatch_table, memory_order_acquire);
+}
+
 // =============================================================================
 // Log functions
 // =============================================================================
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
index 9656718..40f497e 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -57,12 +57,22 @@
 #include <private/bionic_config.h>
 #include <private/bionic_defs.h>
 #include <private/bionic_malloc_dispatch.h>
+#include <private/bionic_malloc.h>
 
 #include <sys/system_properties.h>
 
 #include "malloc_common.h"
 #include "malloc_common_dynamic.h"
 #include "malloc_heapprofd.h"
+#include "malloc_limit.h"
+
+// =============================================================================
+// Global variables instantations.
+// =============================================================================
+pthread_mutex_t gGlobalsMutateLock = PTHREAD_MUTEX_INITIALIZER;
+
+_Atomic bool gGlobalsMutating = false;
+// =============================================================================
 
 static constexpr MallocDispatch __libc_malloc_default_dispatch
   __attribute__((unused)) = {
@@ -292,7 +302,10 @@
 
   // Do a pointer swap so that all of the functions become valid at once to
   // avoid any initialization order problems.
-  atomic_store(&globals->current_dispatch_table, &globals->malloc_dispatch_table);
+  atomic_store(&globals->default_dispatch_table, &globals->malloc_dispatch_table);
+  if (GetDispatchTable() == nullptr) {
+    atomic_store(&globals->current_dispatch_table, &globals->malloc_dispatch_table);
+  }
 
   info_log("%s: malloc %s enabled", getprogname(), prefix);
 
@@ -431,6 +444,9 @@
 // Platform-internal mallopt variant.
 // =============================================================================
 extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) {
+  if (opcode == M_SET_ALLOCATION_LIMIT_BYTES) {
+    return LimitEnable(arg, arg_size);
+  }
   return HeapprofdMallopt(opcode, arg, arg_size);
 }
 // =============================================================================
diff --git a/libc/bionic/malloc_common_dynamic.h b/libc/bionic/malloc_common_dynamic.h
index 8794ed0..755af8f 100644
--- a/libc/bionic/malloc_common_dynamic.h
+++ b/libc/bionic/malloc_common_dynamic.h
@@ -28,7 +28,8 @@
 
 #pragma once
 
-#include <stdbool.h>
+#include <pthread.h>
+#include <stdatomic.h>
 
 #include <private/bionic_globals.h>
 #include <private/bionic_malloc_dispatch.h>
@@ -40,3 +41,7 @@
 void* LoadSharedLibrary(const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table);
 
 bool FinishInstallHooks(libc_globals* globals, const char* options, const char* prefix);
+
+// Lock for globals, to guarantee that only one thread is doing a mutate.
+extern pthread_mutex_t gGlobalsMutateLock;
+extern _Atomic bool gGlobalsMutating;
diff --git a/libc/bionic/malloc_heapprofd.cpp b/libc/bionic/malloc_heapprofd.cpp
index 9cab67a..eda54ce 100644
--- a/libc/bionic/malloc_heapprofd.cpp
+++ b/libc/bionic/malloc_heapprofd.cpp
@@ -32,6 +32,7 @@
 
 #include <dlfcn.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -118,10 +119,27 @@
     return;
   }
 
-  if (!atomic_exchange(&gHeapprofdInitInProgress, true)) {
-    __libc_globals.mutate([](libc_globals* globals) {
-      atomic_store(&globals->current_dispatch_table, &__heapprofd_init_dispatch);
-    });
+  // Checking this variable is only necessary when this could conflict with
+  // the change to enable the allocation limit. All other places will
+  // not ever have a conflict modifying the globals.
+  if (!atomic_exchange(&gGlobalsMutating, true)) {
+    if (!atomic_exchange(&gHeapprofdInitInProgress, true)) {
+      __libc_globals.mutate([](libc_globals* globals) {
+        atomic_store(&globals->default_dispatch_table, &__heapprofd_init_dispatch);
+        auto dispatch_table = GetDispatchTable();
+        if (dispatch_table == nullptr || dispatch_table == &globals->malloc_dispatch_table) {
+          atomic_store(&globals->current_dispatch_table, &__heapprofd_init_dispatch);
+        }
+      });
+    }
+    atomic_store(&gGlobalsMutating, false);
+  } else {
+    // The only way you can get to this point is if the signal has been
+    // blocked by a call to HeapprofdMaskSignal. The raise below will
+    // do nothing until a call to HeapprofdUnmaskSignal, which will cause
+    // the signal to be resent. Using this avoids the need for a busy loop
+    // waiting for gGlobalsMutating to change back to false.
+    raise(kHeapprofdSignal);
   }
 }
 
@@ -212,6 +230,24 @@
   sigaction(kHeapprofdSignal, &action, nullptr);
 }
 
+extern "C" int __rt_sigprocmask(int, const sigset64_t*, sigset64_t*, size_t);
+
+void HeapprofdMaskSignal() {
+  sigset64_t mask_set;
+  // Need to use this function instead because sigprocmask64 filters
+  // out this signal.
+  __rt_sigprocmask(SIG_SETMASK, nullptr, &mask_set, sizeof(mask_set));
+  sigaddset64(&mask_set, kHeapprofdSignal);
+  __rt_sigprocmask(SIG_SETMASK, &mask_set, nullptr, sizeof(mask_set));
+}
+
+void HeapprofdUnmaskSignal() {
+  sigset64_t mask_set;
+  __rt_sigprocmask(SIG_SETMASK, nullptr, &mask_set, sizeof(mask_set));
+  sigdelset64(&mask_set, kHeapprofdSignal);
+  __rt_sigprocmask(SIG_SETMASK, &mask_set, nullptr, sizeof(mask_set));
+}
+
 static void DisplayError(int) {
   error_log("Cannot install heapprofd while malloc debug/malloc hooks are enabled.");
 }
@@ -251,9 +287,11 @@
 }
 
 static void* InitHeapprofd(void*) {
+  pthread_mutex_lock(&gGlobalsMutateLock);
   __libc_globals.mutate([](libc_globals* globals) {
     CommonInstallHooks(globals);
   });
+  pthread_mutex_unlock(&gGlobalsMutateLock);
 
   // Allow to install hook again to re-initialize heap profiling after the
   // current session finished.
@@ -263,9 +301,15 @@
 
 extern "C" void* MallocInitHeapprofdHook(size_t bytes) {
   if (!atomic_exchange(&gHeapprofdInitHookInstalled, true)) {
+    pthread_mutex_lock(&gGlobalsMutateLock);
     __libc_globals.mutate([](libc_globals* globals) {
-      atomic_store(&globals->current_dispatch_table, nullptr);
+      auto old_dispatch = GetDefaultDispatchTable();
+      atomic_store(&globals->default_dispatch_table, nullptr);
+      if (GetDispatchTable() == old_dispatch) {
+        atomic_store(&globals->current_dispatch_table, nullptr);
+      }
     });
+    pthread_mutex_unlock(&gGlobalsMutateLock);
 
     pthread_t thread_id;
     if (pthread_create(&thread_id, nullptr, InitHeapprofd, nullptr) != 0) {
@@ -295,9 +339,15 @@
 
 static bool DispatchReset() {
   if (!atomic_exchange(&gHeapprofdInitInProgress, true)) {
+    pthread_mutex_lock(&gGlobalsMutateLock);
     __libc_globals.mutate([](libc_globals* globals) {
-      atomic_store(&globals->current_dispatch_table, nullptr);
+      auto old_dispatch = GetDefaultDispatchTable();
+      atomic_store(&globals->default_dispatch_table, nullptr);
+      if (GetDispatchTable() == old_dispatch) {
+        atomic_store(&globals->current_dispatch_table, nullptr);
+      }
     });
+    pthread_mutex_unlock(&gGlobalsMutateLock);
     atomic_store(&gHeapprofdInitInProgress, false);
     return true;
   }
diff --git a/libc/bionic/malloc_heapprofd.h b/libc/bionic/malloc_heapprofd.h
index 5a766fc..9e846b6 100644
--- a/libc/bionic/malloc_heapprofd.h
+++ b/libc/bionic/malloc_heapprofd.h
@@ -40,4 +40,8 @@
 
 void HeapprofdInstallErrorSignalHandler();
 
+void HeapprofdMaskSignal();
+
+void HeapprofdUnmaskSignal();
+
 bool HeapprofdMallopt(int optcode, void* arg, size_t arg_size);
diff --git a/libc/bionic/malloc_limit.cpp b/libc/bionic/malloc_limit.cpp
new file mode 100644
index 0000000..69a8f89
--- /dev/null
+++ b/libc/bionic/malloc_limit.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <private/bionic_malloc_dispatch.h>
+
+#if __has_feature(hwaddress_sanitizer)
+#include <sanitizer/allocator_interface.h>
+#endif
+
+#include "malloc_common.h"
+#include "malloc_common_dynamic.h"
+#include "malloc_heapprofd.h"
+#include "malloc_limit.h"
+
+__BEGIN_DECLS
+static void* LimitCalloc(size_t n_elements, size_t elem_size);
+static void LimitFree(void* mem);
+static void* LimitMalloc(size_t bytes);
+static void* LimitMemalign(size_t alignment, size_t bytes);
+static int LimitPosixMemalign(void** memptr, size_t alignment, size_t size);
+static void* LimitRealloc(void* old_mem, size_t bytes);
+static void* LimitAlignedAlloc(size_t alignment, size_t size);
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+static void* LimitPvalloc(size_t bytes);
+static void* LimitValloc(size_t bytes);
+#endif
+
+// Pass through functions.
+static size_t LimitUsableSize(const void* mem);
+static struct mallinfo LimitMallinfo();
+static int LimitIterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*), void* arg);
+static void LimitMallocDisable();
+static void LimitMallocEnable();
+static int LimitMallocInfo(int options, FILE* fp);
+static int LimitMallopt(int param, int value);
+__END_DECLS
+
+static constexpr MallocDispatch __limit_dispatch
+  __attribute__((unused)) = {
+    LimitCalloc,
+    LimitFree,
+    LimitMallinfo,
+    LimitMalloc,
+    LimitUsableSize,
+    LimitMemalign,
+    LimitPosixMemalign,
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    LimitPvalloc,
+#endif
+    LimitRealloc,
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    LimitValloc,
+#endif
+    LimitIterate,
+    LimitMallocDisable,
+    LimitMallocEnable,
+    LimitMallopt,
+    LimitAlignedAlloc,
+    LimitMallocInfo,
+  };
+
+static _Atomic uint64_t gAllocated;
+static uint64_t gAllocLimit;
+
+static inline bool CheckLimit(size_t bytes) {
+  uint64_t total;
+  if (__predict_false(__builtin_add_overflow(
+                          atomic_load_explicit(&gAllocated, memory_order_relaxed), bytes, &total) ||
+                      total > gAllocLimit)) {
+    return false;
+  }
+  return true;
+}
+
+static inline void* IncrementLimit(void* mem) {
+  if (__predict_false(mem == nullptr)) {
+    return nullptr;
+  }
+  atomic_fetch_add(&gAllocated, LimitUsableSize(mem));
+  return mem;
+}
+
+void* LimitCalloc(size_t n_elements, size_t elem_size) {
+  size_t total;
+  if (__builtin_add_overflow(n_elements, elem_size, &total) || !CheckLimit(total)) {
+    warning_log("malloc_limit: calloc(%zu, %zu) exceeds limit %" PRId64, n_elements, elem_size,
+                gAllocLimit);
+    return nullptr;
+  }
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return IncrementLimit(dispatch_table->calloc(n_elements, elem_size));
+  }
+  return IncrementLimit(Malloc(calloc)(n_elements, elem_size));
+}
+
+void LimitFree(void* mem) {
+  atomic_fetch_sub(&gAllocated, LimitUsableSize(mem));
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->free(mem);
+  }
+  return Malloc(free)(mem);
+}
+
+void* LimitMalloc(size_t bytes) {
+  if (!CheckLimit(bytes)) {
+    warning_log("malloc_limit: malloc(%zu) exceeds limit %" PRId64, bytes, gAllocLimit);
+    return nullptr;
+  }
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return IncrementLimit(dispatch_table->malloc(bytes));
+  }
+  return IncrementLimit(Malloc(malloc)(bytes));
+}
+
+static void* LimitMemalign(size_t alignment, size_t bytes) {
+  if (!CheckLimit(bytes)) {
+    warning_log("malloc_limit: memalign(%zu, %zu) exceeds limit %" PRId64, alignment, bytes,
+                gAllocLimit);
+    return nullptr;
+  }
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return IncrementLimit(dispatch_table->memalign(alignment, bytes));
+  }
+  return IncrementLimit(Malloc(memalign)(alignment, bytes));
+}
+
+static int LimitPosixMemalign(void** memptr, size_t alignment, size_t size) {
+  if (!CheckLimit(size)) {
+    warning_log("malloc_limit: posix_memalign(%zu, %zu) exceeds limit %" PRId64, alignment, size,
+                gAllocLimit);
+    return ENOMEM;
+  }
+  int retval;
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    retval = dispatch_table->posix_memalign(memptr, alignment, size);
+  } else {
+    retval = Malloc(posix_memalign)(memptr, alignment, size);
+  }
+  if (__predict_false(retval != 0)) {
+    return retval;
+  }
+  IncrementLimit(*memptr);
+  return 0;
+}
+
+static void* LimitAlignedAlloc(size_t alignment, size_t size) {
+  if (!CheckLimit(size)) {
+    warning_log("malloc_limit: aligned_alloc(%zu, %zu) exceeds limit %" PRId64, alignment, size,
+                gAllocLimit);
+    return nullptr;
+  }
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return IncrementLimit(dispatch_table->aligned_alloc(alignment, size));
+  }
+  return IncrementLimit(Malloc(aligned_alloc)(alignment, size));
+}
+
+static void* LimitRealloc(void* old_mem, size_t bytes) {
+  size_t old_usable_size = LimitUsableSize(old_mem);
+  void* new_ptr;
+  // Need to check the size only if the allocation will increase in size.
+  if (bytes > old_usable_size && !CheckLimit(bytes - old_usable_size)) {
+    warning_log("malloc_limit: realloc(%p, %zu) exceeds limit %" PRId64, old_mem, bytes,
+                gAllocLimit);
+    // Free the old pointer.
+    LimitFree(old_mem);
+    return nullptr;
+  }
+
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    new_ptr = dispatch_table->realloc(old_mem, bytes);
+  } else {
+    new_ptr = Malloc(realloc)(old_mem, bytes);
+  }
+
+  if (__predict_false(new_ptr == nullptr)) {
+    // This acts as if the pointer was freed.
+    atomic_fetch_sub(&gAllocated, old_usable_size);
+    return nullptr;
+  }
+
+  size_t new_usable_size = LimitUsableSize(new_ptr);
+  // Assumes that most allocations increase in size, rather than shrink.
+  if (__predict_false(old_usable_size > new_usable_size)) {
+    atomic_fetch_sub(&gAllocated, old_usable_size - new_usable_size);
+  } else {
+    atomic_fetch_add(&gAllocated, new_usable_size - old_usable_size);
+  }
+  return new_ptr;
+}
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+static void* LimitPvalloc(size_t bytes) {
+  if (!CheckLimit(bytes)) {
+    warning_log("malloc_limit: pvalloc(%zu) exceeds limit %" PRId64, bytes, gAllocLimit);
+    return nullptr;
+  }
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return IncrementLimit(dispatch_table->pvalloc(bytes));
+  }
+  return IncrementLimit(Malloc(pvalloc)(bytes));
+}
+
+static void* LimitValloc(size_t bytes) {
+  if (!CheckLimit(bytes)) {
+    warning_log("malloc_limit: valloc(%zu) exceeds limit %" PRId64, bytes, gAllocLimit);
+    return nullptr;
+  }
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return IncrementLimit(dispatch_table->valloc(bytes));
+  }
+  return IncrementLimit(Malloc(valloc)(bytes));
+}
+#endif
+
+#if defined(LIBC_STATIC)
+static bool EnableLimitDispatchTable() {
+  // This is the only valid way to modify the dispatch tables for a
+  // static executable so no locks are necessary.
+  __libc_globals.mutate([](libc_globals* globals) {
+    atomic_store(&globals->current_dispatch_table, &__limit_dispatch);
+  });
+  return true;
+}
+#else
+static bool EnableLimitDispatchTable() {
+  HeapprofdMaskSignal();
+  pthread_mutex_lock(&gGlobalsMutateLock);
+  // All other code that calls mutate will grab the gGlobalsMutateLock.
+  // However, there is one case where the lock cannot be acquired, in the
+  // signal handler that enables heapprofd. In order to avoid having two
+  // threads calling mutate at the same time, use an atomic variable to
+  // verify that only this function or the signal handler are calling mutate.
+  // If this function is called at the same time as the signal handler is
+  // being called, allow up to five ms for the signal handler to complete
+  // before failing.
+  bool enabled = false;
+  size_t num_tries = 10;
+  while (true) {
+    if (!atomic_exchange(&gGlobalsMutating, true)) {
+      __libc_globals.mutate([](libc_globals* globals) {
+        atomic_store(&globals->current_dispatch_table, &__limit_dispatch);
+      });
+      atomic_store(&gGlobalsMutating, false);
+      enabled = true;
+      break;
+    }
+    if (--num_tries == 0) {
+      break;
+    }
+    usleep(1000);
+  }
+  pthread_mutex_unlock(&gGlobalsMutateLock);
+  HeapprofdUnmaskSignal();
+  if (enabled) {
+    info_log("malloc_limit: Allocation limit enabled, max size %" PRId64 " bytes\n", gAllocLimit);
+  } else {
+    error_log("malloc_limit: Failed to enable allocation limit.");
+  }
+  return enabled;
+}
+#endif
+
+bool LimitEnable(void* arg, size_t arg_size) {
+  if (arg == nullptr || arg_size != sizeof(size_t)) {
+    errno = EINVAL;
+    return false;
+  }
+
+  static _Atomic bool limit_enabled;
+  if (atomic_exchange(&limit_enabled, true)) {
+    // The limit can only be enabled once.
+    error_log("malloc_limit: The allocation limit has already been set, it can only be set once.");
+    return false;
+  }
+
+  gAllocLimit = *reinterpret_cast<size_t*>(arg);
+#if __has_feature(hwaddress_sanitizer)
+  size_t current_allocated = __sanitizer_get_current_allocated_bytes();
+#else
+  size_t current_allocated;
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    current_allocated = dispatch_table->mallinfo().uordblks;
+  } else {
+    current_allocated = Malloc(mallinfo)().uordblks;
+  }
+#endif
+  atomic_store(&gAllocated, current_allocated);
+
+  return EnableLimitDispatchTable();
+}
+
+static size_t LimitUsableSize(const void* mem) {
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->malloc_usable_size(mem);
+  }
+  return Malloc(malloc_usable_size)(mem);
+}
+
+static struct mallinfo LimitMallinfo() {
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->mallinfo();
+  }
+  return Malloc(mallinfo)();
+}
+
+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 Malloc(iterate)(base, size, callback, arg);
+}
+
+static void LimitMallocDisable() {
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    dispatch_table->malloc_disable();
+  } else {
+    Malloc(malloc_disable)();
+  }
+}
+
+static void LimitMallocEnable() {
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    dispatch_table->malloc_enable();
+  } else {
+    Malloc(malloc_enable)();
+  }
+}
+
+static int LimitMallocInfo(int options, FILE* fp) {
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->malloc_info(options, fp);
+  }
+  return Malloc(malloc_info)(options, fp);
+}
+
+static int LimitMallopt(int param, int value) {
+  auto dispatch_table = GetDefaultDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->mallopt(param, value);
+  }
+  return Malloc(mallopt)(param, value);
+}
diff --git a/libc/bionic/malloc_limit.h b/libc/bionic/malloc_limit.h
new file mode 100644
index 0000000..282598f
--- /dev/null
+++ b/libc/bionic/malloc_limit.h
@@ -0,0 +1,34 @@
+/*
+ * 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 <stdint.h>
+
+// Function prototypes.
+bool LimitEnable(void* arg, size_t arg_size);
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index b8784b8..4cf14ad 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -127,18 +127,25 @@
 
 static void __init_shadow_call_stack(pthread_internal_t* thread __unused) {
 #ifdef __aarch64__
-  // Allocate the stack and store its address in register x18. The address is aligned to SCS_SIZE so
-  // that we only need to store the lower log2(SCS_SIZE) bits in jmp_buf.
-  // TODO(pcc): We ought to allocate a larger guard region here and then allocate the SCS at a
-  // random location within it. This will provide greater security since it would mean that an
-  // attacker who can read the pthread_internal_t won't be able to discover the address of the SCS.
-  // However, doing so is blocked on a solution to b/118642754.
+  // Allocate the stack and the guard region.
   char* scs_guard_region = reinterpret_cast<char*>(
       mmap(nullptr, SCS_GUARD_REGION_SIZE, 0, MAP_PRIVATE | MAP_ANON, -1, 0));
   thread->shadow_call_stack_guard_region = scs_guard_region;
 
-  char* scs =
+  // The address is aligned to SCS_SIZE so that we only need to store the lower log2(SCS_SIZE) bits
+  // in jmp_buf.
+  char* scs_aligned_guard_region =
       reinterpret_cast<char*>(align_up(reinterpret_cast<uintptr_t>(scs_guard_region), SCS_SIZE));
+
+  // We need to ensure that [scs_offset,scs_offset+SCS_SIZE) is in the guard region and that there
+  // is at least one unmapped page after the shadow call stack (to catch stack overflows). We can't
+  // use arc4random_uniform in init because /dev/urandom might not have been created yet.
+  size_t scs_offset =
+      (getpid() == 1) ? 0 : (arc4random_uniform(SCS_GUARD_REGION_SIZE / SCS_SIZE - 1) * SCS_SIZE);
+
+  // Make the stack readable and writable and store its address in register x18. This is
+  // deliberately the only place where the address is stored.
+  char *scs = scs_aligned_guard_region + scs_offset;
   mprotect(scs, SCS_SIZE, PROT_READ | PROT_WRITE);
   __asm__ __volatile__("mov x18, %0" ::"r"(scs));
 #endif
diff --git a/libc/include/android/fdsan.h b/libc/include/android/fdsan.h
index ea7689c..1169ed0 100644
--- a/libc/include/android/fdsan.h
+++ b/libc/include/android/fdsan.h
@@ -128,40 +128,40 @@
 /*
  * Create an owner tag with the specified type and least significant 56 bits of tag.
  */
-uint64_t android_fdsan_create_owner_tag(enum android_fdsan_owner_type type, uint64_t tag) __INTRODUCED_IN_FUTURE __attribute__((__weak__));
+uint64_t android_fdsan_create_owner_tag(enum android_fdsan_owner_type type, uint64_t tag) __INTRODUCED_IN(29) __attribute__((__weak__));
 
 /*
  * Exchange a file descriptor's tag.
  *
  * Logs and aborts if the fd's tag does not match expected_tag.
  */
-void android_fdsan_exchange_owner_tag(int fd, uint64_t expected_tag, uint64_t new_tag) __INTRODUCED_IN_FUTURE __attribute__((__weak__));
+void android_fdsan_exchange_owner_tag(int fd, uint64_t expected_tag, uint64_t new_tag) __INTRODUCED_IN(29) __attribute__((__weak__));
 
 /*
  * Close a file descriptor with a tag, and resets the tag to 0.
  *
  * Logs and aborts if the tag is incorrect.
  */
-int android_fdsan_close_with_tag(int fd, uint64_t tag) __INTRODUCED_IN_FUTURE __attribute__((__weak__));
+int android_fdsan_close_with_tag(int fd, uint64_t tag) __INTRODUCED_IN(29) __attribute__((__weak__));
 
 /*
  * Get a file descriptor's current owner tag.
  *
  * Returns 0 for untagged and invalid file descriptors.
  */
-uint64_t android_fdsan_get_owner_tag(int fd);
+uint64_t android_fdsan_get_owner_tag(int fd) __INTRODUCED_IN(29);
 
 /*
  * Get an owner tag's string representation.
  *
  * The return value points to memory with static lifetime, do not attempt to modify it.
  */
-const char* android_fdsan_get_tag_type(uint64_t tag);
+const char* android_fdsan_get_tag_type(uint64_t tag) __INTRODUCED_IN(29);
 
 /*
  * Get an owner tag's value, with the type masked off.
  */
-uint64_t android_fdsan_get_tag_value(uint64_t tag);
+uint64_t android_fdsan_get_tag_value(uint64_t tag) __INTRODUCED_IN(29);
 
 enum android_fdsan_error_level {
   // No errors.
@@ -180,7 +180,7 @@
 /*
  * Get the error level.
  */
-enum android_fdsan_error_level android_fdsan_get_error_level() __INTRODUCED_IN_FUTURE __attribute__((__weak__));
+enum android_fdsan_error_level android_fdsan_get_error_level() __INTRODUCED_IN(29) __attribute__((__weak__));
 
 /*
  * Set the error level and return the previous state.
@@ -195,6 +195,6 @@
  * value, and so should probably only be called in single-threaded contexts
  * (e.g. postfork).
  */
-enum android_fdsan_error_level android_fdsan_set_error_level(enum android_fdsan_error_level new_level) __INTRODUCED_IN_FUTURE __attribute__((__weak__));
+enum android_fdsan_error_level android_fdsan_set_error_level(enum android_fdsan_error_level new_level) __INTRODUCED_IN(29) __attribute__((__weak__));
 
 __END_DECLS
diff --git a/libc/include/android/versioning.h b/libc/include/android/versioning.h
index 01fa348..d60957f 100644
--- a/libc/include/android/versioning.h
+++ b/libc/include/android/versioning.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #define __INTRODUCED_IN(api_level) __attribute__((annotate("introduced_in=" #api_level)))
-#define __INTRODUCED_IN_FUTURE __attribute__((annotate("introduced_in_future")))
 #define __DEPRECATED_IN(api_level) __attribute__((annotate("deprecated_in=" #api_level)))
 #define __REMOVED_IN(api_level) __attribute__((annotate("obsoleted_in=" #api_level)))
 #define __INTRODUCED_IN_32(api_level) __attribute__((annotate("introduced_in_32=" #api_level)))
diff --git a/libc/include/bits/get_device_api_level_inlines.h b/libc/include/bits/get_device_api_level_inlines.h
index 9c6e243..d14eb2c 100644
--- a/libc/include/bits/get_device_api_level_inlines.h
+++ b/libc/include/bits/get_device_api_level_inlines.h
@@ -28,11 +28,9 @@
 
 #pragma once
 
-#include <sys/cdefs.h>
+#if defined(__BIONIC_GET_DEVICE_API_LEVEL_INLINE)
 
-#if !defined(__BIONIC_GET_DEVICE_API_LEVEL_INLINE)
-#define __BIONIC_GET_DEVICE_API_LEVEL_INLINE static inline /* for versioner */
-#endif
+#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
@@ -48,3 +46,5 @@
 }
 
 __END_DECLS
+
+#endif  // __BIONIC_GET_DEVICE_API_LEVEL_INLINE
diff --git a/libc/include/pthread.h b/libc/include/pthread.h
index 3089adc..724e5b7 100644
--- a/libc/include/pthread.h
+++ b/libc/include/pthread.h
@@ -137,7 +137,21 @@
                                         const struct timespec* __timeout) __INTRODUCED_IN_64(28);
 int pthread_cond_wait(pthread_cond_t* __cond, pthread_mutex_t* __mutex);
 
+#if defined(__clang__)
+/*
+ * Disable -Wbuiltin-requires-header because clang confuses this declaration with the one defined in
+ * "llvm/tools/clang/include/clang/Basic/Builtins.def", which did not define any formal arguments.
+ * It seems to be an upstream bug and the fix (https://reviews.llvm.org/D58531) is still under
+ * review. Thus, let's disable the warning for this function declaration.
+ */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wbuiltin-requires-header"
+#endif
 int pthread_create(pthread_t* __pthread_ptr, pthread_attr_t const* __attr, void* (*__start_routine)(void*), void*);
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
 int pthread_detach(pthread_t __pthread);
 void pthread_exit(void* __return_value) __noreturn;
 
diff --git a/libc/include/resolv.h b/libc/include/resolv.h
index 1518475..6318d00 100644
--- a/libc/include/resolv.h
+++ b/libc/include/resolv.h
@@ -60,7 +60,7 @@
 int res_search(const char* __name, int __class, int __type, u_char* __answer, int __answer_size);
 
 #define res_randomid __res_randomid
-u_int __res_randomid(void) __INTRODUCED_IN_FUTURE;
+u_int __res_randomid(void) __INTRODUCED_IN(29);
 
 __END_DECLS
 
diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h
index 96a77a7..d5b8619 100644
--- a/libc/include/stdlib.h
+++ b/libc/include/stdlib.h
@@ -154,7 +154,7 @@
  *
  * Returns the number of samples written to `__averages` (at most 3), and returns -1 on failure.
  */
-int getloadavg(double __averages[], int __n) __INTRODUCED_IN_FUTURE;
+int getloadavg(double __averages[], int __n) __INTRODUCED_IN(29);
 
 /* BSD compatibility. */
 const char* getprogname(void) __INTRODUCED_IN(21);
diff --git a/libc/include/time.h b/libc/include/time.h
index ea41fda..48c5efc 100644
--- a/libc/include/time.h
+++ b/libc/include/time.h
@@ -109,7 +109,7 @@
 time_t timegm(struct tm* __tm) __INTRODUCED_IN(12);
 
 #define TIME_UTC 1
-int timespec_get(struct timespec* __ts, int __base) __INTRODUCED_IN_FUTURE;
+int timespec_get(struct timespec* __ts, int __base) __INTRODUCED_IN(29);
 
 __END_DECLS
 
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 7d30c26..e094967 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1481,7 +1481,7 @@
     # Used by libandroid_net
     android_getaddrinfofornet; # apex
 
-    # Used by libandroid_runtime
+    # Used by libandroid_runtime and libmedia
     android_mallopt; # apex
     gMallocLeakZygoteChild; # apex
 } LIBC_P;
diff --git a/libc/malloc_debug/Android.bp b/libc/malloc_debug/Android.bp
index f808d0c..bcbd7da 100644
--- a/libc/malloc_debug/Android.bp
+++ b/libc/malloc_debug/Android.bp
@@ -145,6 +145,8 @@
 cc_test {
     name: "malloc_debug_system_tests",
 
+    include_dirs: ["bionic/libc"],
+
     srcs: [
         "tests/malloc_debug_system_tests.cpp",
     ],
diff --git a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
index ccefb25..4fcd04c 100644
--- a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
@@ -37,13 +37,15 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
-
 #include <log/log.h>
 
 #include <string>
 #include <vector>
 
+#include "private/bionic_malloc.h"
+
 static constexpr time_t kTimeoutSeconds = 5;
 
 static void Exec(const char* test_name, const char* debug_options, pid_t* pid) {
@@ -60,13 +62,15 @@
     ASSERT_NE(0, dup2(fds[1], STDERR_FILENO));
 
     std::vector<const char*> args;
-    args.push_back(testing::internal::GetArgvs()[0].c_str());
+    // Get a copy of this argument so it doesn't disappear on us.
+    std::string exec(testing::internal::GetArgvs()[0]);
+    args.push_back(exec.c_str());
     args.push_back("--gtest_also_run_disabled_tests");
     std::string filter_arg = std::string("--gtest_filter=") + test_name;
     args.push_back(filter_arg.c_str());
     args.push_back(nullptr);
     execv(args[0], reinterpret_cast<char* const*>(const_cast<char**>(args.data())));
-    exit(1);
+    exit(20);
   }
   ASSERT_NE(-1, *pid);
   close(fds[1]);
@@ -196,16 +200,217 @@
   ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"}));
 }
 
-TEST(MallocTests, DISABLED_leak_memory) {
+static void SetAllocationLimit() {
+  // Set to a large value, this is only to enable the limit code and
+  // verify that malloc debug is still called properly.
+  size_t limit = 500 * 1024 * 1024;
+  ASSERT_TRUE(android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, &limit, sizeof(limit)));
+}
+
+static void AlignedAlloc() {
+  void* ptr = aligned_alloc(64, 1152);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 1152);
+}
+
+TEST(MallocTests, DISABLED_leak_memory_aligned_alloc) {
+  AlignedAlloc();
+}
+
+TEST(MallocTests, DISABLED_leak_memory_limit_aligned_alloc) {
+  SetAllocationLimit();
+  AlignedAlloc();
+}
+
+static void Calloc() {
+  void* ptr = calloc(1, 1123);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 1, 1123);
+}
+
+TEST(MallocTests, DISABLED_leak_memory_calloc) {
+  Calloc();
+}
+
+TEST(MallocTests, DISABLED_leak_memory_limit_calloc) {
+  SetAllocationLimit();
+  Calloc();
+}
+
+static void Malloc() {
   void* ptr = malloc(1123);
   ASSERT_TRUE(ptr != nullptr);
   memset(ptr, 0, 1123);
 }
 
-TEST(MallocDebugSystemTest, verify_leak) {
-  pid_t pid;
-  ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_leak_memory", "backtrace leak_track", &pid));
+TEST(MallocTests, DISABLED_leak_memory_malloc) {
+  Malloc();
+}
 
-  ASSERT_NO_FATAL_FAILURE(FindStrings(
-      pid, std::vector<const char*>{"malloc debug enabled", "leaked block of size 1123 at"}));
+TEST(MallocTests, DISABLED_leak_memory_limit_malloc) {
+  SetAllocationLimit();
+  Malloc();
+}
+
+static void Memalign() {
+  void* ptr = memalign(64, 1123);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 1123);
+}
+
+TEST(MallocTests, DISABLED_leak_memory_memalign) {
+  Memalign();
+}
+
+TEST(MallocTests, DISABLED_leak_memory_limit_memalign) {
+  SetAllocationLimit();
+  Memalign();
+}
+
+static void PosixMemalign() {
+  void* ptr;
+  ASSERT_EQ(0, posix_memalign(&ptr, 64, 1123));
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 1123);
+}
+
+TEST(MallocTests, DISABLED_leak_memory_posix_memalign) {
+  PosixMemalign();
+}
+
+TEST(MallocTests, DISABLED_leak_memory_limit_posix_memalign) {
+  SetAllocationLimit();
+  PosixMemalign();
+}
+
+static void Reallocarray() {
+  void* ptr = reallocarray(nullptr, 1, 1123);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 1123);
+}
+
+TEST(MallocTests, DISABLED_leak_memory_reallocarray) {
+  Reallocarray();
+}
+
+TEST(MallocTests, DISABLED_leak_memory_limit_reallocarray) {
+  SetAllocationLimit();
+  Reallocarray();
+}
+
+static void Realloc() {
+  void* ptr = realloc(nullptr, 1123);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 1123);
+}
+
+TEST(MallocTests, DISABLED_leak_memory_realloc) {
+  Realloc();
+}
+
+TEST(MallocTests, DISABLED_leak_memory_limit_realloc) {
+  SetAllocationLimit();
+  Realloc();
+}
+
+#if !defined(__LP64__)
+extern "C" void* pvalloc(size_t);
+
+static void Pvalloc() {
+  void* ptr = pvalloc(1123);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 1123);
+}
+
+TEST(MallocTests, DISABLED_leak_memory_pvalloc) {
+  Pvalloc();
+}
+
+TEST(MallocTests, DISABLED_leak_memory_limit_pvalloc) {
+  SetAllocationLimit();
+  Pvalloc();
+}
+
+extern "C" void* valloc(size_t);
+
+static void Valloc() {
+  void* ptr = valloc(1123);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 1123);
+}
+
+TEST(MallocTests, DISABLED_leak_memory_valloc) {
+  Valloc();
+}
+
+TEST(MallocTests, DISABLED_leak_memory_limit_valloc) {
+  SetAllocationLimit();
+  Valloc();
+}
+#endif
+
+static void VerifyLeak(const char* test_prefix) {
+  struct FunctionInfo {
+    const char* name;
+    size_t size;
+  };
+  static FunctionInfo functions[] = {
+    {
+      "aligned_alloc",
+      1152,
+    },
+    {
+      "calloc",
+      1123,
+    },
+    {
+      "malloc",
+      1123,
+    },
+    {
+      "memalign",
+      1123,
+    },
+    {
+      "posix_memalign",
+      1123,
+    },
+    {
+      "reallocarray",
+      1123,
+    },
+    {
+      "realloc",
+      1123,
+    },
+#if !defined(__LP64__)
+    {
+      "pvalloc",
+      4096,
+    },
+    {
+      "valloc",
+      1123,
+    }
+#endif
+  };
+
+  for (size_t i = 0; i < sizeof(functions) / sizeof(FunctionInfo); i++) {
+    pid_t pid;
+    SCOPED_TRACE(testing::Message() << functions[i].name << " expected size " << functions[i].size);
+    std::string test = std::string("MallocTests.DISABLED_") + test_prefix + functions[i].name;
+    EXPECT_NO_FATAL_FAILURE(Exec(test.c_str(), "backtrace leak_track", &pid));
+
+    std::string expected_leak = android::base::StringPrintf("leaked block of size %zu at", functions[i].size);
+    EXPECT_NO_FATAL_FAILURE(FindStrings(
+        pid, std::vector<const char*>{"malloc debug enabled", expected_leak.c_str()}));
+  }
+}
+
+TEST(MallocDebugSystemTest, verify_leak) {
+  VerifyLeak("leak_memory_");
+}
+
+TEST(MallocDebugSystemTest, verify_leak_allocation_limit) {
+  VerifyLeak("leak_memory_limit_");
 }
diff --git a/libc/private/bionic_constants.h b/libc/private/bionic_constants.h
index e64c826..09294b6 100644
--- a/libc/private/bionic_constants.h
+++ b/libc/private/bionic_constants.h
@@ -26,7 +26,6 @@
 // guard region must be large enough that we can allocate an SCS_SIZE-aligned SCS while ensuring
 // that there is at least one guard page after the SCS so that a stack overflow results in a SIGSEGV
 // instead of corrupting the allocation that comes after it.
-// TODO(b/118642754): Use a larger guard region.
-#define SCS_GUARD_REGION_SIZE (SCS_SIZE * 2)
+#define SCS_GUARD_REGION_SIZE (16 * 1024 * 1024)
 
 #endif // _BIONIC_CONSTANTS_H_
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index 447b3b9..d73079e 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -55,6 +55,9 @@
   // The malloc_dispatch_table is modified by malloc debug, malloc hooks,
   // and heaprofd. Only one of these modes can be active at any given time.
   _Atomic(const MallocDispatch*) current_dispatch_table;
+  // This pointer is only used by the allocation limit code when both a
+  // limit is enabled and some other hook is enabled at the same time.
+  _Atomic(const MallocDispatch*) default_dispatch_table;
   MallocDispatch malloc_dispatch_table;
 };
 
diff --git a/libc/private/bionic_malloc.h b/libc/private/bionic_malloc.h
index 5f4a75d..a1744aa 100644
--- a/libc/private/bionic_malloc.h
+++ b/libc/private/bionic_malloc.h
@@ -39,6 +39,12 @@
 #define M_INIT_ZYGOTE_CHILD_PROFILING M_INIT_ZYGOTE_CHILD_PROFILING
   M_RESET_HOOKS = 2,
 #define M_RESET_HOOKS M_RESET_HOOKS
+  // Set an upper bound on the total size in bytes of all allocations made
+  // using the memory allocation APIs.
+  //   arg = size_t*
+  //   arg_size = sizeof(size_t)
+  M_SET_ALLOCATION_LIMIT_BYTES = 3,
+#define M_SET_ALLOCATION_LIMIT_BYTES M_SET_ALLOCATION_LIMIT_BYTES
 };
 
 // Manipulates bionic-specific handling of memory allocation APIs such as
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 642cc7a..2e171d6 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -108,7 +108,6 @@
         symbol_file: "libdl.map.txt",
         versions: ["10000"],
     },
-    required: ["libdl.mountpoint"],
 }
 
 ndk_library {
diff --git a/libm/Android.bp b/libm/Android.bp
index 5075fb2..8c32810 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -512,7 +512,6 @@
         symbol_file: "libm.map.txt",
         versions: ["10000"],
     },
-    required: ["libm.mountpoint"],
 }
 
 ndk_library {
diff --git a/linker/Android.bp b/linker/Android.bp
index 613be3d..73328da 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -283,8 +283,6 @@
 
     name: "linker",
     symlinks: ["linker_asan"],
-    // The linker in the system partition is now only for bootstrapping
-    relative_install_path: "bootstrap",
     recovery_available: true,
     multilib: {
         lib32: {
@@ -307,7 +305,6 @@
     },
     compile_multilib: "both",
     xom: false,
-    required: ["linker.mountpoint"],
 }
 
 cc_library {
diff --git a/linker/ld.config.format.md b/linker/ld.config.format.md
index 686d6be..faf5cc8 100644
--- a/linker/ld.config.format.md
+++ b/linker/ld.config.format.md
@@ -79,5 +79,8 @@
 # and links it to default namespace
 namespace.ns.links = default
 namespace.ns.link.default.shared_libs = libc.so:libdl.so:libm.so:libstdc++.so
+
+# This defines what libraries are allowed to be loaded from ns1
+namespace.ns1.whitelisted = libsomething.so
 ```
 
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 56e85e4..c60ab6a 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1124,7 +1124,7 @@
                         ZipArchiveCache* zip_archive_cache,
                         const char* name, soinfo *needed_by,
                         off64_t* file_offset, std::string* realpath) {
-  TRACE("[ opening %s at namespace %s]", name, ns->get_name());
+  TRACE("[ opening %s from namespace %s ]", name, ns->get_name());
 
   // If the name contains a slash, we should attempt to open it directly and not search the paths.
   if (strchr(name, '/') != nullptr) {
@@ -1265,6 +1265,9 @@
   const char* name = task->get_name();
   const android_dlextinfo* extinfo = task->get_extinfo();
 
+  LD_LOG(kLogDlopen, "load_library(ns=%s, task=%s, flags=0x%x, realpath=%s)", ns->get_name(), name,
+         rtld_flags, realpath.c_str());
+
   if ((file_offset % PAGE_SIZE) != 0) {
     DL_ERR("file offset for the library \"%s\" is not page-aligned: %" PRId64, name, file_offset);
     return false;
@@ -1290,8 +1293,10 @@
   if (extinfo == nullptr || (extinfo->flags & ANDROID_DLEXT_FORCE_LOAD) == 0) {
     soinfo* si = nullptr;
     if (find_loaded_library_by_inode(ns, file_stat, file_offset, search_linked_namespaces, &si)) {
-      TRACE("library \"%s\" is already loaded under different name/path \"%s\" - "
-            "will return existing soinfo", name, si->get_realpath());
+      LD_LOG(kLogDlopen,
+             "load_library(ns=%s, task=%s): Already loaded under different name/path \"%s\" - "
+             "will return existing soinfo",
+             ns->get_name(), name, si->get_realpath());
       task->set_soinfo(si);
       return true;
     }
@@ -1392,6 +1397,8 @@
 #endif
 
   for_each_dt_needed(task->get_elf_reader(), [&](const char* name) {
+    LD_LOG(kLogDlopen, "load_library(ns=%s, task=%s): Adding DT_NEEDED task: %s",
+           ns->get_name(), task->get_name(), name);
     load_tasks->push_back(LoadTask::create(name, si, ns, task->get_readers_map()));
   });
 
@@ -1507,17 +1514,24 @@
 
   if (!namespace_link.is_accessible(soname.c_str())) {
     // the library is not accessible via namespace_link
+    LD_LOG(kLogDlopen,
+           "find_library_in_linked_namespace(ns=%s, task=%s): Not accessible (soname=%s)",
+           ns->get_name(), task->get_name(), soname.c_str());
     return false;
   }
 
   // if library is already loaded - return it
   if (loaded) {
+    LD_LOG(kLogDlopen, "find_library_in_linked_namespace(ns=%s, task=%s): Already loaded",
+           ns->get_name(), task->get_name());
     task->set_soinfo(candidate);
     return true;
   }
 
   // returning true with empty soinfo means that the library is okay to be
   // loaded in the namespace but has not yet been loaded there before.
+  LD_LOG(kLogDlopen, "find_library_in_linked_namespace(ns=%s, task=%s): Ok to load", ns->get_name(),
+         task->get_name());
   task->set_soinfo(nullptr);
   return true;
 }
@@ -1531,14 +1545,17 @@
   soinfo* candidate;
 
   if (find_loaded_library_by_soname(ns, task->get_name(), search_linked_namespaces, &candidate)) {
+    LD_LOG(kLogDlopen,
+           "find_library_internal(ns=%s, task=%s): Already loaded (by soname): %s",
+           ns->get_name(), task->get_name(), candidate->get_realpath());
     task->set_soinfo(candidate);
     return true;
   }
 
   // Library might still be loaded, the accurate detection
   // of this fact is done by load_library.
-  TRACE("[ \"%s\" find_loaded_library_by_soname failed (*candidate=%s@%p). Trying harder...]",
-      task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate);
+  TRACE("[ \"%s\" find_loaded_library_by_soname failed (*candidate=%s@%p). Trying harder... ]",
+        task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate);
 
   if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags, search_linked_namespaces)) {
     return true;
@@ -1550,6 +1567,9 @@
     // try the load again from there. The library could be loaded from the
     // default namespace or from another namespace (e.g. runtime) that is linked
     // from the default namespace.
+    LD_LOG(kLogDlopen,
+           "find_library_internal(ns=%s, task=%s): Greylisted library - trying namespace %s",
+           ns->get_name(), task->get_name(), g_default_namespace.get_name());
     ns = &g_default_namespace;
     if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags,
                      search_linked_namespaces)) {
@@ -1562,9 +1582,10 @@
     // if a library was not found - look into linked namespaces
     // preserve current dlerror in the case it fails.
     DlErrorRestorer dlerror_restorer;
+    LD_LOG(kLogDlopen, "find_library_internal(ns=%s, task=%s): Trying %zu linked namespaces",
+           ns->get_name(), task->get_name(), ns->linked_namespaces().size());
     for (auto& linked_namespace : ns->linked_namespaces()) {
-      if (find_library_in_linked_namespace(linked_namespace,
-                                           task)) {
+      if (find_library_in_linked_namespace(linked_namespace, task)) {
         if (task->get_soinfo() == nullptr) {
           // try to load the library - once namespace boundary is crossed
           // we need to load a library within separate load_group
@@ -1575,6 +1596,9 @@
           // Otherwise, the libs in the linked namespace won't get symbols from
           // the global group.
           if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks, rtld_flags, false)) {
+            LD_LOG(
+                kLogDlopen, "find_library_internal(ns=%s, task=%s): Found in linked namespace %s",
+                ns->get_name(), task->get_name(), linked_namespace.linked_namespace()->get_name());
             return true;
           }
         } else {
@@ -1662,6 +1686,9 @@
     task->set_extinfo(is_dt_needed ? nullptr : extinfo);
     task->set_dt_needed(is_dt_needed);
 
+    LD_LOG(kLogDlopen, "find_libraries(ns=%s): task=%s, is_dt_needed=%d", ns->get_name(),
+           task->get_name(), is_dt_needed);
+
     // Note: start from the namespace that is stored in the LoadTask. This namespace
     // is different from the current namespace when the LoadTask is for a transitive
     // dependency and the lib that created the LoadTask is not found in the
@@ -3133,7 +3160,7 @@
           }
         }
         break;
-#endif  // defined(R_GENERIC_TLSDESC)
+#endif  // defined(__aarch64__)
 
 #if defined(__aarch64__)
       case R_AARCH64_ABS64:
@@ -3305,11 +3332,14 @@
     }
   }
 
+#if defined(__aarch64__)
+  // Bionic currently only implements TLSDESC for arm64.
   for (const std::pair<TlsDescriptor*, size_t>& pair : deferred_tlsdesc_relocs) {
     TlsDescriptor* desc = pair.first;
     desc->func = tlsdesc_resolver_dynamic;
     desc->arg = reinterpret_cast<size_t>(&tlsdesc_args_[pair.second]);
   }
+#endif
 
   return true;
 }
@@ -4080,6 +4110,7 @@
 
   std::string ld_config_file_path = get_ld_config_file_path(executable_path);
 
+  INFO("[ Reading linker config \"%s\" ]", ld_config_file_path.c_str());
   if (!Config::read_binary_config(ld_config_file_path.c_str(),
                                   executable_path,
                                   g_is_asan,
@@ -4125,6 +4156,7 @@
     ns->set_isolated(ns_config->isolated());
     ns->set_default_library_paths(ns_config->search_paths());
     ns->set_permitted_paths(ns_config->permitted_paths());
+    ns->set_whitelisted_libs(ns_config->whitelisted_libs());
 
     namespaces[ns_config->name()] = ns;
     if (ns_config->visible()) {
diff --git a/linker/linker_block_allocator.cpp b/linker/linker_block_allocator.cpp
index fdb4c85..1e2f9a2 100644
--- a/linker/linker_block_allocator.cpp
+++ b/linker/linker_block_allocator.cpp
@@ -134,7 +134,7 @@
 
   FreeBlockInfo* first_block = reinterpret_cast<FreeBlockInfo*>(page->bytes);
   first_block->next_block = free_block_list_;
-  first_block->num_free_blocks = (kAllocateSize - sizeof(LinkerBlockAllocatorPage*))/block_size_;
+  first_block->num_free_blocks = sizeof(page->bytes) / block_size_;
 
   free_block_list_ = first_block;
 
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 5a728d3..437aa86 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -80,7 +80,7 @@
         continue;
       }
 
-      if (line[0] == '[' && line[line.size() - 1] == ']') {
+      if (line[0] == '[' && line.back() == ']') {
         *name = line.substr(1, line.size() - 2);
         return kSection;
       }
@@ -269,6 +269,8 @@
     }
   }
 
+  INFO("[ Using config section \"%s\" ]", section_name.c_str());
+
   // skip everything until we meet a correct section
   while (true) {
     std::string name;
@@ -550,6 +552,12 @@
     ns_config->set_isolated(properties.get_bool(property_name_prefix + ".isolated"));
     ns_config->set_visible(properties.get_bool(property_name_prefix + ".visible"));
 
+    std::string whitelisted =
+        properties.get_string(property_name_prefix + ".whitelisted", &lineno);
+    if (!whitelisted.empty()) {
+      ns_config->set_whitelisted_libs(android::base::Split(whitelisted, ":"));
+    }
+
     // these are affected by is_asan flag
     if (is_asan) {
       property_name_prefix += ".asan";
diff --git a/linker/linker_config.h b/linker/linker_config.h
index 49739ee..a318bba 100644
--- a/linker/linker_config.h
+++ b/linker/linker_config.h
@@ -92,6 +92,10 @@
     return permitted_paths_;
   }
 
+  const std::vector<std::string>& whitelisted_libs() const {
+    return whitelisted_libs_;
+  }
+
   const std::vector<NamespaceLinkConfig>& links() const {
     return namespace_links_;
   }
@@ -116,12 +120,17 @@
   void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
     permitted_paths_ = permitted_paths;
   }
+
+  void set_whitelisted_libs(std::vector<std::string>&& whitelisted_libs) {
+    whitelisted_libs_ = whitelisted_libs;
+  }
  private:
   const std::string name_;
   bool isolated_;
   bool visible_;
   std::vector<std::string> search_paths_;
   std::vector<std::string> permitted_paths_;
+  std::vector<std::string> whitelisted_libs_;
   std::vector<NamespaceLinkConfig> namespace_links_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceConfig);
diff --git a/linker/linker_config_test.cpp b/linker/linker_config_test.cpp
index 6a55bb2..4937056 100644
--- a/linker/linker_config_test.cpp
+++ b/linker/linker_config_test.cpp
@@ -56,6 +56,7 @@
   "enable.target.sdk.version = true\n"
   "additional.namespaces=system\n"
   "additional.namespaces+=vndk\n"
+  "additional.namespaces+=vndk_in_system\n"
   "namespace.default.isolated = true\n"
   "namespace.default.search.paths = /vendor/${LIB}\n"
   "namespace.default.permitted.paths = /vendor/${LIB}\n"
@@ -82,6 +83,12 @@
   "namespace.vndk.asan.search.paths += /system/${LIB}/vndk\n"
   "namespace.vndk.links = default\n"
   "namespace.vndk.link.default.allow_all_shared_libs = true\n"
+  "namespace.vndk.link.vndk_in_system.allow_all_shared_libs = true\n"
+  "namespace.vndk_in_system.isolated = true\n"
+  "namespace.vndk_in_system.visible = true\n"
+  "namespace.vndk_in_system.search.paths = /system/${LIB}\n"
+  "namespace.vndk_in_system.permitted.paths = /system/${LIB}\n"
+  "namespace.vndk_in_system.whitelisted = libz.so:libyuv.so:libtinyxml2.so\n"
   "\n";
 
 static bool write_version(const std::string& path, uint32_t version) {
@@ -165,20 +172,24 @@
   ASSERT_FALSE(default_ns_links[1].allow_all_shared_libs());
 
   auto& ns_configs = config->namespace_configs();
-  ASSERT_EQ(3U, ns_configs.size());
+  ASSERT_EQ(4U, ns_configs.size());
 
   // find second namespace
   const NamespaceConfig* ns_system = nullptr;
   const NamespaceConfig* ns_vndk = nullptr;
+  const NamespaceConfig* ns_vndk_in_system = nullptr;
   for (auto& ns : ns_configs) {
     std::string ns_name = ns->name();
-    ASSERT_TRUE(ns_name == "system" || ns_name == "default" || ns_name == "vndk")
+    ASSERT_TRUE(ns_name == "system" || ns_name == "default" ||
+                ns_name == "vndk" || ns_name == "vndk_in_system")
         << "unexpected ns name: " << ns->name();
 
     if (ns_name == "system") {
       ns_system = ns.get();
     } else if (ns_name == "vndk") {
       ns_vndk = ns.get();
+    } else if (ns_name == "vndk_in_system") {
+      ns_vndk_in_system = ns.get();
     }
   }
 
@@ -199,6 +210,11 @@
   ASSERT_EQ(1U, ns_vndk_links.size());
   ASSERT_EQ("default", ns_vndk_links[0].ns_name());
   ASSERT_TRUE(ns_vndk_links[0].allow_all_shared_libs());
+
+  ASSERT_TRUE(ns_vndk_in_system != nullptr) << "vndk_in_system namespace was not found";
+  ASSERT_EQ(
+      std::vector<std::string>({"libz.so", "libyuv.so", "libtinyxml2.so"}),
+      ns_vndk_in_system->whitelisted_libs());
 }
 
 TEST(linker_config, smoke) {
diff --git a/linker/linker_logger.h b/linker/linker_logger.h
index f9fc38e..fedbc05 100644
--- a/linker/linker_logger.h
+++ b/linker/linker_logger.h
@@ -49,7 +49,7 @@
   LinkerLogger() : flags_(0) { }
 
   void ResetState();
-  void Log(const char* format, ...);
+  void Log(const char* format, ...) __printflike(2, 3);
 
   uint32_t IsEnabled(uint32_t type) {
     return flags_ & type;
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 9de7f51..6c762a9 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -353,6 +353,8 @@
   // a C-style string to last until the program exits.
   static std::string exe_path = exe_info.path;
 
+  INFO("[ Linking executable \"%s\" ]", exe_path.c_str());
+
   // Initialize the main exe's soinfo.
   soinfo* si = soinfo_alloc(&g_default_namespace,
                             exe_path.c_str(), &exe_info.file_stat,
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp
index fd72cdc..e870ef7 100644
--- a/linker/linker_namespaces.cpp
+++ b/linker/linker_namespaces.cpp
@@ -38,6 +38,14 @@
     return true;
   }
 
+  if (!whitelisted_libs_.empty()) {
+    const char *lib_name = basename(file.c_str());
+    if (std::find(whitelisted_libs_.begin(), whitelisted_libs_.end(),
+                  lib_name) == whitelisted_libs_.end()) {
+      return false;
+    }
+  }
+
   for (const auto& dir : ld_library_paths_) {
     if (file_is_in_dir(file, dir)) {
       return true;
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
index cd8b09d..31aeeb6 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -110,6 +110,16 @@
     permitted_paths_ = permitted_paths;
   }
 
+  const std::vector<std::string>& get_whitelisted_libs() const {
+    return whitelisted_libs_;
+  }
+  void set_whitelisted_libs(std::vector<std::string>&& whitelisted_libs) {
+    whitelisted_libs_ = whitelisted_libs;
+  }
+  void set_whitelisted_libs(const std::vector<std::string>& whitelisted_libs) {
+    whitelisted_libs_ = whitelisted_libs;
+  }
+
   const std::vector<android_namespace_link_t>& linked_namespaces() const {
     return linked_namespaces_;
   }
@@ -157,6 +167,7 @@
   std::vector<std::string> ld_library_paths_;
   std::vector<std::string> default_library_paths_;
   std::vector<std::string> permitted_paths_;
+  std::vector<std::string> whitelisted_libs_;
   // Loader looks into linked namespace if it was not able
   // to find a library in this namespace. Note that library
   // lookup in linked namespaces are limited by the list of
diff --git a/tests/complex_test.cpp b/tests/complex_test.cpp
index 3bbddbb..f015b2c 100644
--- a/tests/complex_test.cpp
+++ b/tests/complex_test.cpp
@@ -27,7 +27,6 @@
 #define __INTRODUCED_IN(x)
 #define __INTRODUCED_IN_32(x)
 #define __INTRODUCED_IN_64(x)
-#define __INTRODUCED_IN_FUTURE
 #define __RENAME_LDBL(a,b,c)
 #endif
 
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index cace36c..f5e0c9c 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -372,12 +372,12 @@
 }
 
 TEST_F(DlExtTest, ReservedRecursiveTooSmall) {
-  void* start = mmap(nullptr, kLibSize/2, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  void* start = mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   ASSERT_TRUE(start != MAP_FAILED);
   android_dlextinfo extinfo;
   extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE;
   extinfo.reserved_addr = start;
-  extinfo.reserved_size = kLibSize/2;
+  extinfo.reserved_size = PAGE_SIZE;
   handle_ = android_dlopen_ext(kLibNameRecursive, RTLD_NOW, &extinfo);
   EXPECT_EQ(nullptr, handle_);
 }
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index bc6a37b..9380680 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -18,12 +18,17 @@
 
 #include <elf.h>
 #include <limits.h>
+#include <pthread.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #include <malloc.h>
 #include <unistd.h>
 
+#include <atomic>
 #include <tinyxml2.h>
 
 #include <android-base/file.h>
@@ -676,3 +681,239 @@
   GTEST_LOG_(INFO) << "This tests a bionic implementation detail.\n";
 #endif
 }
+
+#if defined(__BIONIC__)
+template <typename FuncType>
+void CheckAllocationFunction(FuncType func) {
+  // Assumes that no more than 108MB of memory is allocated before this.
+  size_t limit = 128 * 1024 * 1024;
+  ASSERT_TRUE(android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, &limit, sizeof(limit)));
+  if (!func(20 * 1024 * 1024))
+    exit(1);
+  if (func(128 * 1024 * 1024))
+    exit(1);
+  exit(0);
+}
+#endif
+
+TEST(android_mallopt, set_allocation_limit) {
+#if defined(__BIONIC__)
+  EXPECT_EXIT(CheckAllocationFunction([](size_t bytes) { return calloc(bytes, 1) != nullptr; }),
+              testing::ExitedWithCode(0), "");
+  EXPECT_EXIT(CheckAllocationFunction([](size_t bytes) { return calloc(1, bytes) != nullptr; }),
+              testing::ExitedWithCode(0), "");
+  EXPECT_EXIT(CheckAllocationFunction([](size_t bytes) { return malloc(bytes) != nullptr; }),
+              testing::ExitedWithCode(0), "");
+  EXPECT_EXIT(CheckAllocationFunction(
+                  [](size_t bytes) { return memalign(sizeof(void*), bytes) != nullptr; }),
+              testing::ExitedWithCode(0), "");
+  EXPECT_EXIT(CheckAllocationFunction([](size_t bytes) {
+                void* ptr;
+                return posix_memalign(&ptr, sizeof(void *), bytes) == 0;
+              }),
+              testing::ExitedWithCode(0), "");
+  EXPECT_EXIT(CheckAllocationFunction(
+                  [](size_t bytes) { return aligned_alloc(sizeof(void*), bytes) != nullptr; }),
+              testing::ExitedWithCode(0), "");
+  EXPECT_EXIT(CheckAllocationFunction([](size_t bytes) {
+                void* p = malloc(1024 * 1024);
+                return realloc(p, bytes) != nullptr;
+              }),
+              testing::ExitedWithCode(0), "");
+#if !defined(__LP64__)
+  EXPECT_EXIT(CheckAllocationFunction([](size_t bytes) { return pvalloc(bytes) != nullptr; }),
+              testing::ExitedWithCode(0), "");
+  EXPECT_EXIT(CheckAllocationFunction([](size_t bytes) { return valloc(bytes) != nullptr; }),
+              testing::ExitedWithCode(0), "");
+#endif
+#else
+  GTEST_LOG_(INFO) << "This tests a bionic extension.\n";
+#endif
+}
+
+TEST(android_mallopt, set_allocation_limit_multiple) {
+#if defined(__BIONIC__)
+  // Only the first set should work.
+  size_t limit = 256 * 1024 * 1024;
+  ASSERT_TRUE(android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, &limit, sizeof(limit)));
+  limit = 32 * 1024 * 1024;
+  ASSERT_FALSE(android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, &limit, sizeof(limit)));
+#else
+  GTEST_LOG_(INFO) << "This tests a bionic extension.\n";
+#endif
+}
+
+#if defined(__BIONIC__)
+static constexpr size_t kAllocationSize = 8 * 1024 * 1024;
+
+static size_t GetMaxAllocations() {
+  size_t max_pointers = 0;
+  void* ptrs[20];
+  for (size_t i = 0; i < sizeof(ptrs) / sizeof(void*); i++) {
+    ptrs[i] = malloc(kAllocationSize);
+    if (ptrs[i] == nullptr) {
+      max_pointers = i;
+      break;
+    }
+  }
+  for (size_t i = 0; i < max_pointers; i++) {
+    free(ptrs[i]);
+  }
+  return max_pointers;
+}
+
+static void VerifyMaxPointers(size_t max_pointers) {
+  // Now verify that we can allocate the same number as before.
+  void* ptrs[20];
+  for (size_t i = 0; i < max_pointers; i++) {
+    ptrs[i] = malloc(kAllocationSize);
+    ASSERT_TRUE(ptrs[i] != nullptr) << "Failed to allocate on iteration " << i;
+  }
+
+  // Make sure the next allocation still fails.
+  ASSERT_TRUE(malloc(kAllocationSize) == nullptr);
+  for (size_t i = 0; i < max_pointers; i++) {
+    free(ptrs[i]);
+  }
+}
+#endif
+
+TEST(android_mallopt, set_allocation_limit_realloc_increase) {
+#if defined(__BIONIC__)
+  size_t limit = 128 * 1024 * 1024;
+  ASSERT_TRUE(android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, &limit, sizeof(limit)));
+
+  size_t max_pointers = GetMaxAllocations();
+  ASSERT_TRUE(max_pointers != 0) << "Limit never reached.";
+
+  void* memory = malloc(10 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+
+  // Increase size.
+  memory = realloc(memory, 20 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+  memory = realloc(memory, 40 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+  memory = realloc(memory, 60 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+  memory = realloc(memory, 80 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+  // Now push past limit.
+  memory = realloc(memory, 130 * 1024 * 1024);
+  ASSERT_TRUE(memory == nullptr);
+
+  VerifyMaxPointers(max_pointers);
+#else
+  GTEST_LOG_(INFO) << "This tests a bionic extension.\n";
+#endif
+}
+
+TEST(android_mallopt, set_allocation_limit_realloc_decrease) {
+#if defined(__BIONIC__)
+  size_t limit = 100 * 1024 * 1024;
+  ASSERT_TRUE(android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, &limit, sizeof(limit)));
+
+  size_t max_pointers = GetMaxAllocations();
+  ASSERT_TRUE(max_pointers != 0) << "Limit never reached.";
+
+  void* memory = malloc(80 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+
+  // Decrease size.
+  memory = realloc(memory, 60 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+  memory = realloc(memory, 40 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+  memory = realloc(memory, 20 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+  memory = realloc(memory, 10 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+  free(memory);
+
+  VerifyMaxPointers(max_pointers);
+#else
+  GTEST_LOG_(INFO) << "This tests a bionic extension.\n";
+#endif
+}
+
+TEST(android_mallopt, set_allocation_limit_realloc_free) {
+#if defined(__BIONIC__)
+  size_t limit = 100 * 1024 * 1024;
+  ASSERT_TRUE(android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, &limit, sizeof(limit)));
+
+  size_t max_pointers = GetMaxAllocations();
+  ASSERT_TRUE(max_pointers != 0) << "Limit never reached.";
+
+  void* memory = malloc(60 * 1024 * 1024);
+  ASSERT_TRUE(memory != nullptr);
+
+  memory = realloc(memory, 0);
+  ASSERT_TRUE(memory == nullptr);
+
+  VerifyMaxPointers(max_pointers);
+#else
+  GTEST_LOG_(INFO) << "This tests a bionic extension.\n";
+#endif
+}
+
+#if defined(__BIONIC__)
+static void* SetAllocationLimit(void* data) {
+  std::atomic_bool* go = reinterpret_cast<std::atomic_bool*>(data);
+  while (!go->load()) {
+  }
+  size_t limit = 500 * 1024 * 1024;
+  if (android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, &limit, sizeof(limit))) {
+    return reinterpret_cast<void*>(-1);
+  }
+  return nullptr;
+}
+
+static void SetAllocationLimitMultipleThreads() {
+  std::atomic_bool go;
+  go = false;
+
+  static constexpr size_t kNumThreads = 4;
+  pthread_t threads[kNumThreads];
+  for (size_t i = 0; i < kNumThreads; i++) {
+    ASSERT_EQ(0, pthread_create(&threads[i], nullptr, SetAllocationLimit, &go));
+  }
+
+  // Let them go all at once.
+  go = true;
+  ASSERT_EQ(0, kill(getpid(), __SIGRTMIN + 4));
+
+  size_t num_successful = 0;
+  for (size_t i = 0; i < kNumThreads; i++) {
+    void* result;
+    ASSERT_EQ(0, pthread_join(threads[i], &result));
+    if (result != nullptr) {
+      num_successful++;
+    }
+  }
+  ASSERT_EQ(1U, num_successful);
+  exit(0);
+}
+#endif
+
+TEST(android_mallopt, set_allocation_limit_multiple_threads) {
+#if defined(__BIONIC__)
+  if (IsDynamic()) {
+    ASSERT_TRUE(android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0));
+  }
+
+  // Run this a number of times as a stress test.
+  for (size_t i = 0; i < 100; i++) {
+    // Not using ASSERT_EXIT because errors messages are not displayed.
+    pid_t pid;
+    if ((pid = fork()) == 0) {
+      ASSERT_NO_FATAL_FAILURE(SetAllocationLimitMultipleThreads());
+    }
+    ASSERT_NE(-1, pid);
+    int status;
+    ASSERT_EQ(pid, wait(&status));
+    ASSERT_EQ(0, WEXITSTATUS(status));
+  }
+#else
+  GTEST_LOG_(INFO) << "This tests a bionic extension.\n";
+#endif
+}
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index cb94e45..10c1710 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -628,6 +628,45 @@
   ASSERT_NE(static_cast<uint64_t>(parent_tid), reinterpret_cast<uint64_t>(result));
 }
 
+static void optimization_barrier(void* arg) {
+  asm volatile("" : : "r"(arg) : "memory");
+}
+
+__attribute__((noinline)) static void HwasanVforkTestChild() {
+  // Allocate a tagged region on stack and leave it there.
+  char x[10000];
+  optimization_barrier(x);
+  _exit(0);
+}
+
+__attribute__((noinline)) static void HwasanReadMemory(const char* p, size_t size) {
+  // Read memory byte-by-byte. This will blow up if the pointer tag in p does not match any memory
+  // tag in [p, p+size).
+  volatile char z;
+  for (size_t i = 0; i < size; ++i) {
+    z = p[i];
+  }
+}
+
+__attribute__((noinline, no_sanitize("hwaddress"))) static void HwasanVforkTestParent() {
+  // Allocate a region on stack, but don't tag it (see the function attribute).
+  // This depends on unallocated stack space at current function entry being untagged.
+  char x[10000];
+  optimization_barrier(x);
+  // Verify that contents of x[] are untagged.
+  HwasanReadMemory(x, sizeof(x));
+}
+
+TEST(UNISTD_TEST, hwasan_vfork) {
+  // Test hwasan annotation in vfork. This test is only interesting when built with hwasan, but it
+  // is supposed to work correctly either way.
+  if (vfork()) {
+    HwasanVforkTestParent();
+  } else {
+    HwasanVforkTestChild();
+  }
+}
+
 class UNISTD_DEATHTEST : public BionicDeathTest {};
 
 TEST_F(UNISTD_DEATHTEST, abort) {
diff --git a/tools/versioner/src/Arch.h b/tools/versioner/src/Arch.h
index bac9ec4..16fa265 100644
--- a/tools/versioner/src/Arch.h
+++ b/tools/versioner/src/Arch.h
@@ -138,7 +138,7 @@
   { Arch::x86_64, "x86_64-linux-android" },
 };
 
-static const std::set<int> default_levels = { 14, 15, 16, 17, 18, 19, 21, 23, 24, 25, 26, 27, 28 };
+static const std::set<int> default_levels = { 14, 15, 16, 17, 18, 19, 21, 23, 24, 25, 26, 27, 28, 29 };
 
 static const ArchMap<int> arch_min_api = {
   { Arch::arm, 9 },
@@ -149,8 +149,6 @@
   { Arch::x86_64, 21 },
 };
 
-static constexpr int future_api = 10000;
-
 static const std::unordered_map<std::string, int> api_codename_map{
   {"G", 9},
   {"I", 14},
@@ -166,5 +164,5 @@
   {"O", 26},
   {"O-MR1", 27},
   {"P", 28},
-  {"Q", 9001},
+  {"Q", 29},
 };
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index d9cff17..0ba51d1 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -147,9 +147,6 @@
       llvm::StringRef annotation = attr->getAnnotation();
       if (annotation == "versioner_no_guard") {
         no_guard = true;
-      } else if (annotation == "introduced_in_future") {
-        // Tag the compiled-for arch, since this can vary across archs.
-        availability.arch_availability[type.arch].future = true;
       } else {
         llvm::SmallVector<llvm::StringRef, 2> fragments;
         annotation.split(fragments, "=");
@@ -345,10 +342,6 @@
 std::string to_string(const AvailabilityValues& av) {
   std::stringstream ss;
 
-  if (av.future) {
-    ss << "future, ";
-  }
-
   if (av.introduced != 0) {
     ss << "introduced = " << av.introduced << ", ";
   }
diff --git a/tools/versioner/src/DeclarationDatabase.h b/tools/versioner/src/DeclarationDatabase.h
index 0daa2cd..4496ee9 100644
--- a/tools/versioner/src/DeclarationDatabase.h
+++ b/tools/versioner/src/DeclarationDatabase.h
@@ -42,13 +42,12 @@
 };
 
 struct AvailabilityValues {
-  bool future = false;
   int introduced = 0;
   int deprecated = 0;
   int obsoleted = 0;
 
   bool empty() const {
-    return !(future || introduced || deprecated || obsoleted);
+    return !(introduced || deprecated || obsoleted);
   }
 
   bool operator==(const AvailabilityValues& rhs) const {
diff --git a/tools/versioner/src/Preprocessor.cpp b/tools/versioner/src/Preprocessor.cpp
index a7f289b..9eac2ab 100644
--- a/tools/versioner/src/Preprocessor.cpp
+++ b/tools/versioner/src/Preprocessor.cpp
@@ -190,13 +190,6 @@
 
     int version = avail.arch_availability[*it.second.begin()].introduced;
 
-    // Assume that the entire declaration is declared __INTRODUCED_IN_FUTURE if one arch is.
-    bool future = avail.arch_availability[*it.second.begin()].future;
-
-    if (future) {
-      return "__ANDROID_API__ >= __ANDROID_API_FUTURE__";
-    }
-
     // The maximum min_version of the set.
     int max_min_version = 0;
     for (Arch arch : archs) {
diff --git a/tools/versioner/src/SymbolFileParser.cpp b/tools/versioner/src/SymbolFileParser.cpp
index 33308d9..c312b48 100644
--- a/tools/versioner/src/SymbolFileParser.cpp
+++ b/tools/versioner/src/SymbolFileParser.cpp
@@ -257,10 +257,6 @@
         intro_arch = true;
         continue;
       }
-
-      if (tag == "future") {
-        return compilation_type.api_level == future_api;
-      }
     }
 
     if (intro.empty() && api_level.empty()) {
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index 73eff0e..d3c2f7c 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -397,10 +397,6 @@
         should_be_available = false;
       }
 
-      if (arch_availability.future) {
-        continue;
-      }
-
       // The function declaration might be (validly) missing for the given CompilationType.
       if (!symbol_it.second.hasDeclaration(type)) {
         should_be_available = false;
diff --git a/tools/versioner/tests/future/headers/foo.h b/tools/versioner/tests/future/headers/foo.h
deleted file mode 100644
index 51a3a1c..0000000
--- a/tools/versioner/tests/future/headers/foo.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-int foo() __INTRODUCED_IN_FUTURE;
-
-#if defined(__cplusplus)
-}
-#endif
diff --git a/tools/versioner/tests/future/platforms/libc.map.txt b/tools/versioner/tests/future/platforms/libc.map.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/versioner/tests/future/platforms/libc.map.txt
+++ /dev/null
diff --git a/tools/versioner/tests/future/run.sh b/tools/versioner/tests/future/run.sh
deleted file mode 100644
index 041b047..0000000
--- a/tools/versioner/tests/future/run.sh
+++ /dev/null
@@ -1 +0,0 @@
-versioner -v headers -p platforms -r arm -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/future_arch/headers/foo.h b/tools/versioner/tests/future_arch/headers/foo.h
deleted file mode 100644
index a3258e7..0000000
--- a/tools/versioner/tests/future_arch/headers/foo.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-#if defined(__arm__)
-int foo() __INTRODUCED_IN(9);
-#else
-int foo() __INTRODUCED_IN_FUTURE;
-#endif
-
-#if defined(__cplusplus)
-}
-#endif
diff --git a/tools/versioner/tests/future_arch/platforms/libc.map.txt b/tools/versioner/tests/future_arch/platforms/libc.map.txt
deleted file mode 100644
index f56190e..0000000
--- a/tools/versioner/tests/future_arch/platforms/libc.map.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-LIBC {
-  global:
-    foo;  # arm
-};
diff --git a/tools/versioner/tests/future_arch/run.sh b/tools/versioner/tests/future_arch/run.sh
deleted file mode 100644
index ad8f430..0000000
--- a/tools/versioner/tests/future_arch/run.sh
+++ /dev/null
@@ -1 +0,0 @@
-versioner -v headers -p platforms -r arm -r x86 -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/preprocessor/expected/foo.h b/tools/versioner/tests/preprocessor/expected/foo.h
index cb719f0..61e6056 100644
--- a/tools/versioner/tests/preprocessor/expected/foo.h
+++ b/tools/versioner/tests/preprocessor/expected/foo.h
@@ -74,11 +74,6 @@
 
 
 
-#if __ANDROID_API__ >= __ANDROID_API_FUTURE__
-int future() __INTRODUCED_IN_FUTURE;
-#endif /* __ANDROID_API__ >= __ANDROID_API_FUTURE__ */
-
-
 #if defined(__cplusplus)
 }
 #endif
diff --git a/tools/versioner/tests/preprocessor/headers/foo.h b/tools/versioner/tests/preprocessor/headers/foo.h
index 2429334..349e5e6 100644
--- a/tools/versioner/tests/preprocessor/headers/foo.h
+++ b/tools/versioner/tests/preprocessor/headers/foo.h
@@ -45,8 +45,6 @@
 
 int group_lp32() __INTRODUCED_IN_ARM(12) __INTRODUCED_IN_X86(12) __INTRODUCED_IN_MIPS(12);
 
-int future() __INTRODUCED_IN_FUTURE;
-
 #if defined(__cplusplus)
 }
 #endif