Merge "Fix internal uses of _PATH_BSHELL."
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/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/include/android/fdsan.h b/libc/include/android/fdsan.h
index d71e6d4..1169ed0 100644
--- a/libc/include/android/fdsan.h
+++ b/libc/include/android/fdsan.h
@@ -149,19 +149,19 @@
  *
  * 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.
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/paths.h b/libc/include/paths.h
index b5b8610..0cf2789 100644
--- a/libc/include/paths.h
+++ b/libc/include/paths.h
@@ -47,7 +47,7 @@
 #define _PATH_CONSOLE "/dev/console"
 
 /** Default shell search path. */
-#define _PATH_DEFPATH "/sbin:/system/sbin:/apex/com.android.runtime/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin"
+#define _PATH_DEFPATH "/sbin:/system/sbin:/product/bin:/apex/com.android.runtime/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin"
 
 /** Path to the directory containing device files. */
 #define _PATH_DEV "/dev/"
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/sys/param.h b/libc/include/sys/param.h
index 5cde4b7..16fed86 100644
--- a/libc/include/sys/param.h
+++ b/libc/include/sys/param.h
@@ -51,8 +51,17 @@
 #endif
 #define roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
 
-/** Returns true if the argument is a power of two. */
-#define powerof2(x) ((((x)-1)&(x))==0)
+/*
+ * Returns true if the binary representation of the argument is all zeros
+ * or has exactly one bit set. Contrary to the macro name, this macro
+ * DOES NOT determine if the provided value is a power of 2. In particular,
+ * this function falsely returns true for powerof2(0) and some negative
+ * numbers.
+ */
+#define powerof2(x) \
+  ({ __typeof__(x) _x = (x); \
+     __typeof__(x) _x2; \
+     __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2&_x) == 0 ); })
 
 /** Returns the lesser of its two arguments. */
 #define MIN(a,b) (((a)<(b))?(a):(b))
diff --git a/libc/kernel/uapi/linux/android/binder.h b/libc/kernel/uapi/linux/android/binder.h
index 2d00a79..542cf1c 100644
--- a/libc/kernel/uapi/linux/android/binder.h
+++ b/libc/kernel/uapi/linux/android/binder.h
@@ -34,6 +34,7 @@
 enum {
   FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
   FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
+  FLAT_BINDER_FLAG_TXN_SECURITY_CTX = 0x1000,
 };
 #ifdef BINDER_IPC_32BIT
 typedef __u32 binder_size_t;
@@ -120,6 +121,7 @@
 #define BINDER_VERSION _IOWR('b', 9, struct binder_version)
 #define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info)
 #define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref)
+#define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object)
 enum transaction_flags {
   TF_ONE_WAY = 0x01,
   TF_ROOT_OBJECT = 0x04,
@@ -146,6 +148,10 @@
     __u8 buf[8];
   } data;
 };
