xref: /llvm-project/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp (revision 58c2a4e806b2882c0622cbded923b32f94c5b47b)
13fa38318SNico Weber //===-- wrappers_c_test.cpp -------------------------------------*- C++ -*-===//
23fa38318SNico Weber //
33fa38318SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43fa38318SNico Weber // See https://llvm.org/LICENSE.txt for license information.
53fa38318SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63fa38318SNico Weber //
73fa38318SNico Weber //===----------------------------------------------------------------------===//
83fa38318SNico Weber 
901c02abcSRiley #include "common.h"
101e6d1353SVitaly Buka #include "memtag.h"
11b83417aaSPeter Collingbourne #include "scudo/interface.h"
120d3d4d3bSKostya Kortchinsky #include "tests/scudo_unit_test.h"
133fa38318SNico Weber 
140d3d4d3bSKostya Kortchinsky #include <errno.h>
153fa38318SNico Weber #include <limits.h>
163fa38318SNico Weber #include <malloc.h>
17161cca26SKostya Kortchinsky #include <stdlib.h>
183fa38318SNico Weber #include <unistd.h>
1901c02abcSRiley #include <vector>
203fa38318SNico Weber 
2182fc4cc6SVitaly Buka #ifndef __GLIBC_PREREQ
2282fc4cc6SVitaly Buka #define __GLIBC_PREREQ(x, y) 0
2382fc4cc6SVitaly Buka #endif
2482fc4cc6SVitaly Buka 
25c981f0b4SChristopher Ferris #if SCUDO_FUCHSIA
26c981f0b4SChristopher Ferris // Fuchsia only has valloc
27c981f0b4SChristopher Ferris #define HAVE_VALLOC 1
28c981f0b4SChristopher Ferris #elif SCUDO_ANDROID
29c981f0b4SChristopher Ferris // Android only has pvalloc/valloc on 32 bit
30c981f0b4SChristopher Ferris #if !defined(__LP64__)
31c981f0b4SChristopher Ferris #define HAVE_PVALLOC 1
32c981f0b4SChristopher Ferris #define HAVE_VALLOC 1
33c981f0b4SChristopher Ferris #endif // !defined(__LP64__)
34c981f0b4SChristopher Ferris #else
35c981f0b4SChristopher Ferris // All others assumed to support both functions.
36c981f0b4SChristopher Ferris #define HAVE_PVALLOC 1
37c981f0b4SChristopher Ferris #define HAVE_VALLOC 1
38c981f0b4SChristopher Ferris #endif
39c981f0b4SChristopher Ferris 
4088852964SChia-hung Duan extern "C" {
4188852964SChia-hung Duan void malloc_enable(void);
4288852964SChia-hung Duan void malloc_disable(void);
4388852964SChia-hung Duan int malloc_iterate(uintptr_t base, size_t size,
4488852964SChia-hung Duan                    void (*callback)(uintptr_t base, size_t size, void *arg),
4588852964SChia-hung Duan                    void *arg);
4688852964SChia-hung Duan void *valloc(size_t size);
4788852964SChia-hung Duan void *pvalloc(size_t size);
4888852964SChia-hung Duan 
4988852964SChia-hung Duan #ifndef SCUDO_ENABLE_HOOKS_TESTS
5088852964SChia-hung Duan #define SCUDO_ENABLE_HOOKS_TESTS 0
5188852964SChia-hung Duan #endif
5288852964SChia-hung Duan 
5388852964SChia-hung Duan #if (SCUDO_ENABLE_HOOKS_TESTS == 1) && (SCUDO_ENABLE_HOOKS == 0)
5488852964SChia-hung Duan #error "Hooks tests should have hooks enabled as well!"
5588852964SChia-hung Duan #endif
5688852964SChia-hung Duan 
574f76810dSChia-hung Duan struct AllocContext {
584f76810dSChia-hung Duan   void *Ptr;
594f76810dSChia-hung Duan   size_t Size;
604f76810dSChia-hung Duan };
614f76810dSChia-hung Duan struct DeallocContext {
624f76810dSChia-hung Duan   void *Ptr;
634f76810dSChia-hung Duan };
64*58c2a4e8SChiaHungDuan struct ReallocContext {
65*58c2a4e8SChiaHungDuan   void *AllocPtr;
66*58c2a4e8SChiaHungDuan   void *DeallocPtr;
67*58c2a4e8SChiaHungDuan   size_t Size;
68*58c2a4e8SChiaHungDuan };
694f76810dSChia-hung Duan static AllocContext AC;
704f76810dSChia-hung Duan static DeallocContext DC;
71*58c2a4e8SChiaHungDuan static ReallocContext RC;
724f76810dSChia-hung Duan 
7388852964SChia-hung Duan #if (SCUDO_ENABLE_HOOKS_TESTS == 1)
__scudo_allocate_hook(void * Ptr,size_t Size)744f76810dSChia-hung Duan __attribute__((visibility("default"))) void __scudo_allocate_hook(void *Ptr,
754f76810dSChia-hung Duan                                                                   size_t Size) {
764f76810dSChia-hung Duan   AC.Ptr = Ptr;
774f76810dSChia-hung Duan   AC.Size = Size;
784f76810dSChia-hung Duan }
__scudo_deallocate_hook(void * Ptr)794f76810dSChia-hung Duan __attribute__((visibility("default"))) void __scudo_deallocate_hook(void *Ptr) {
804f76810dSChia-hung Duan   DC.Ptr = Ptr;
814f76810dSChia-hung Duan }
82*58c2a4e8SChiaHungDuan __attribute__((visibility("default"))) void
__scudo_realloc_allocate_hook(void * OldPtr,void * NewPtr,size_t Size)83*58c2a4e8SChiaHungDuan __scudo_realloc_allocate_hook(void *OldPtr, void *NewPtr, size_t Size) {
84*58c2a4e8SChiaHungDuan   // Verify that __scudo_realloc_deallocate_hook is called first and set the
85*58c2a4e8SChiaHungDuan   // right pointer.
86*58c2a4e8SChiaHungDuan   EXPECT_EQ(OldPtr, RC.DeallocPtr);
87*58c2a4e8SChiaHungDuan   RC.AllocPtr = NewPtr;
88*58c2a4e8SChiaHungDuan   RC.Size = Size;
89*58c2a4e8SChiaHungDuan 
90*58c2a4e8SChiaHungDuan   // Note that this is only used for testing. In general, only one pair of hooks
91*58c2a4e8SChiaHungDuan   // will be invoked in `realloc`. if __scudo_realloc_*_hook are not defined,
92*58c2a4e8SChiaHungDuan   // it'll call the general hooks only. To make the test easier, we call the
93*58c2a4e8SChiaHungDuan   // general one here so that either case (whether __scudo_realloc_*_hook are
94*58c2a4e8SChiaHungDuan   // defined) will be verified without separating them into different tests.
95*58c2a4e8SChiaHungDuan   __scudo_allocate_hook(NewPtr, Size);
96*58c2a4e8SChiaHungDuan }
97*58c2a4e8SChiaHungDuan __attribute__((visibility("default"))) void
__scudo_realloc_deallocate_hook(void * Ptr)98*58c2a4e8SChiaHungDuan __scudo_realloc_deallocate_hook(void *Ptr) {
99*58c2a4e8SChiaHungDuan   RC.DeallocPtr = Ptr;
100*58c2a4e8SChiaHungDuan 
101*58c2a4e8SChiaHungDuan   // See the comment in the __scudo_realloc_allocate_hook above.
102*58c2a4e8SChiaHungDuan   __scudo_deallocate_hook(Ptr);
103*58c2a4e8SChiaHungDuan }
10488852964SChia-hung Duan #endif // (SCUDO_ENABLE_HOOKS_TESTS == 1)
1053e5360f1SKostya Kortchinsky }
1063e5360f1SKostya Kortchinsky 
10788852964SChia-hung Duan class ScudoWrappersCTest : public Test {
10888852964SChia-hung Duan protected:
SetUp()10988852964SChia-hung Duan   void SetUp() override {
11088852964SChia-hung Duan     if (SCUDO_ENABLE_HOOKS && !SCUDO_ENABLE_HOOKS_TESTS)
11188852964SChia-hung Duan       printf("Hooks are enabled but hooks tests are disabled.\n");
11288852964SChia-hung Duan   }
11388852964SChia-hung Duan 
invalidateHookPtrs()11475867f8eSChiaHungDuan   void invalidateHookPtrs() {
11575867f8eSChiaHungDuan     if (SCUDO_ENABLE_HOOKS_TESTS) {
11675867f8eSChiaHungDuan       void *InvalidPtr = reinterpret_cast<void *>(0xdeadbeef);
11775867f8eSChiaHungDuan       AC.Ptr = InvalidPtr;
11875867f8eSChiaHungDuan       DC.Ptr = InvalidPtr;
119*58c2a4e8SChiaHungDuan       RC.AllocPtr = RC.DeallocPtr = InvalidPtr;
12075867f8eSChiaHungDuan     }
12188852964SChia-hung Duan   }
verifyAllocHookPtr(UNUSED void * Ptr)12288852964SChia-hung Duan   void verifyAllocHookPtr(UNUSED void *Ptr) {
12388852964SChia-hung Duan     if (SCUDO_ENABLE_HOOKS_TESTS)
12488852964SChia-hung Duan       EXPECT_EQ(Ptr, AC.Ptr);
12588852964SChia-hung Duan   }
verifyAllocHookSize(UNUSED size_t Size)12688852964SChia-hung Duan   void verifyAllocHookSize(UNUSED size_t Size) {
12788852964SChia-hung Duan     if (SCUDO_ENABLE_HOOKS_TESTS)
12888852964SChia-hung Duan       EXPECT_EQ(Size, AC.Size);
12988852964SChia-hung Duan   }
verifyDeallocHookPtr(UNUSED void * Ptr)13088852964SChia-hung Duan   void verifyDeallocHookPtr(UNUSED void *Ptr) {
13188852964SChia-hung Duan     if (SCUDO_ENABLE_HOOKS_TESTS)
13288852964SChia-hung Duan       EXPECT_EQ(Ptr, DC.Ptr);
13388852964SChia-hung Duan   }
verifyReallocHookPtrs(UNUSED void * OldPtr,void * NewPtr,size_t Size)134*58c2a4e8SChiaHungDuan   void verifyReallocHookPtrs(UNUSED void *OldPtr, void *NewPtr, size_t Size) {
135*58c2a4e8SChiaHungDuan     if (SCUDO_ENABLE_HOOKS_TESTS) {
136*58c2a4e8SChiaHungDuan       EXPECT_EQ(OldPtr, RC.DeallocPtr);
137*58c2a4e8SChiaHungDuan       EXPECT_EQ(NewPtr, RC.AllocPtr);
138*58c2a4e8SChiaHungDuan       EXPECT_EQ(Size, RC.Size);
139*58c2a4e8SChiaHungDuan     }
140*58c2a4e8SChiaHungDuan   }
14188852964SChia-hung Duan };
14288852964SChia-hung Duan using ScudoWrappersCDeathTest = ScudoWrappersCTest;
14388852964SChia-hung Duan 
1443fa38318SNico Weber // Note that every C allocation function in the test binary will be fulfilled
1453fa38318SNico Weber // by Scudo (this includes the gtest APIs, etc.), which is a test by itself.
1463fa38318SNico Weber // But this might also lead to unexpected side-effects, since the allocation and
1473fa38318SNico Weber // deallocation operations in the TEST functions will coexist with others (see
1483fa38318SNico Weber // the EXPECT_DEATH comment below).
1493fa38318SNico Weber 
1503fa38318SNico Weber // We have to use a small quarantine to make sure that our double-free tests
1513fa38318SNico Weber // trigger. Otherwise EXPECT_DEATH ends up reallocating the chunk that was just
1523fa38318SNico Weber // freed (this depends on the size obviously) and the following free succeeds.
1533fa38318SNico Weber 
1543fa38318SNico Weber static const size_t Size = 100U;
1553fa38318SNico Weber 
TEST_F(ScudoWrappersCDeathTest,Malloc)15688852964SChia-hung Duan TEST_F(ScudoWrappersCDeathTest, Malloc) {
1573fa38318SNico Weber   void *P = malloc(Size);
1583fa38318SNico Weber   EXPECT_NE(P, nullptr);
1593fa38318SNico Weber   EXPECT_LE(Size, malloc_usable_size(P));
1603fa38318SNico Weber   EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % FIRST_32_SECOND_64(8U, 16U), 0U);
16188852964SChia-hung Duan   verifyAllocHookPtr(P);
16288852964SChia-hung Duan   verifyAllocHookSize(Size);
163bed88824SLeonard Chan 
164bed88824SLeonard Chan   // An update to this warning in Clang now triggers in this line, but it's ok
165bed88824SLeonard Chan   // because the check is expecting a bad pointer and should fail.
166bed88824SLeonard Chan #if defined(__has_warning) && __has_warning("-Wfree-nonheap-object")
167bed88824SLeonard Chan #pragma GCC diagnostic push
168bed88824SLeonard Chan #pragma GCC diagnostic ignored "-Wfree-nonheap-object"
169bed88824SLeonard Chan #endif
1703fa38318SNico Weber   EXPECT_DEATH(
1713fa38318SNico Weber       free(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(P) | 1U)), "");
172bed88824SLeonard Chan #if defined(__has_warning) && __has_warning("-Wfree-nonheap-object")
173bed88824SLeonard Chan #pragma GCC diagnostic pop
174bed88824SLeonard Chan #endif
175bed88824SLeonard Chan 
1763fa38318SNico Weber   free(P);
17788852964SChia-hung Duan   verifyDeallocHookPtr(P);
1783fa38318SNico Weber   EXPECT_DEATH(free(P), "");
1793fa38318SNico Weber 
1803fa38318SNico Weber   P = malloc(0U);
1813fa38318SNico Weber   EXPECT_NE(P, nullptr);
1823fa38318SNico Weber   free(P);
1833fa38318SNico Weber 
1843fa38318SNico Weber   errno = 0;
1853fa38318SNico Weber   EXPECT_EQ(malloc(SIZE_MAX), nullptr);
1863fa38318SNico Weber   EXPECT_EQ(errno, ENOMEM);
1873fa38318SNico Weber }
1883fa38318SNico Weber 
TEST_F(ScudoWrappersCTest,Calloc)18988852964SChia-hung Duan TEST_F(ScudoWrappersCTest, Calloc) {
1903fa38318SNico Weber   void *P = calloc(1U, Size);
1913fa38318SNico Weber   EXPECT_NE(P, nullptr);
1923fa38318SNico Weber   EXPECT_LE(Size, malloc_usable_size(P));
19388852964SChia-hung Duan   verifyAllocHookPtr(P);
19488852964SChia-hung Duan   verifyAllocHookSize(Size);
1953fa38318SNico Weber   for (size_t I = 0; I < Size; I++)
1963fa38318SNico Weber     EXPECT_EQ((reinterpret_cast<uint8_t *>(P))[I], 0U);
1973fa38318SNico Weber   free(P);
19888852964SChia-hung Duan   verifyDeallocHookPtr(P);
1993fa38318SNico Weber 
2003fa38318SNico Weber   P = calloc(1U, 0U);
2013fa38318SNico Weber   EXPECT_NE(P, nullptr);
2023fa38318SNico Weber   free(P);
2033fa38318SNico Weber   P = calloc(0U, 1U);
2043fa38318SNico Weber   EXPECT_NE(P, nullptr);
2053fa38318SNico Weber   free(P);
2063fa38318SNico Weber 
2073fa38318SNico Weber   errno = 0;
2083fa38318SNico Weber   EXPECT_EQ(calloc(SIZE_MAX, 1U), nullptr);
2093fa38318SNico Weber   EXPECT_EQ(errno, ENOMEM);
2103fa38318SNico Weber   errno = 0;
2113fa38318SNico Weber   EXPECT_EQ(calloc(static_cast<size_t>(LONG_MAX) + 1U, 2U), nullptr);
2123fa38318SNico Weber   if (SCUDO_ANDROID)
2133fa38318SNico Weber     EXPECT_EQ(errno, ENOMEM);
2143fa38318SNico Weber   errno = 0;
2153fa38318SNico Weber   EXPECT_EQ(calloc(SIZE_MAX, SIZE_MAX), nullptr);
2163fa38318SNico Weber   EXPECT_EQ(errno, ENOMEM);
2173fa38318SNico Weber }
2183fa38318SNico Weber 
TEST_F(ScudoWrappersCTest,SmallAlign)21988852964SChia-hung Duan TEST_F(ScudoWrappersCTest, SmallAlign) {
22001c02abcSRiley   // Allocating pointers by the powers of 2 from 1 to 0x10000
22101c02abcSRiley   // Using powers of 2 due to memalign using powers of 2 and test more sizes
22201c02abcSRiley   constexpr size_t MaxSize = 0x10000;
22301c02abcSRiley   std::vector<void *> ptrs;
22401c02abcSRiley   // Reserving space to prevent further allocation during the test
22501c02abcSRiley   ptrs.reserve((scudo::getLeastSignificantSetBitIndex(MaxSize) + 1) *
22601c02abcSRiley                (scudo::getLeastSignificantSetBitIndex(MaxSize) + 1) * 3);
22701c02abcSRiley   for (size_t Size = 1; Size <= MaxSize; Size <<= 1) {
22801c02abcSRiley     for (size_t Align = 1; Align <= MaxSize; Align <<= 1) {
229e78b64dfSMitch Phillips       for (size_t Count = 0; Count < 3; ++Count) {
23001c02abcSRiley         void *P = memalign(Align, Size);
231e78b64dfSMitch Phillips         EXPECT_TRUE(reinterpret_cast<uintptr_t>(P) % Align == 0);
23201c02abcSRiley         ptrs.push_back(P);
233e78b64dfSMitch Phillips       }
234e78b64dfSMitch Phillips     }
235e78b64dfSMitch Phillips   }
23601c02abcSRiley   for (void *ptr : ptrs)
23701c02abcSRiley     free(ptr);
238e78b64dfSMitch Phillips }
239e78b64dfSMitch Phillips 
TEST_F(ScudoWrappersCTest,Memalign)24088852964SChia-hung Duan TEST_F(ScudoWrappersCTest, Memalign) {
2413fa38318SNico Weber   void *P;
2423fa38318SNico Weber   for (size_t I = FIRST_32_SECOND_64(2U, 3U); I <= 18U; I++) {
2433fa38318SNico Weber     const size_t Alignment = 1U << I;
2443fa38318SNico Weber 
2453fa38318SNico Weber     P = memalign(Alignment, Size);
2463fa38318SNico Weber     EXPECT_NE(P, nullptr);
2473fa38318SNico Weber     EXPECT_LE(Size, malloc_usable_size(P));
2483fa38318SNico Weber     EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
24988852964SChia-hung Duan     verifyAllocHookPtr(P);
25088852964SChia-hung Duan     verifyAllocHookSize(Size);
2513fa38318SNico Weber     free(P);
25288852964SChia-hung Duan     verifyDeallocHookPtr(P);
2533fa38318SNico Weber 
2543fa38318SNico Weber     P = nullptr;
2553fa38318SNico Weber     EXPECT_EQ(posix_memalign(&P, Alignment, Size), 0);
2563fa38318SNico Weber     EXPECT_NE(P, nullptr);
2573fa38318SNico Weber     EXPECT_LE(Size, malloc_usable_size(P));
2583fa38318SNico Weber     EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
25988852964SChia-hung Duan     verifyAllocHookPtr(P);
26088852964SChia-hung Duan     verifyAllocHookSize(Size);
2613fa38318SNico Weber     free(P);
26288852964SChia-hung Duan     verifyDeallocHookPtr(P);
2633fa38318SNico Weber   }
2643fa38318SNico Weber 
2653fa38318SNico Weber   EXPECT_EQ(memalign(4096U, SIZE_MAX), nullptr);
2663fa38318SNico Weber   EXPECT_EQ(posix_memalign(&P, 15U, Size), EINVAL);
2673fa38318SNico Weber   EXPECT_EQ(posix_memalign(&P, 4096U, SIZE_MAX), ENOMEM);
2683fa38318SNico Weber 
2693fa38318SNico Weber   // Android's memalign accepts non power-of-2 alignments, and 0.
2703fa38318SNico Weber   if (SCUDO_ANDROID) {
2713fa38318SNico Weber     for (size_t Alignment = 0U; Alignment <= 128U; Alignment++) {
2723fa38318SNico Weber       P = memalign(Alignment, 1024U);
2733fa38318SNico Weber       EXPECT_NE(P, nullptr);
27488852964SChia-hung Duan       verifyAllocHookPtr(P);
27588852964SChia-hung Duan       verifyAllocHookSize(Size);
2763fa38318SNico Weber       free(P);
27788852964SChia-hung Duan       verifyDeallocHookPtr(P);
2783fa38318SNico Weber     }
2793fa38318SNico Weber   }
2803fa38318SNico Weber }
2813fa38318SNico Weber 
TEST_F(ScudoWrappersCTest,AlignedAlloc)28288852964SChia-hung Duan TEST_F(ScudoWrappersCTest, AlignedAlloc) {
2833fa38318SNico Weber   const size_t Alignment = 4096U;
2843fa38318SNico Weber   void *P = aligned_alloc(Alignment, Alignment * 4U);
2853fa38318SNico Weber   EXPECT_NE(P, nullptr);
2863fa38318SNico Weber   EXPECT_LE(Alignment * 4U, malloc_usable_size(P));
2873fa38318SNico Weber   EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
28888852964SChia-hung Duan   verifyAllocHookPtr(P);
28988852964SChia-hung Duan   verifyAllocHookSize(Alignment * 4U);
2903fa38318SNico Weber   free(P);
29188852964SChia-hung Duan   verifyDeallocHookPtr(P);
2923fa38318SNico Weber 
2933fa38318SNico Weber   errno = 0;
2943fa38318SNico Weber   P = aligned_alloc(Alignment, Size);
2953fa38318SNico Weber   EXPECT_EQ(P, nullptr);
2963fa38318SNico Weber   EXPECT_EQ(errno, EINVAL);
2973fa38318SNico Weber }
2983fa38318SNico Weber 
TEST_F(ScudoWrappersCDeathTest,Realloc)29988852964SChia-hung Duan TEST_F(ScudoWrappersCDeathTest, Realloc) {
30075867f8eSChiaHungDuan   invalidateHookPtrs();
3013fa38318SNico Weber   // realloc(nullptr, N) is malloc(N)
3024f76810dSChia-hung Duan   void *P = realloc(nullptr, Size);
3033fa38318SNico Weber   EXPECT_NE(P, nullptr);
30488852964SChia-hung Duan   verifyAllocHookPtr(P);
30588852964SChia-hung Duan   verifyAllocHookSize(Size);
3063fa38318SNico Weber   free(P);
30788852964SChia-hung Duan   verifyDeallocHookPtr(P);
3083fa38318SNico Weber 
30975867f8eSChiaHungDuan   invalidateHookPtrs();
3103fa38318SNico Weber   P = malloc(Size);
3113fa38318SNico Weber   EXPECT_NE(P, nullptr);
3123fa38318SNico Weber   // realloc(P, 0U) is free(P) and returns nullptr
3133fa38318SNico Weber   EXPECT_EQ(realloc(P, 0U), nullptr);
31488852964SChia-hung Duan   verifyDeallocHookPtr(P);
3153fa38318SNico Weber 
3163fa38318SNico Weber   P = malloc(Size);
3173fa38318SNico Weber   EXPECT_NE(P, nullptr);
3183fa38318SNico Weber   EXPECT_LE(Size, malloc_usable_size(P));
3193fa38318SNico Weber   memset(P, 0x42, Size);
3203fa38318SNico Weber 
32175867f8eSChiaHungDuan   invalidateHookPtrs();
3224f76810dSChia-hung Duan   void *OldP = P;
3233fa38318SNico Weber   P = realloc(P, Size * 2U);
3243fa38318SNico Weber   EXPECT_NE(P, nullptr);
3253fa38318SNico Weber   EXPECT_LE(Size * 2U, malloc_usable_size(P));
3263fa38318SNico Weber   for (size_t I = 0; I < Size; I++)
3273fa38318SNico Weber     EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
3284f76810dSChia-hung Duan   if (OldP == P) {
32975867f8eSChiaHungDuan     verifyDeallocHookPtr(OldP);
33075867f8eSChiaHungDuan     verifyAllocHookPtr(OldP);
3314f76810dSChia-hung Duan   } else {
33288852964SChia-hung Duan     verifyAllocHookPtr(P);
33388852964SChia-hung Duan     verifyAllocHookSize(Size * 2U);
33488852964SChia-hung Duan     verifyDeallocHookPtr(OldP);
3354f76810dSChia-hung Duan   }
336*58c2a4e8SChiaHungDuan   verifyReallocHookPtrs(OldP, P, Size * 2U);
3373fa38318SNico Weber 
33875867f8eSChiaHungDuan   invalidateHookPtrs();
3394f76810dSChia-hung Duan   OldP = P;
3403fa38318SNico Weber   P = realloc(P, Size / 2U);
3413fa38318SNico Weber   EXPECT_NE(P, nullptr);
3423fa38318SNico Weber   EXPECT_LE(Size / 2U, malloc_usable_size(P));
3433fa38318SNico Weber   for (size_t I = 0; I < Size / 2U; I++)
3443fa38318SNico Weber     EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
3454f76810dSChia-hung Duan   if (OldP == P) {
34675867f8eSChiaHungDuan     verifyDeallocHookPtr(OldP);
34775867f8eSChiaHungDuan     verifyAllocHookPtr(OldP);
3484f76810dSChia-hung Duan   } else {
34988852964SChia-hung Duan     verifyAllocHookPtr(P);
35088852964SChia-hung Duan     verifyAllocHookSize(Size / 2U);
3514f76810dSChia-hung Duan   }
352*58c2a4e8SChiaHungDuan   verifyReallocHookPtrs(OldP, P, Size / 2U);
3533fa38318SNico Weber   free(P);
3543fa38318SNico Weber 
3553fa38318SNico Weber   EXPECT_DEATH(P = realloc(P, Size), "");
3563fa38318SNico Weber 
3573fa38318SNico Weber   errno = 0;
3583fa38318SNico Weber   EXPECT_EQ(realloc(nullptr, SIZE_MAX), nullptr);
3593fa38318SNico Weber   EXPECT_EQ(errno, ENOMEM);
3603fa38318SNico Weber   P = malloc(Size);
3613fa38318SNico Weber   EXPECT_NE(P, nullptr);
3623fa38318SNico Weber   errno = 0;
3633fa38318SNico Weber   EXPECT_EQ(realloc(P, SIZE_MAX), nullptr);
3643fa38318SNico Weber   EXPECT_EQ(errno, ENOMEM);
3653fa38318SNico Weber   free(P);
3663fa38318SNico Weber 
3673fa38318SNico Weber   // Android allows realloc of memalign pointers.
3683fa38318SNico Weber   if (SCUDO_ANDROID) {
3693fa38318SNico Weber     const size_t Alignment = 1024U;
3703fa38318SNico Weber     P = memalign(Alignment, Size);
3713fa38318SNico Weber     EXPECT_NE(P, nullptr);
3723fa38318SNico Weber     EXPECT_LE(Size, malloc_usable_size(P));
3733fa38318SNico Weber     EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
3743fa38318SNico Weber     memset(P, 0x42, Size);
3753fa38318SNico Weber 
3763fa38318SNico Weber     P = realloc(P, Size * 2U);
3773fa38318SNico Weber     EXPECT_NE(P, nullptr);
3783fa38318SNico Weber     EXPECT_LE(Size * 2U, malloc_usable_size(P));
3793fa38318SNico Weber     for (size_t I = 0; I < Size; I++)
3803fa38318SNico Weber       EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
3813fa38318SNico Weber     free(P);
3823fa38318SNico Weber   }
3833fa38318SNico Weber }
3843fa38318SNico Weber 
3850d3d4d3bSKostya Kortchinsky #if !SCUDO_FUCHSIA
TEST_F(ScudoWrappersCTest,MallOpt)38688852964SChia-hung Duan TEST_F(ScudoWrappersCTest, MallOpt) {
3873fa38318SNico Weber   errno = 0;
3883fa38318SNico Weber   EXPECT_EQ(mallopt(-1000, 1), 0);
3893fa38318SNico Weber   // mallopt doesn't set errno.
3903fa38318SNico Weber   EXPECT_EQ(errno, 0);
3913fa38318SNico Weber 
3923fa38318SNico Weber   EXPECT_EQ(mallopt(M_PURGE, 0), 1);
3933fa38318SNico Weber 
3943fa38318SNico Weber   EXPECT_EQ(mallopt(M_DECAY_TIME, 1), 1);
3953fa38318SNico Weber   EXPECT_EQ(mallopt(M_DECAY_TIME, 0), 1);
3963fa38318SNico Weber   EXPECT_EQ(mallopt(M_DECAY_TIME, 1), 1);
3973fa38318SNico Weber   EXPECT_EQ(mallopt(M_DECAY_TIME, 0), 1);
398f14472a2SChristopher Ferris 
399f14472a2SChristopher Ferris   if (SCUDO_ANDROID) {
400f14472a2SChristopher Ferris     EXPECT_EQ(mallopt(M_CACHE_COUNT_MAX, 100), 1);
401f14472a2SChristopher Ferris     EXPECT_EQ(mallopt(M_CACHE_SIZE_MAX, 1024 * 1024 * 2), 1);
402f14472a2SChristopher Ferris     EXPECT_EQ(mallopt(M_TSDS_COUNT_MAX, 10), 1);
403f14472a2SChristopher Ferris   }
4043fa38318SNico Weber }
4050d3d4d3bSKostya Kortchinsky #endif
4063fa38318SNico Weber 
TEST_F(ScudoWrappersCTest,OtherAlloc)40788852964SChia-hung Duan TEST_F(ScudoWrappersCTest, OtherAlloc) {
408c981f0b4SChristopher Ferris #if HAVE_PVALLOC
409af41f79fSChristopher Ferris   const size_t PageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
4103fa38318SNico Weber 
4113fa38318SNico Weber   void *P = pvalloc(Size);
4123fa38318SNico Weber   EXPECT_NE(P, nullptr);
4133fa38318SNico Weber   EXPECT_EQ(reinterpret_cast<uintptr_t>(P) & (PageSize - 1), 0U);
4143fa38318SNico Weber   EXPECT_LE(PageSize, malloc_usable_size(P));
41588852964SChia-hung Duan   verifyAllocHookPtr(P);
4164f76810dSChia-hung Duan   // Size will be rounded up to PageSize.
41788852964SChia-hung Duan   verifyAllocHookSize(PageSize);
4183fa38318SNico Weber   free(P);
41988852964SChia-hung Duan   verifyDeallocHookPtr(P);
4203fa38318SNico Weber 
4213fa38318SNico Weber   EXPECT_EQ(pvalloc(SIZE_MAX), nullptr);
4223fa38318SNico Weber 
4233fa38318SNico Weber   P = pvalloc(Size);
4243fa38318SNico Weber   EXPECT_NE(P, nullptr);
4253fa38318SNico Weber   EXPECT_EQ(reinterpret_cast<uintptr_t>(P) & (PageSize - 1), 0U);
4263fa38318SNico Weber   free(P);
4270d3d4d3bSKostya Kortchinsky #endif
4283fa38318SNico Weber 
429c981f0b4SChristopher Ferris #if HAVE_VALLOC
4303fa38318SNico Weber   EXPECT_EQ(valloc(SIZE_MAX), nullptr);
431c981f0b4SChristopher Ferris #endif
4323fa38318SNico Weber }
4332be59170SKostya Kortchinsky 
434fd1721d8SChristopher Ferris template<typename FieldType>
MallInfoTest()435fd1721d8SChristopher Ferris void MallInfoTest() {
43682fc4cc6SVitaly Buka   // mallinfo is deprecated.
43782fc4cc6SVitaly Buka #pragma clang diagnostic push
43882fc4cc6SVitaly Buka #pragma clang diagnostic ignored "-Wdeprecated-declarations"
439fd1721d8SChristopher Ferris   const FieldType BypassQuarantineSize = 1024U;
4402be59170SKostya Kortchinsky   struct mallinfo MI = mallinfo();
441fd1721d8SChristopher Ferris   FieldType Allocated = MI.uordblks;
4422be59170SKostya Kortchinsky   void *P = malloc(BypassQuarantineSize);
4432be59170SKostya Kortchinsky   EXPECT_NE(P, nullptr);
4442be59170SKostya Kortchinsky   MI = mallinfo();
445fd1721d8SChristopher Ferris   EXPECT_GE(MI.uordblks, Allocated + BypassQuarantineSize);
44679e96b24SMitch Phillips   EXPECT_GT(MI.hblkhd, static_cast<FieldType>(0));
447fd1721d8SChristopher Ferris   FieldType Free = MI.fordblks;
4482be59170SKostya Kortchinsky   free(P);
4492be59170SKostya Kortchinsky   MI = mallinfo();
450fd1721d8SChristopher Ferris   EXPECT_GE(MI.fordblks, Free + BypassQuarantineSize);
45182fc4cc6SVitaly Buka #pragma clang diagnostic pop
45282fc4cc6SVitaly Buka }
453fd1721d8SChristopher Ferris 
454fd1721d8SChristopher Ferris #if !SCUDO_FUCHSIA
TEST_F(ScudoWrappersCTest,MallInfo)455fd1721d8SChristopher Ferris TEST_F(ScudoWrappersCTest, MallInfo) {
456fd1721d8SChristopher Ferris #if SCUDO_ANDROID
457fd1721d8SChristopher Ferris   // Android accidentally set the fields to size_t instead of int.
458fd1721d8SChristopher Ferris   MallInfoTest<size_t>();
459fd1721d8SChristopher Ferris #else
460fd1721d8SChristopher Ferris   MallInfoTest<int>();
461fd1721d8SChristopher Ferris #endif
462fd1721d8SChristopher Ferris }
46382fc4cc6SVitaly Buka #endif
46482fc4cc6SVitaly Buka 
465fd1721d8SChristopher Ferris #if __GLIBC_PREREQ(2, 33) || SCUDO_ANDROID
TEST_F(ScudoWrappersCTest,MallInfo2)46688852964SChia-hung Duan TEST_F(ScudoWrappersCTest, MallInfo2) {
46782fc4cc6SVitaly Buka   const size_t BypassQuarantineSize = 1024U;
46882fc4cc6SVitaly Buka   struct mallinfo2 MI = mallinfo2();
46982fc4cc6SVitaly Buka   size_t Allocated = MI.uordblks;
47082fc4cc6SVitaly Buka   void *P = malloc(BypassQuarantineSize);
47182fc4cc6SVitaly Buka   EXPECT_NE(P, nullptr);
47282fc4cc6SVitaly Buka   MI = mallinfo2();
47382fc4cc6SVitaly Buka   EXPECT_GE(MI.uordblks, Allocated + BypassQuarantineSize);
47482fc4cc6SVitaly Buka   EXPECT_GT(MI.hblkhd, 0U);
47582fc4cc6SVitaly Buka   size_t Free = MI.fordblks;
47682fc4cc6SVitaly Buka   free(P);
47782fc4cc6SVitaly Buka   MI = mallinfo2();
47882fc4cc6SVitaly Buka   EXPECT_GE(MI.fordblks, Free + BypassQuarantineSize);
4792be59170SKostya Kortchinsky }
4800d3d4d3bSKostya Kortchinsky #endif
4813e5360f1SKostya Kortchinsky 
4823e5360f1SKostya Kortchinsky static uintptr_t BoundaryP;
4833e5360f1SKostya Kortchinsky static size_t Count;
4843e5360f1SKostya Kortchinsky 
callback(uintptr_t Base,UNUSED size_t Size,UNUSED void * Arg)485af41f79fSChristopher Ferris static void callback(uintptr_t Base, UNUSED size_t Size, UNUSED void *Arg) {
4861e6d1353SVitaly Buka   if (scudo::archSupportsMemoryTagging()) {
4871e6d1353SVitaly Buka     Base = scudo::untagPointer(Base);
4881e6d1353SVitaly Buka     BoundaryP = scudo::untagPointer(BoundaryP);
4891e6d1353SVitaly Buka   }
4903e5360f1SKostya Kortchinsky   if (Base == BoundaryP)
4913e5360f1SKostya Kortchinsky     Count++;
4923e5360f1SKostya Kortchinsky }
4933e5360f1SKostya Kortchinsky 
4943e5360f1SKostya Kortchinsky // Verify that a block located on an iteration boundary is not mis-accounted.
4953e5360f1SKostya Kortchinsky // To achieve this, we allocate a chunk for which the backing block will be
4963e5360f1SKostya Kortchinsky // aligned on a page, then run the malloc_iterate on both the pages that the
4973e5360f1SKostya Kortchinsky // block is a boundary for. It must only be seen once by the callback function.
TEST_F(ScudoWrappersCTest,MallocIterateBoundary)49888852964SChia-hung Duan TEST_F(ScudoWrappersCTest, MallocIterateBoundary) {
499af41f79fSChristopher Ferris   const size_t PageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
500261d9e58SChristopher Ferris #if SCUDO_ANDROID
501261d9e58SChristopher Ferris   // Android uses a 16 byte alignment for both 32 bit and 64 bit.
502261d9e58SChristopher Ferris   const size_t BlockDelta = 16U;
503261d9e58SChristopher Ferris #else
5046ee594beSChristopher Ferris   const size_t BlockDelta = FIRST_32_SECOND_64(8U, 16U);
505261d9e58SChristopher Ferris #endif
5063e5360f1SKostya Kortchinsky   const size_t SpecialSize = PageSize - BlockDelta;
5073e5360f1SKostya Kortchinsky 
50887303fd9SPeter Collingbourne   // We aren't guaranteed that any size class is exactly a page wide. So we need
509261d9e58SChristopher Ferris   // to keep making allocations until we get an allocation that starts exactly
510261d9e58SChristopher Ferris   // on a page boundary. The BlockDelta value is expected to be the number of
511261d9e58SChristopher Ferris   // bytes to subtract from a returned pointer to get to the actual start of
512261d9e58SChristopher Ferris   // the pointer in the size class. In practice, this means BlockDelta should
513261d9e58SChristopher Ferris   // be set to the minimum alignment in bytes for the allocation.
51487303fd9SPeter Collingbourne   //
51587303fd9SPeter Collingbourne   // With a 16-byte block alignment and 4096-byte page size, each allocation has
51687303fd9SPeter Collingbourne   // a probability of (1 - (16/4096)) of failing to meet the alignment
51787303fd9SPeter Collingbourne   // requirements, and the probability of failing 65536 times is
51887303fd9SPeter Collingbourne   // (1 - (16/4096))^65536 < 10^-112. So if we still haven't succeeded after
51987303fd9SPeter Collingbourne   // 65536 tries, give up.
52087303fd9SPeter Collingbourne   uintptr_t Block;
52187303fd9SPeter Collingbourne   void *P = nullptr;
52287303fd9SPeter Collingbourne   for (unsigned I = 0; I != 65536; ++I) {
52387303fd9SPeter Collingbourne     void *PrevP = P;
52487303fd9SPeter Collingbourne     P = malloc(SpecialSize);
5253e5360f1SKostya Kortchinsky     EXPECT_NE(P, nullptr);
52687303fd9SPeter Collingbourne     *reinterpret_cast<void **>(P) = PrevP;
5273e5360f1SKostya Kortchinsky     BoundaryP = reinterpret_cast<uintptr_t>(P);
52887303fd9SPeter Collingbourne     Block = BoundaryP - BlockDelta;
52987303fd9SPeter Collingbourne     if ((Block & (PageSize - 1)) == 0U)
53087303fd9SPeter Collingbourne       break;
53187303fd9SPeter Collingbourne   }
5323e5360f1SKostya Kortchinsky   EXPECT_EQ((Block & (PageSize - 1)), 0U);
5333e5360f1SKostya Kortchinsky 
5343e5360f1SKostya Kortchinsky   Count = 0U;
5353e5360f1SKostya Kortchinsky   malloc_disable();
5363e5360f1SKostya Kortchinsky   malloc_iterate(Block - PageSize, PageSize, callback, nullptr);
5373e5360f1SKostya Kortchinsky   malloc_iterate(Block, PageSize, callback, nullptr);
5383e5360f1SKostya Kortchinsky   malloc_enable();
5393e5360f1SKostya Kortchinsky   EXPECT_EQ(Count, 1U);
5403e5360f1SKostya Kortchinsky 
54187303fd9SPeter Collingbourne   while (P) {
54287303fd9SPeter Collingbourne     void *NextP = *reinterpret_cast<void **>(P);
5433e5360f1SKostya Kortchinsky     free(P);
54487303fd9SPeter Collingbourne     P = NextP;
54587303fd9SPeter Collingbourne   }
5463e5360f1SKostya Kortchinsky }
547dc802dbeSKostya Kortchinsky 
548e9cc5fefSKostya Kortchinsky // Fuchsia doesn't have alarm, fork or malloc_info.
549e9cc5fefSKostya Kortchinsky #if !SCUDO_FUCHSIA
TEST_F(ScudoWrappersCDeathTest,MallocDisableDeadlock)55088852964SChia-hung Duan TEST_F(ScudoWrappersCDeathTest, MallocDisableDeadlock) {
551e9cc5fefSKostya Kortchinsky   // We expect heap operations within a disable/enable scope to deadlock.
55277e906acSKostya Kortchinsky   EXPECT_DEATH(
55377e906acSKostya Kortchinsky       {
55477e906acSKostya Kortchinsky         void *P = malloc(Size);
55577e906acSKostya Kortchinsky         EXPECT_NE(P, nullptr);
55677e906acSKostya Kortchinsky         free(P);
55777e906acSKostya Kortchinsky         malloc_disable();
55877e906acSKostya Kortchinsky         alarm(1);
55977e906acSKostya Kortchinsky         P = malloc(Size);
56077e906acSKostya Kortchinsky         malloc_enable();
56177e906acSKostya Kortchinsky       },
56277e906acSKostya Kortchinsky       "");
56377e906acSKostya Kortchinsky }
56477e906acSKostya Kortchinsky 
TEST_F(ScudoWrappersCTest,MallocInfo)56588852964SChia-hung Duan TEST_F(ScudoWrappersCTest, MallocInfo) {
5669068766bSPeter Collingbourne   // Use volatile so that the allocations don't get optimized away.
5679068766bSPeter Collingbourne   void *volatile P1 = malloc(1234);
5689068766bSPeter Collingbourne   void *volatile P2 = malloc(4321);
5699068766bSPeter Collingbourne 
5709068766bSPeter Collingbourne   char Buffer[16384];
571dc802dbeSKostya Kortchinsky   FILE *F = fmemopen(Buffer, sizeof(Buffer), "w+");
572dc802dbeSKostya Kortchinsky   EXPECT_NE(F, nullptr);
573dc802dbeSKostya Kortchinsky   errno = 0;
574dc802dbeSKostya Kortchinsky   EXPECT_EQ(malloc_info(0, F), 0);
575dc802dbeSKostya Kortchinsky   EXPECT_EQ(errno, 0);
576dc802dbeSKostya Kortchinsky   fclose(F);
577dc802dbeSKostya Kortchinsky   EXPECT_EQ(strncmp(Buffer, "<malloc version=\"scudo-", 23), 0);
5789068766bSPeter Collingbourne   EXPECT_NE(nullptr, strstr(Buffer, "<alloc size=\"1234\" count=\""));
5799068766bSPeter Collingbourne   EXPECT_NE(nullptr, strstr(Buffer, "<alloc size=\"4321\" count=\""));
5809068766bSPeter Collingbourne 
5819068766bSPeter Collingbourne   free(P1);
5829068766bSPeter Collingbourne   free(P2);
583dc802dbeSKostya Kortchinsky }
5849ef6faf4SKostya Kortchinsky 
TEST_F(ScudoWrappersCDeathTest,Fork)58588852964SChia-hung Duan TEST_F(ScudoWrappersCDeathTest, Fork) {
5869ef6faf4SKostya Kortchinsky   void *P;
5879ef6faf4SKostya Kortchinsky   pid_t Pid = fork();
588b41b76b3SVitaly Buka   EXPECT_GE(Pid, 0) << strerror(errno);
5899ef6faf4SKostya Kortchinsky   if (Pid == 0) {
5909ef6faf4SKostya Kortchinsky     P = malloc(Size);
5919ef6faf4SKostya Kortchinsky     EXPECT_NE(P, nullptr);
5929ef6faf4SKostya Kortchinsky     memset(P, 0x42, Size);
5939ef6faf4SKostya Kortchinsky     free(P);
5949ef6faf4SKostya Kortchinsky     _exit(0);
5959ef6faf4SKostya Kortchinsky   }
5969ef6faf4SKostya Kortchinsky   waitpid(Pid, nullptr, 0);
5979ef6faf4SKostya Kortchinsky   P = malloc(Size);
5989ef6faf4SKostya Kortchinsky   EXPECT_NE(P, nullptr);
5999ef6faf4SKostya Kortchinsky   memset(P, 0x42, Size);
6009ef6faf4SKostya Kortchinsky   free(P);
6019ef6faf4SKostya Kortchinsky 
6029ef6faf4SKostya Kortchinsky   // fork should stall if the allocator has been disabled.
6039ef6faf4SKostya Kortchinsky   EXPECT_DEATH(
6049ef6faf4SKostya Kortchinsky       {
6059ef6faf4SKostya Kortchinsky         malloc_disable();
6069ef6faf4SKostya Kortchinsky         alarm(1);
6079ef6faf4SKostya Kortchinsky         Pid = fork();
6089ef6faf4SKostya Kortchinsky         EXPECT_GE(Pid, 0);
6099ef6faf4SKostya Kortchinsky       },
6109ef6faf4SKostya Kortchinsky       "");
6119ef6faf4SKostya Kortchinsky }
6129ef6faf4SKostya Kortchinsky 
6139ef6faf4SKostya Kortchinsky static pthread_mutex_t Mutex;
6149ef6faf4SKostya Kortchinsky static pthread_cond_t Conditional = PTHREAD_COND_INITIALIZER;
615519959adSEvgenii Stepanov static bool Ready;
6169ef6faf4SKostya Kortchinsky 
enableMalloc(UNUSED void * Unused)617af41f79fSChristopher Ferris static void *enableMalloc(UNUSED void *Unused) {
6189ef6faf4SKostya Kortchinsky   // Initialize the allocator for this thread.
6199ef6faf4SKostya Kortchinsky   void *P = malloc(Size);
6209ef6faf4SKostya Kortchinsky   EXPECT_NE(P, nullptr);
6219ef6faf4SKostya Kortchinsky   memset(P, 0x42, Size);
6229ef6faf4SKostya Kortchinsky   free(P);
6239ef6faf4SKostya Kortchinsky 
6249ef6faf4SKostya Kortchinsky   // Signal the main thread we are ready.
6259ef6faf4SKostya Kortchinsky   pthread_mutex_lock(&Mutex);
626519959adSEvgenii Stepanov   Ready = true;
6279ef6faf4SKostya Kortchinsky   pthread_cond_signal(&Conditional);
6289ef6faf4SKostya Kortchinsky   pthread_mutex_unlock(&Mutex);
6299ef6faf4SKostya Kortchinsky 
6309ef6faf4SKostya Kortchinsky   // Wait for the malloc_disable & fork, then enable the allocator again.
6319ef6faf4SKostya Kortchinsky   sleep(1);
6329ef6faf4SKostya Kortchinsky   malloc_enable();
6339ef6faf4SKostya Kortchinsky 
6349ef6faf4SKostya Kortchinsky   return nullptr;
6359ef6faf4SKostya Kortchinsky }
6369ef6faf4SKostya Kortchinsky 
TEST_F(ScudoWrappersCTest,DisableForkEnable)63788852964SChia-hung Duan TEST_F(ScudoWrappersCTest, DisableForkEnable) {
6389ef6faf4SKostya Kortchinsky   pthread_t ThreadId;
6396f00f3b5SKostya Kortchinsky   Ready = false;
6409ef6faf4SKostya Kortchinsky   EXPECT_EQ(pthread_create(&ThreadId, nullptr, &enableMalloc, nullptr), 0);
6419ef6faf4SKostya Kortchinsky 
6429ef6faf4SKostya Kortchinsky   // Wait for the thread to be warmed up.
6439ef6faf4SKostya Kortchinsky   pthread_mutex_lock(&Mutex);
644519959adSEvgenii Stepanov   while (!Ready)
6459ef6faf4SKostya Kortchinsky     pthread_cond_wait(&Conditional, &Mutex);
6469ef6faf4SKostya Kortchinsky   pthread_mutex_unlock(&Mutex);
6479ef6faf4SKostya Kortchinsky 
6489ef6faf4SKostya Kortchinsky   // Disable the allocator and fork. fork should succeed after malloc_enable.
6499ef6faf4SKostya Kortchinsky   malloc_disable();
6509ef6faf4SKostya Kortchinsky   pid_t Pid = fork();
6519ef6faf4SKostya Kortchinsky   EXPECT_GE(Pid, 0);
6529ef6faf4SKostya Kortchinsky   if (Pid == 0) {
6539ef6faf4SKostya Kortchinsky     void *P = malloc(Size);
6549ef6faf4SKostya Kortchinsky     EXPECT_NE(P, nullptr);
6559ef6faf4SKostya Kortchinsky     memset(P, 0x42, Size);
6569ef6faf4SKostya Kortchinsky     free(P);
6579ef6faf4SKostya Kortchinsky     _exit(0);
6589ef6faf4SKostya Kortchinsky   }
6599ef6faf4SKostya Kortchinsky   waitpid(Pid, nullptr, 0);
6609ef6faf4SKostya Kortchinsky   EXPECT_EQ(pthread_join(ThreadId, 0), 0);
6619ef6faf4SKostya Kortchinsky }
6629ef6faf4SKostya Kortchinsky 
6639ef6faf4SKostya Kortchinsky #endif // SCUDO_FUCHSIA
664