Merge "Add deprecation message for __sF." into main
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index a2bb1db..1bbdb29 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -77,9 +77,13 @@
 int je_mallopt(int param, int value) {
   // The only parameter we currently understand is M_DECAY_TIME.
   if (param == M_DECAY_TIME) {
-    // Only support setting the value to 1 or 0.
+    // Only support setting the value to -1 or 0 or 1.
     ssize_t decay_time_ms;
-    if (value) {
+    if (value < 0) {
+      // Given that SSIZE_MAX may not be supported in jemalloc, set this to a
+      // sufficiently large number that essentially disables the decay timer.
+      decay_time_ms = 10000000;
+    } else if (value) {
       decay_time_ms = 1000;
     } else {
       decay_time_ms = 0;
diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index 1180a51..2dde2f1 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -61,7 +61,7 @@
 };
 
 void memtag_stack_dlopen_callback() {
-  async_safe_format_log(ANDROID_LOG_INFO, "libc", "remapping stacks as PROT_MTE");
+  async_safe_format_log(ANDROID_LOG_DEBUG, "libc", "remapping stacks as PROT_MTE");
   __pthread_internal_remap_stack_with_mte();
 }
 
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 9932e3e..596a1fc 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -123,7 +123,7 @@
   // Track the M_DECAY_TIME mallopt calls.
   if (param == M_DECAY_TIME && retval == 1) {
     __libc_globals.mutate([value](libc_globals* globals) {
-      if (value == 0) {
+      if (value <= 0) {
         atomic_store(&globals->decay_time_enabled, false);
       } else {
         atomic_store(&globals->decay_time_enabled, true);
diff --git a/libc/include/malloc.h b/libc/include/malloc.h
index d22b85c..ef1e27d 100644
--- a/libc/include/malloc.h
+++ b/libc/include/malloc.h
@@ -186,7 +186,11 @@
 int malloc_info(int __must_be_zero, FILE* _Nonnull __fp) __INTRODUCED_IN(23);
 
 /**
- * mallopt() option to set the decay time. Valid values are 0 and 1.
+ * mallopt() option to set the decay time. Valid values are -1, 0 and 1.
+ *   -1 : Disable the releasing of unused pages. This value is available since
+ *        API level 35.
+ *    0 : Release the unused pages immediately.
+ *    1 : Release the unused pages at a device-specific interval.
  *
  * Available since API level 27.
  */
diff --git a/libc/system_properties/system_properties.cpp b/libc/system_properties/system_properties.cpp
index 1dedb61..e0d38a8 100644
--- a/libc/system_properties/system_properties.cpp
+++ b/libc/system_properties/system_properties.cpp
@@ -337,31 +337,42 @@
 
 int SystemProperties::Add(const char* name, unsigned int namelen, const char* value,
                           unsigned int valuelen) {
-  if (valuelen >= PROP_VALUE_MAX && !is_read_only(name)) {
+  if (namelen < 1) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "__system_property_add failed: name length 0");
     return -1;
   }
 
-  if (namelen < 1) {
+  if (valuelen >= PROP_VALUE_MAX && !is_read_only(name)) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "__system_property_add failed: \"%s\" value too long: %d >= PROP_VALUE_MAX",
+                          name, valuelen);
     return -1;
   }
 
   if (!initialized_) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "__system_property_add failed: properties not initialized");
     return -1;
   }
 
   prop_area* serial_pa = contexts_->GetSerialPropArea();
   if (serial_pa == nullptr) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "__system_property_add failed: property area not found");
     return -1;
   }
 
   prop_area* pa = contexts_->GetPropAreaForName(name);
   if (!pa) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name);
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "__system_property_add failed: access denied for \"%s\"", name);
     return -1;
   }
 
-  bool ret = pa->add(name, namelen, value, valuelen);
-  if (!ret) {
+  if (!pa->add(name, namelen, value, valuelen)) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "__system_property_add failed: add failed for \"%s\"", name);
     return -1;
   }
 
diff --git a/libc/upstream-openbsd/android/include/openbsd-compat.h b/libc/upstream-openbsd/android/include/openbsd-compat.h
index 8e6f87d..cbc52b5 100644
--- a/libc/upstream-openbsd/android/include/openbsd-compat.h
+++ b/libc/upstream-openbsd/android/include/openbsd-compat.h
@@ -42,23 +42,8 @@
 
 #define PROTO_NORMAL(x)
 