+struct binder_transaction_data_secctx {
+  struct binder_transaction_data transaction_data;
+  binder_uintptr_t secctx;
+};
 struct binder_transaction_data_sg {
   struct binder_transaction_data transaction_data;
   binder_size_t buffers_size;
@@ -170,6 +176,7 @@
 enum binder_driver_return_protocol {
   BR_ERROR = _IOR('r', 0, __s32),
   BR_OK = _IO('r', 1),
+  BR_TRANSACTION_SEC_CTX = _IOR('r', 2, struct binder_transaction_data_secctx),
   BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
   BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
   BR_ACQUIRE_RESULT = _IOR('r', 4, __s32),
diff --git a/libc/kernel/uapi/linux/version.h b/libc/kernel/uapi/linux/version.h
index 6ec3412..43c04d1 100644
--- a/libc/kernel/uapi/linux/version.h
+++ b/libc/kernel/uapi/linux/version.h
@@ -16,5 +16,5 @@
  ***
  ****************************************************************************
  ****************************************************************************/
-#define LINUX_VERSION_CODE 327680
+#define LINUX_VERSION_CODE 327683
 #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index d482a91..dc23200 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -1170,11 +1170,6 @@
   return (__sfvwrite(fp, &uio) == 0) ? count : ((n - uio.uio_resid) / size);
 }
 
-static int __close_if_popened(FILE* fp) {
-  if (_EXT(fp)->_popen_pid > 0) close(fileno(fp));
-  return 0;
-}
-
 static FILE* __popen_fail(int fds[2]) {
   ErrnoRestorer errno_restorer;
   close(fds[0]);
@@ -1220,9 +1215,6 @@
 
   if (pid == 0) {
     close(fds[parent]);
-    // POSIX says "The popen() function shall ensure that any streams from previous popen() calls
-    // that remain open in the parent process are closed in the new child process."
-    _fwalk(__close_if_popened);
     // dup2 so that the child fd isn't closed on exec.
     if (dup2(fds[child], desired_child_fd) == -1) _exit(127);
     close(fds[child]);
diff --git a/libc/tzcode/bionic.cpp b/libc/tzcode/bionic.cpp
index 9051308..1742d79 100644
--- a/libc/tzcode/bionic.cpp
+++ b/libc/tzcode/bionic.cpp
@@ -228,13 +228,18 @@
   if (fd >= 0) return fd;
 #else
   // On the host, we don't expect those locations to exist, and we're not
-  // worried about security so we trust $ANDROID_DATA, $ANDROID_RUNTIME_ROOT
-  // and $ANDROID_ROOT to point us in the right direction.
+  // worried about security so we trust $ANDROID_DATA, $ANDROID_RUNTIME_ROOT,
+  // $ANDROID_TZDATA_ROOT, and $ANDROID_ROOT to point us in the right direction.
   char* path = make_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata");
   fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
   free(path);
   if (fd >= 0) return fd;
 
+  path = make_path("ANDROID_TZDATA_ROOT", "/etc/tz/tzdata");
+  fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
+  free(path);
+  if (fd >= 0) return fd;
+
   path = make_path("ANDROID_RUNTIME_ROOT", "/etc/tz/tzdata");
   fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
   free(path);
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 49c8f11..c60ab6a 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -4156,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_config.cpp b/linker/linker_config.cpp
index f7f9c41..7741904 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -60,7 +60,7 @@
   };
 
   explicit ConfigParser(std::string&& content)
-      : content_(content), p_(0), lineno_(0), was_end_of_file_(false) {}
+      : content_(std::move(content)), p_(0), lineno_(0), was_end_of_file_(false) {}
 
   /*
    * Possible return values
@@ -147,7 +147,7 @@
   PropertyValue() = default;
 
   PropertyValue(std::string&& value, size_t lineno)
-    : value_(value), lineno_(lineno) {}
+    : value_(std::move(value)), lineno_(lineno) {}
 
   const std::string& value() const {
     return value_;
@@ -362,7 +362,7 @@
 class Properties {
  public:
   explicit Properties(std::unordered_map<std::string, PropertyValue>&& properties)
-      : properties_(properties), target_sdk_version_(__ANDROID_API__) {}
+      : properties_(std::move(properties)), target_sdk_version_(__ANDROID_API__) {}
 
   std::vector<std::string> get_strings(const std::string& name, size_t* lineno = nullptr) const {
     auto it = find_property(name, lineno);
@@ -411,7 +411,7 @@
     static std::string vndk = Config::get_vndk_version_string('-');
     params.push_back({ "VNDK_VER", vndk });
 
-    for (auto&& path : paths) {
+    for (auto& path : paths) {
       format_string(&path, params);
     }
 
@@ -552,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..75d9378 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_;
   }
@@ -110,11 +114,15 @@
   }
 
   void set_search_paths(std::vector<std::string>&& search_paths) {
-    search_paths_ = search_paths;
+    search_paths_ = std::move(search_paths);
   }
 
   void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
-    permitted_paths_ = permitted_paths;
+    permitted_paths_ = std::move(permitted_paths);
+  }
+
+  void set_whitelisted_libs(std::vector<std::string>&& whitelisted_libs) {
+    whitelisted_libs_ = std::move(whitelisted_libs);
   }
  private:
   const std::string name_;
@@ -122,6 +130,7 @@
   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_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..f4428eb 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -87,14 +87,14 @@
     return ld_library_paths_;
   }
   void set_ld_library_paths(std::vector<std::string>&& library_paths) {
-    ld_library_paths_ = library_paths;
+    ld_library_paths_ = std::move(library_paths);
   }
 
   const std::vector<std::string>& get_default_library_paths() const {
     return default_library_paths_;
   }
   void set_default_library_paths(std::vector<std::string>&& library_paths) {
-    default_library_paths_ = library_paths;
+    default_library_paths_ = std::move(library_paths);
   }
   void set_default_library_paths(const std::vector<std::string>& library_paths) {
     default_library_paths_ = library_paths;
@@ -104,12 +104,22 @@
     return permitted_paths_;
   }
   void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
-    permitted_paths_ = permitted_paths;
+    permitted_paths_ = std::move(permitted_paths);
   }
   void set_permitted_paths(const std::vector<std::string>& permitted_paths) {
     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_ = std::move(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/Android.bp b/tests/Android.bp
index ab11d47..408312a 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -153,6 +153,7 @@
         "sys_epoll_test.cpp",
         "sys_mman_test.cpp",
         "sys_msg_test.cpp",
+        "sys_param_test.cpp",
         "sys_personality_test.cpp",
         "sys_prctl_test.cpp",
         "sys_procfs_test.cpp",
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 9380680..8d5db54 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -341,6 +341,7 @@
 
 TEST(malloc, malloc_info) {
 #ifdef __BIONIC__
+  SKIP_WITH_HWASAN; // hwasan does not implement malloc_info
   char* buf;
   size_t bufsize;
   FILE* memstream = open_memstream(&buf, &bufsize);
diff --git a/tests/sys_param_test.cpp b/tests/sys_param_test.cpp
new file mode 100644
index 0000000..e4bbf42
--- /dev/null
+++ b/tests/sys_param_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <sys/param.h>
+
+TEST(sys_param_test, powerof2_positives) {
+  ASSERT_TRUE(powerof2(1));
+  ASSERT_TRUE(powerof2(2));
+  ASSERT_TRUE(powerof2(4));
+  ASSERT_TRUE(powerof2(8));
+  ASSERT_FALSE(powerof2(3));
+  ASSERT_FALSE(powerof2(5));
+  ASSERT_FALSE(powerof2(7));
+  ASSERT_FALSE(powerof2(9));
+  ASSERT_FALSE(powerof2(10));
+}
+
+TEST(sys_param_test, powerof2_zero) {
+  // 0 isn't a power of 2, but for compatibility, we assume it is.
+  ASSERT_TRUE(powerof2(0));
+  uint32_t zero = 0;
+  ASSERT_TRUE(powerof2(zero));
+}
+
+TEST(sys_param_test, powerof2_negatives) {
+  // negative numbers can never be a power of 2, but for compatibility,
+  // we assume they can be.
+  int32_t min32 = INT32_MIN;
+  int64_t min64 = INT64_MIN;
+  ASSERT_TRUE(powerof2(min32));
+  ASSERT_FALSE(powerof2(min32 + 1));
+  ASSERT_TRUE(powerof2(min64));
+  ASSERT_FALSE(powerof2(min64 + 1));
+}