[scudo] -1 is also valid for M_DECAY_TIME

Test: Run tests
Change-Id: I044ef84bf0ec97c0f8cb8d3a2340b82218d85efc
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/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/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