-/* OpenBSD's <ctype.h> uses these names, which conflicted with stlport.
- * Additionally, we changed the numeric/digit type from N to D for libcxx.
- */
-#define _U _CTYPE_U
-#define _L _CTYPE_L
-#define _N _CTYPE_D
-#define _S _CTYPE_S
-#define _P _CTYPE_P
-#define _C _CTYPE_C
-#define _X _CTYPE_X
-#define _B _CTYPE_B
-
-/* OpenBSD has this, but we can't really implement it correctly on Linux. */
-#define issetugid() 0
-
 #if !defined(ANDROID_HOST_MUSL)
-#define explicit_bzero(p, s) memset(p, 0, s)
+#define explicit_bzero(p, s) memset_explicit(p, 0, s)
 #endif
 
 #if defined(ANDROID_HOST_MUSL)
diff --git a/libc/upstream-openbsd/lib/libc/crypt/arc4random.c b/libc/upstream-openbsd/lib/libc/crypt/arc4random.c
index 8a4ecc9..0737cf3 100644
--- a/libc/upstream-openbsd/lib/libc/crypt/arc4random.c
+++ b/libc/upstream-openbsd/lib/libc/crypt/arc4random.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: arc4random.c,v 1.54 2015/09/13 08:31:47 guenther Exp $	*/
+/*	$OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $	*/
 
 /*
  * Copyright (c) 1996, David Mazieres <dm@uun.org>
@@ -49,6 +49,8 @@
 #define BLOCKSZ	64
 #define RSBUFSZ	(16*BLOCKSZ)
 
+#define REKEY_BASE	(1024*1024) /* NB. should be a power of 2 */
+
 /* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
 static struct _rs {
 	size_t		rs_have;	/* valid bytes at end of rs_buf */
@@ -78,7 +80,7 @@
 			abort();
 	}
 
-	chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0);
+	chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8);
 	chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
 }
 
@@ -86,6 +88,7 @@
 _rs_stir(void)
 {
 	u_char rnd[KEYSZ + IVSZ];
+	uint32_t rekey_fuzz = 0;
 
 	if (getentropy(rnd, sizeof rnd) == -1)
 		_getentropy_fail();
@@ -100,7 +103,10 @@
 	rs->rs_have = 0;
 	memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
 
-	rs->rs_count = 1600000;
+	/* rekey interval should not be predictable */
+	chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,
+	    (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
+	rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
 }
 
 static inline void
diff --git a/libc/upstream-openbsd/lib/libc/crypt/chacha_private.h b/libc/upstream-openbsd/lib/libc/crypt/chacha_private.h
index 7c3680f..b0427b6 100644
--- a/libc/upstream-openbsd/lib/libc/crypt/chacha_private.h
+++ b/libc/upstream-openbsd/lib/libc/crypt/chacha_private.h
@@ -4,7 +4,7 @@
 Public domain.
 */
 
-/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */
+/* $OpenBSD: chacha_private.h,v 1.3 2022/02/28 21:56:29 dtucker Exp $ */
 
 typedef unsigned char u8;
 typedef unsigned int u32;
@@ -52,7 +52,7 @@
 static const char tau[16] = "expand 16-byte k";
 
 static void
-chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits,u32 ivbits)
+chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits)
 {
   const char *constants;
 
diff --git a/libc/upstream-openbsd/lib/libc/gen/ctype_.c b/libc/upstream-openbsd/lib/libc/gen/ctype_.c
index 8972244..9742c9f 100644
--- a/libc/upstream-openbsd/lib/libc/gen/ctype_.c
+++ b/libc/upstream-openbsd/lib/libc/gen/ctype_.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: ctype_.c,v 1.12 2015/09/19 04:02:21 guenther Exp $ */
+/*	$OpenBSD: ctype_.c,v 1.13 2024/02/04 13:03:18 jca Exp $ */
 /*
  * Copyright (c) 1989 The Regents of the University of California.
  * All rights reserved.
@@ -36,6 +36,16 @@
 #include <ctype.h>
 #include "ctype_private.h"
 
+/* Shorter names for the defines provided by <ctype.h> */
+#define	_U	_CTYPE_U
+#define	_L	_CTYPE_L
+#define	_N	_CTYPE_N
+#define	_S	_CTYPE_S
+#define	_P	_CTYPE_P
+#define	_C	_CTYPE_C
+#define	_X	_CTYPE_X
+#define	_B	_CTYPE_B
+
 const char _C_ctype_[1 + CTYPE_NUM_CHARS] = {
 	0,
 	_C,	_C,	_C,	_C,	_C,	_C,	_C,	_C,
diff --git a/libc/upstream-openbsd/lib/libc/net/htonl.c b/libc/upstream-openbsd/lib/libc/net/htonl.c
index 6ee6e7e..58bfb46 100644
--- a/libc/upstream-openbsd/lib/libc/net/htonl.c
+++ b/libc/upstream-openbsd/lib/libc/net/htonl.c
@@ -1,6 +1,5 @@
-/*	$OpenBSD: htonl.c,v 1.7 2014/07/21 01:51:10 guenther Exp $ */
+/*	$OpenBSD: htonl.c,v 1.8 2024/04/15 14:30:48 naddy Exp $ */
 /*
- * Written by J.T. Conklin <jtc@netbsd.org>.
  * Public domain.
  */
 
@@ -9,13 +8,8 @@
 
 #undef htonl
 
-u_int32_t
-htonl(u_int32_t x)
+uint32_t
+htonl(uint32_t x)
 {
-#if BYTE_ORDER == LITTLE_ENDIAN
-	u_char *s = (u_char *)&x;
-	return (u_int32_t)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]);
-#else
-	return x;
-#endif
+	return htobe32(x);
 }
