[scudo] Handle mallinfo2

mallinfo is deprecated by GLIBC

Reviewed By: cryptoad

Differential Revision: https://reviews.llvm.org/D113951

GitOrigin-RevId: 82fc4cc60bec6d0eacad7e8575f404dcdf5144c5
diff --git a/tests/wrappers_c_test.cpp b/tests/wrappers_c_test.cpp
index f607ba7..616cf54 100644
--- a/tests/wrappers_c_test.cpp
+++ b/tests/wrappers_c_test.cpp
@@ -16,6 +16,10 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#ifndef __GLIBC_PREREQ
+#define __GLIBC_PREREQ(x, y) 0
+#endif
+
 extern "C" {
 void malloc_enable(void);
 void malloc_disable(void);
@@ -258,8 +262,10 @@
 
 #if !SCUDO_FUCHSIA
 TEST(ScudoWrappersCTest, MallInfo) {
+  // mallinfo is deprecated.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
   const size_t BypassQuarantineSize = 1024U;
-
   struct mallinfo MI = mallinfo();
   size_t Allocated = MI.uordblks;
   void *P = malloc(BypassQuarantineSize);
@@ -271,6 +277,24 @@
   free(P);
   MI = mallinfo();
   EXPECT_GE(static_cast<size_t>(MI.fordblks), Free + BypassQuarantineSize);
+#pragma clang diagnostic pop
+}
+#endif
+
+#if __GLIBC_PREREQ(2, 33)
+TEST(ScudoWrappersCTest, MallInfo2) {
+  const size_t BypassQuarantineSize = 1024U;
+  struct mallinfo2 MI = mallinfo2();
+  size_t Allocated = MI.uordblks;
+  void *P = malloc(BypassQuarantineSize);
+  EXPECT_NE(P, nullptr);
+  MI = mallinfo2();
+  EXPECT_GE(MI.uordblks, Allocated + BypassQuarantineSize);
+  EXPECT_GT(MI.hblkhd, 0U);
+  size_t Free = MI.fordblks;
+  free(P);
+  MI = mallinfo2();
+  EXPECT_GE(MI.fordblks, Free + BypassQuarantineSize);
 }
 #endif
 
diff --git a/wrappers_c.h b/wrappers_c.h
index 6d0cecd..5f7f51f 100644
--- a/wrappers_c.h
+++ b/wrappers_c.h
@@ -32,6 +32,19 @@
   __scudo_mallinfo_data_t keepcost;
 };
 
+struct __scudo_mallinfo2 {
+  size_t arena;
+  size_t ordblks;
+  size_t smblks;
+  size_t hblks;
+  size_t hblkhd;
+  size_t usmblks;
+  size_t fsmblks;
+  size_t uordblks;
+  size_t fordblks;
+  size_t keepcost;
+};
+
 // Android sometimes includes malloc.h no matter what, which yields to
 // conflicting return types for mallinfo() if we use our own structure. So if
 // struct mallinfo is declared (#define courtesy of malloc.h), use it directly.
diff --git a/wrappers_c.inc b/wrappers_c.inc
index 6c6bcb6..bbe3617 100644
--- a/wrappers_c.inc
+++ b/wrappers_c.inc
@@ -54,6 +54,23 @@
   return Info;
 }
 
+INTERFACE WEAK struct __scudo_mallinfo2 SCUDO_PREFIX(mallinfo2)(void) {
+  struct __scudo_mallinfo2 Info = {};
+  scudo::StatCounters Stats;
+  SCUDO_ALLOCATOR.getStats(Stats);
+  // Space allocated in mmapped regions (bytes)
+  Info.hblkhd = Stats[scudo::StatMapped];
+  // Maximum total allocated space (bytes)
+  Info.usmblks = Info.hblkhd;
+  // Space in freed fastbin blocks (bytes)
+  Info.fsmblks = Stats[scudo::StatFree];
+  // Total allocated space (bytes)
+  Info.uordblks = Stats[scudo::StatAllocated];
+  // Total free space (bytes)
+  Info.fordblks = Info.fsmblks;
+  return Info;
+}
+
 INTERFACE WEAK void *SCUDO_PREFIX(malloc)(size_t size) {
   return scudo::setErrnoOnNull(SCUDO_ALLOCATOR.allocate(
       size, scudo::Chunk::Origin::Malloc, SCUDO_MALLOC_ALIGNMENT));