diff --git a/libc/upstream-openbsd/lib/libc/net/htons.c b/libc/upstream-openbsd/lib/libc/net/htons.c
index f48d91e..28b13ce 100644
--- a/libc/upstream-openbsd/lib/libc/net/htons.c
+++ b/libc/upstream-openbsd/lib/libc/net/htons.c
@@ -1,6 +1,5 @@
-/*	$OpenBSD: htons.c,v 1.9 2014/07/21 01:51:10 guenther Exp $ */
+/*	$OpenBSD: htons.c,v 1.10 2024/04/15 14:30:48 naddy Exp $ */
 /*
- * Written by J.T. Conklin <jtc@netbsd.org>.
  * Public domain.
  */
 
@@ -9,13 +8,8 @@
 
 #undef htons
 
-u_int16_t
-htons(u_int16_t x)
+uint16_t
+htons(uint16_t x)
 {
-#if BYTE_ORDER == LITTLE_ENDIAN
-	u_char *s = (u_char *) &x;
-	return (u_int16_t)(s[0] << 8 | s[1]);
-#else
-	return x;
-#endif
+	return htobe16(x);
 }
diff --git a/libc/upstream-openbsd/lib/libc/net/ntohl.c b/libc/upstream-openbsd/lib/libc/net/ntohl.c
index 0d05bac..7592398 100644
--- a/libc/upstream-openbsd/lib/libc/net/ntohl.c
+++ b/libc/upstream-openbsd/lib/libc/net/ntohl.c
@@ -1,6 +1,5 @@
-/*	$OpenBSD: ntohl.c,v 1.7 2014/07/21 01:51:10 guenther Exp $ */
+/*	$OpenBSD: ntohl.c,v 1.8 2024/04/15 14:30:48 naddy Exp $ */
 /*
- * Written by J.T. Conklin <jtc@netbsd.org>.
  * Public domain.
  */
 
@@ -9,13 +8,8 @@
 
 #undef ntohl
 
-u_int32_t
-ntohl(u_int32_t x)
+uint32_t
+ntohl(uint32_t x)
 {
-#if BYTE_ORDER == LITTLE_ENDIAN
-	u_char *s = (u_char *)&x;
-	return (u_int32_t)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]);
-#else
-	return x;
-#endif
+	return be32toh(x);
 }
diff --git a/libc/upstream-openbsd/lib/libc/net/ntohs.c b/libc/upstream-openbsd/lib/libc/net/ntohs.c
index b5ea361..ef22ea3 100644
--- a/libc/upstream-openbsd/lib/libc/net/ntohs.c
+++ b/libc/upstream-openbsd/lib/libc/net/ntohs.c
@@ -1,6 +1,5 @@
-/*	$OpenBSD: ntohs.c,v 1.9 2014/07/21 01:51:10 guenther Exp $ */
+/*	$OpenBSD: ntohs.c,v 1.10 2024/04/15 14:30:48 naddy Exp $ */
 /*
- * Written by J.T. Conklin <jtc@netbsd.org>.
  * Public domain.
  */
 
@@ -9,13 +8,8 @@
 
 #undef ntohs
 
-u_int16_t
-ntohs(u_int16_t x)
+uint16_t
+ntohs(uint16_t x)
 {
-#if BYTE_ORDER == LITTLE_ENDIAN
-	u_char *s = (u_char *) &x;
-	return (u_int16_t)(s[0] << 8 | s[1]);
-#else
-	return x;
-#endif
+	return be16toh(x);
 }
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c b/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c
index d83de88..d615245 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: fvwrite.c,v 1.21 2023/10/06 16:41:02 millert Exp $ */
+/*	$OpenBSD: fvwrite.c,v 1.22 2024/04/28 14:28:02 millert Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -31,6 +31,7 @@
  * SUCH DAMAGE.
  */
 
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -76,11 +77,12 @@
 	}
 	if (fp->_flags & __SNBF) {
 		/*
-		 * Unbuffered: write up to BUFSIZ bytes at a time.
+		 * Unbuffered: write up to INT_MAX bytes at a time, to not
+		 * truncate the value of len if it is greater than 2^31 bytes.
 		 */
 		do {
 			GETIOV(;);
-			w = (*fp->_write)(fp->_cookie, p, MIN(len, BUFSIZ));
+			w = (*fp->_write)(fp->_cookie, p, MIN(len, INT_MAX));
 			if (w <= 0)
 				goto err;
 			p += w;
@@ -90,7 +92,8 @@
 		/*
 		 * Fully buffered: fill partially full buffer, if any,
 		 * and then flush.  If there is no partial buffer, write
-		 * one _bf._size byte chunk directly (without copying).
+		 * entire payload directly (without copying) up to a
+		 * multiple of the buffer size.
 		 *
 		 * String output is a special case: write as many bytes
 		 * as fit, but pretend we wrote everything.  This makes
@@ -134,7 +137,15 @@
 				if (__sflush(fp))
 					goto err;
 			} else if (len >= (w = fp->_bf._size)) {
-				/* write directly */
+				/*
+				 * Write directly up to INT_MAX or greatest
+				 * multiple of buffer size (whichever is
+				 * smaller), keeping in the memory buffer the
+				 * remaining part of payload that is smaller
+				 * than buffer size.
+				 */
+				if (w != 0)
+					w = MIN(w * (len / w), INT_MAX);
 				w = (*fp->_write)(fp->_cookie, p, w);
 				if (w <= 0)
 					goto err;
diff --git a/linker/Android.bp b/linker/Android.bp
index 1ede380..e1a5a91 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -367,9 +367,7 @@
         "liblinker_main",
         "liblinker_malloc",
 
-        // Use a version of libc++ built without exceptions, because accessing EH globals uses
-        // ELF TLS, which is not supported in the loader.
-        "libc++_static_noexcept",
+        "libc++_static",
         "libc_nomalloc",
         "libc_dynamic_dispatch",
         "libm",
@@ -436,7 +434,7 @@
                 "linker_debuggerd_android.cpp",
             ],
             static_libs: [
-                "libc++demangle_noexcept",
+                "libc++demangle",
                 "libdebuggerd_handler_fallback",
             ],
         },
diff --git a/linker/linker_crt_pad_segment_test.cpp b/linker/linker_crt_pad_segment_test.cpp
index 5a219f8..c11df50 100644
--- a/linker/linker_crt_pad_segment_test.cpp
+++ b/linker/linker_crt_pad_segment_test.cpp
@@ -72,13 +72,22 @@
 };  // anonymous namespace
 
 TEST(crt_pad_segment, note_absent) {
+  if (!page_size_migration_supported()) {
+    GTEST_SKIP() << "Kernel does not support page size migration";
+  }
   ASSERT_FALSE(GetPadSegment("no_crt_pad_segment.so"));
 }
 
 TEST(crt_pad_segment, note_present_and_enabled) {
+  if (!page_size_migration_supported()) {
+    GTEST_SKIP() << "Kernel does not support page size migration";
+  }
   ASSERT_TRUE(GetPadSegment("crt_pad_segment_enabled.so"));
 }
 
 TEST(crt_pad_segment, note_present_and_disabled) {
+  if (!page_size_migration_supported()) {
+    GTEST_SKIP() << "Kernel does not support page size migration";
+  }
   ASSERT_FALSE(GetPadSegment("crt_pad_segment_disabled.so"));
 }
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index f966e04..089eceb 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -221,14 +221,10 @@
     exe_path = arg_path;
   }
 
-  // Path might be a symlink
+  // Path might be a symlink; we need the target so that we get the right
+  // linker configuration later.
   char sym_path[PATH_MAX];
-  auto ret = realpath(exe_path, sym_path);
-  if (ret != nullptr) {
-    result.path = std::string(sym_path, strlen(sym_path));
-  } else {
-    result.path = std::string(exe_path, strlen(exe_path));
-  }
+  result.path = std::string(realpath(exe_path, sym_path) != nullptr ? sym_path : exe_path);
 
   result.phdr = reinterpret_cast<const ElfW(Phdr)*>(getauxval(AT_PHDR));
   result.phdr_count = getauxval(AT_PHNUM);
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index ef7671c..fa712a1 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -46,6 +46,8 @@
 #include "private/CFIShadow.h" // For kLibraryAlignment
 #include "private/elf_note.h"
 
+#include <android-base/file.h>
+
 static int GetTargetElfMachine() {
 #if defined(__arm__)
   return EM_ARM;
@@ -707,8 +709,28 @@
   return true;
 }
 
+/*
+ * Returns true if the kernel supports page size migration, else false.
+ */
+bool page_size_migration_supported() {
+  static bool pgsize_migration_enabled = []() {
+    std::string enabled;
+    if (!android::base::ReadFileToString("/sys/kernel/mm/pgsize_migration/enabled", &enabled)) {
+      return false;
+    }
+    return enabled.find("1") != std::string::npos;
+  }();
+  return pgsize_migration_enabled;
+}
+
 // Find the ELF note of type NT_ANDROID_TYPE_PAD_SEGMENT and check that the desc value is 1.
 bool ElfReader::ReadPadSegmentNote() {
+  if (!page_size_migration_supported()) {
+    // Don't attempt to read the note, since segment extension isn't
+    // supported; but return true so that loading can continue normally.
+    return true;
+  }
+
   // The ELF can have multiple PT_NOTE's, check them all
   for (size_t i = 0; i < phdr_num_; ++i) {
     const ElfW(Phdr)* phdr = &phdr_table_[i];
@@ -773,7 +795,16 @@
   const ElfW(Phdr)* next = nullptr;
   size_t next_idx = phdr_idx + 1;
 
-  if (phdr->p_align == kPageSize || !should_pad_segments) {
+  // Don't do segment extension for p_align > 64KiB, such ELFs already existed in the
+  // field e.g. 2MiB p_align for THPs and are relatively small in number.
+  //
+  // The kernel can only represent padding for p_align up to 64KiB. This is because
+  // the kernel uses 4 available bits in the vm_area_struct to represent padding
+  // extent; and so cannot enable mitigations to avoid breaking app compatibility for
+  // p_aligns > 64KiB.
+  //
+  // Don't perform segment extension on these to avoid app compatibility issues.
+  if (phdr->p_align <= kPageSize || phdr->p_align > 64*1024 || !should_pad_segments) {
     return;
   }
 
@@ -887,10 +918,28 @@
     //   2) Break the COW backing, faulting in new anon pages for a region
     //      that will not be used.
 
-    // _seg_file_end = unextended seg_file_end
-    uint64_t _seg_file_end = seg_start + phdr->p_filesz;
-    if ((phdr->p_flags & PF_W) != 0 && page_offset(_seg_file_end) > 0) {
-      memset(reinterpret_cast<void*>(_seg_file_end), 0, kPageSize - page_offset(_seg_file_end));
+    uint64_t unextended_seg_file_end = seg_start + phdr->p_filesz;
+    if ((phdr->p_flags & PF_W) != 0 && page_offset(unextended_seg_file_end) > 0) {
+      memset(reinterpret_cast<void*>(unextended_seg_file_end), 0,
+             kPageSize - page_offset(unextended_seg_file_end));
+    }
+
+    // Pages may be brought in due to readahead.
+    // Drop the padding (zero) pages, to avoid reclaim work later.
+    //
+    // NOTE: The madvise() here is special, as it also serves to hint to the
+    // kernel the portion of the LOAD segment that is padding.
+    //
+    // See: [1] https://android-review.googlesource.com/c/kernel/common/+/3032411
+    //      [2] https://android-review.googlesource.com/c/kernel/common/+/3048835
+    uint64_t pad_start = page_end(unextended_seg_file_end);
+    uint64_t pad_end = page_end(seg_file_end);
+    CHECK(pad_start <= pad_end);
+    uint64_t pad_len = pad_end - pad_start;
+    if (page_size_migration_supported() && pad_len > 0 &&
+        madvise(reinterpret_cast<void*>(pad_start), pad_len, MADV_DONTNEED)) {
+      DL_WARN("\"%s\": madvise(0x%" PRIx64 ", 0x%" PRIx64 ", MADV_DONTNEED) failed: %m",
+              name_.c_str(), pad_start, pad_len);
     }
 
     seg_file_end = page_end(seg_file_end);
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 61242eb..aab9018 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -154,3 +154,5 @@
 
 const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
                                             ElfW(Addr) load_bias);
+
+bool page_size_migration_supported();
diff --git a/tests/clang_fortify_tests.cpp b/tests/clang_fortify_tests.cpp
index 544af43..f4ef4ac 100644
--- a/tests/clang_fortify_tests.cpp
+++ b/tests/clang_fortify_tests.cpp
@@ -164,9 +164,7 @@
     const char large_string[] = "Hello!!!";
     static_assert(sizeof(large_string) > sizeof(small_buffer), "");
 
-#if __clang_major__ > 13
-    // expected-error@+3{{will always overflow}}
-#endif
+    // expected-error@+2{{will always overflow}}
     // expected-error@+1{{string bigger than buffer}}
     EXPECT_FORTIFY_DEATH(strcpy(small_buffer, large_string));
     // expected-error@+1{{string bigger than buffer}}
@@ -204,9 +202,7 @@
     static_assert(sizeof(small_string) > sizeof(split.tiny_buffer), "");
 
 #if _FORTIFY_SOURCE > 1
-#if __clang_major__ > 13
-    // expected-error@+4{{will always overflow}}
-#endif
+    // expected-error@+3{{will always overflow}}
     // expected-error@+2{{string bigger than buffer}}
 #endif
     EXPECT_FORTIFY_DEATH_STRUCT(strcpy(split.tiny_buffer, small_string));
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 2b48d85..bd17b82 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -678,10 +678,12 @@
 TEST(malloc, mallopt_decay) {
 #if defined(__BIONIC__)
   SKIP_WITH_HWASAN << "hwasan does not implement mallopt";
+  ASSERT_EQ(1, mallopt(M_DECAY_TIME, -1));
   ASSERT_EQ(1, mallopt(M_DECAY_TIME, 1));
   ASSERT_EQ(1, mallopt(M_DECAY_TIME, 0));
   ASSERT_EQ(1, mallopt(M_DECAY_TIME, 1));
   ASSERT_EQ(1, mallopt(M_DECAY_TIME, 0));
+  ASSERT_EQ(1, mallopt(M_DECAY_TIME, -1));
 #else
   GTEST_SKIP() << "bionic-only test";
 #endif
@@ -1490,7 +1492,7 @@
   // release secondary allocations back to the OS) was modified to 0ms/1ms by
   // mallopt_decay. Ensure that we delay for at least a second before releasing
   // pages to the OS in order to avoid implicit zeroing by the kernel.
-  mallopt(M_DECAY_TIME, 1000);
+  mallopt(M_DECAY_TIME, 1);
   TestHeapZeroing(/* num_iterations */ 32, [](int iteration) -> int {
     return 1 << (19 + iteration % 4);
   });
@@ -1764,6 +1766,10 @@
   EXPECT_EQ(1, mallopt(M_DECAY_TIME, 1));
   EXPECT_TRUE(android_mallopt(M_GET_DECAY_TIME_ENABLED, &value, sizeof(value)));
   EXPECT_TRUE(value);
+
+  EXPECT_EQ(1, mallopt(M_DECAY_TIME, -1));
+  EXPECT_TRUE(android_mallopt(M_GET_DECAY_TIME_ENABLED, &value, sizeof(value)));
+  EXPECT_FALSE(value);
 #else
   GTEST_SKIP() << "bionic-only test";
 #endif
diff --git a/tests/sys_hwprobe_test.cpp b/tests/sys_hwprobe_test.cpp
index 6b74e18..fd59e1d 100644
--- a/tests/sys_hwprobe_test.cpp
+++ b/tests/sys_hwprobe_test.cpp
@@ -33,6 +33,68 @@
 #include <sys/syscall.h>
 #endif
 
+
+#if defined(__riscv)
+#include <riscv_vector.h>
+
+__attribute__((noinline))
+uint64_t scalar_cast(uint8_t const* p) {
+  return *(uint64_t const*)p;
+}
+
+__attribute__((noinline))
+uint64_t scalar_memcpy(uint8_t const* p) {
+  uint64_t r;
+  __builtin_memcpy(&r, p, sizeof(r));
+  return r;
+}
+
+__attribute__((noinline))
+uint64_t vector_memcpy(uint8_t* d, uint8_t const* p) {
+  __builtin_memcpy(d, p, 16);
+  return *(uint64_t const*)d;
+}
+
+__attribute__((noinline))
+uint64_t vector_ldst(uint8_t* d, uint8_t const* p) {
+  __riscv_vse8(d, __riscv_vle8_v_u8m1(p, 16), 16);
+  return *(uint64_t const*)d;
+}
+
+__attribute__((noinline))
+uint64_t vector_ldst64(uint8_t* d, uint8_t const* p) {
+  __riscv_vse64((unsigned long *)d, __riscv_vle64_v_u64m1((const unsigned long *)p, 16), 16);
+  return *(uint64_t const*)d;
+}
+
+// For testing scalar and vector unaligned accesses.
+uint64_t tmp[3] = {1,1,1};
+uint64_t dst[3] = {1,1,1};
+#endif
+
+TEST(sys_hwprobe, __riscv_hwprobe_misaligned_scalar) {
+#if defined(__riscv)
+  uint8_t* p = (uint8_t*)tmp + 1;
+  ASSERT_NE(0U, scalar_cast(p));
+  ASSERT_NE(0U, scalar_memcpy(p));
+#else
+  GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
+#endif
+}
+
+TEST(sys_hwprobe, __riscv_hwprobe_misaligned_vector) {
+#if defined(__riscv)
+  uint8_t* p = (uint8_t*)tmp + 1;
+  uint8_t* d = (uint8_t*)dst + 1;
+
+  ASSERT_NE(0U, vector_ldst(d, p));
+  ASSERT_NE(0U, vector_memcpy(d, p));
+  ASSERT_NE(0U, vector_ldst64(d, p));
+#else
+  GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
+#endif
+}
+
 TEST(sys_hwprobe, __riscv_hwprobe) {
 #if defined(__riscv) && __has_include(<sys/hwprobe.h>)
   riscv_hwprobe probes[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0},
@@ -82,4 +144,4 @@
 #else
   GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
 #endif
-}
+}
\ No newline at end of file
diff --git a/tests/sys_statvfs_test.cpp b/tests/sys_statvfs_test.cpp
index 73b2a96..5dd7b93 100644
--- a/tests/sys_statvfs_test.cpp
+++ b/tests/sys_statvfs_test.cpp
@@ -25,7 +25,7 @@
 #include <string>
 
 template <typename StatVfsT> void Check(StatVfsT& sb) {
-  EXPECT_EQ(4096U, sb.f_bsize);
+  EXPECT_EQ(getpagesize(), static_cast<int>(sb.f_bsize));
   EXPECT_EQ(0U, sb.f_bfree);
   EXPECT_EQ(0U, sb.f_ffree);
   EXPECT_EQ(255U, sb.f_namemax);
diff --git a/tests/sys_vfs_test.cpp b/tests/sys_vfs_test.cpp
index 96fd61a..e783190 100644
--- a/tests/sys_vfs_test.cpp
+++ b/tests/sys_vfs_test.cpp
@@ -27,7 +27,7 @@
 #include "utils.h"
 
 template <typename StatFsT> void Check(StatFsT& sb) {
-  EXPECT_EQ(4096, static_cast<int>(sb.f_bsize));
+  EXPECT_EQ(getpagesize(), static_cast<int>(sb.f_bsize));
   EXPECT_EQ(0U, sb.f_bfree);
   EXPECT_EQ(0U, sb.f_ffree);
   EXPECT_EQ(255, static_cast<int>(sb.f_namelen));