1//===-- wrappers_c_test.cpp -------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "common.h"
10#include "memtag.h"
11#include "scudo/interface.h"
12#include "tests/scudo_unit_test.h"
13
14#include <errno.h>
15#include <limits.h>
16#include <malloc.h>
17#include <stdlib.h>
18#include <unistd.h>
19#include <vector>
20
21#ifndef __GLIBC_PREREQ
22#define __GLIBC_PREREQ(x, y) 0
23#endif
24
25#if SCUDO_FUCHSIA
26// Fuchsia only has valloc
27#define HAVE_VALLOC 1
28#elif SCUDO_ANDROID
29// Android only has pvalloc/valloc on 32 bit
30#if !defined(__LP64__)
31#define HAVE_PVALLOC 1
32#define HAVE_VALLOC 1
33#endif // !defined(__LP64__)
34#else
35// All others assumed to support both functions.
36#define HAVE_PVALLOC 1
37#define HAVE_VALLOC 1
38#endif
39
40extern "C" {
41void malloc_enable(void);
42void malloc_disable(void);
43int malloc_iterate(uintptr_t base, size_t size,
44 void (*callback)(uintptr_t base, size_t size, void *arg),
45 void *arg);
46void *valloc(size_t size);
47void *pvalloc(size_t size);
48
49#ifndef SCUDO_ENABLE_HOOKS_TESTS
50#define SCUDO_ENABLE_HOOKS_TESTS 0
51#endif
52
53#if (SCUDO_ENABLE_HOOKS_TESTS == 1) && (SCUDO_ENABLE_HOOKS == 0)
54#error "Hooks tests should have hooks enabled as well!"
55#endif
56
57struct AllocContext {
58 void *Ptr;
59 size_t Size;
60};
61struct DeallocContext {
62 void *Ptr;
63};
64struct ReallocContext {
65 void *AllocPtr;
66 void *DeallocPtr;
67 size_t Size;
68};
69static AllocContext AC;
70static DeallocContext DC;
71static ReallocContext RC;
72
73#if (SCUDO_ENABLE_HOOKS_TESTS == 1)
74__attribute__((visibility("default"))) void __scudo_allocate_hook(void *Ptr,
75 size_t Size) {
76 AC.Ptr = Ptr;
77 AC.Size = Size;
78}
79__attribute__((visibility("default"))) void __scudo_deallocate_hook(void *Ptr) {
80 DC.Ptr = Ptr;
81}
82__attribute__((visibility("default"))) void
83__scudo_realloc_allocate_hook(void *OldPtr, void *NewPtr, size_t Size) {
84 // Verify that __scudo_realloc_deallocate_hook is called first and set the
85 // right pointer.
86 EXPECT_EQ(OldPtr, RC.DeallocPtr);
87 RC.AllocPtr = NewPtr;
88 RC.Size = Size;
89
90 // Note that this is only used for testing. In general, only one pair of hooks
91 // will be invoked in `realloc`. if __scudo_realloc_*_hook are not defined,
92 // it'll call the general hooks only. To make the test easier, we call the
93 // general one here so that either case (whether __scudo_realloc_*_hook are
94 // defined) will be verified without separating them into different tests.
95 __scudo_allocate_hook(NewPtr, Size);
96}
97__attribute__((visibility("default"))) void
98__scudo_realloc_deallocate_hook(void *Ptr) {
99 RC.DeallocPtr = Ptr;
100
101 // See the comment in the __scudo_realloc_allocate_hook above.
102 __scudo_deallocate_hook(Ptr);
103}
104#endif // (SCUDO_ENABLE_HOOKS_TESTS == 1)
105}
106
107class ScudoWrappersCTest : public Test {
108protected:
109 void SetUp() override {
110 if (SCUDO_ENABLE_HOOKS && !SCUDO_ENABLE_HOOKS_TESTS)
111 printf(format: "Hooks are enabled but hooks tests are disabled.\n");
112 }
113
114 void invalidateHookPtrs() {
115 if (SCUDO_ENABLE_HOOKS_TESTS) {
116 void *InvalidPtr = reinterpret_cast<void *>(0xdeadbeef);
117 AC.Ptr = InvalidPtr;
118 DC.Ptr = InvalidPtr;
119 RC.AllocPtr = RC.DeallocPtr = InvalidPtr;
120 }
121 }
122 void verifyAllocHookPtr(UNUSED void *Ptr) {
123 if (SCUDO_ENABLE_HOOKS_TESTS)
124 EXPECT_EQ(Ptr, AC.Ptr);
125 }
126 void verifyAllocHookSize(UNUSED size_t Size) {
127 if (SCUDO_ENABLE_HOOKS_TESTS)
128 EXPECT_EQ(Size, AC.Size);
129 }
130 void verifyDeallocHookPtr(UNUSED void *Ptr) {
131 if (SCUDO_ENABLE_HOOKS_TESTS)
132 EXPECT_EQ(Ptr, DC.Ptr);
133 }
134 void verifyReallocHookPtrs(UNUSED void *OldPtr, void *NewPtr, size_t Size) {
135 if (SCUDO_ENABLE_HOOKS_TESTS) {
136 EXPECT_EQ(OldPtr, RC.DeallocPtr);
137 EXPECT_EQ(NewPtr, RC.AllocPtr);
138 EXPECT_EQ(Size, RC.Size);
139 }
140 }
141};
142using ScudoWrappersCDeathTest = ScudoWrappersCTest;
143
144// Note that every C allocation function in the test binary will be fulfilled
145// by Scudo (this includes the gtest APIs, etc.), which is a test by itself.
146// But this might also lead to unexpected side-effects, since the allocation and
147// deallocation operations in the TEST functions will coexist with others (see
148// the EXPECT_DEATH comment below).
149
150// We have to use a small quarantine to make sure that our double-free tests
151// trigger. Otherwise EXPECT_DEATH ends up reallocating the chunk that was just
152// freed (this depends on the size obviously) and the following free succeeds.
153
154static const size_t Size = 100U;
155
156TEST_F(ScudoWrappersCDeathTest, Malloc) {
157 void *P = malloc(size: Size);
158 EXPECT_NE(P, nullptr);
159 EXPECT_LE(Size, malloc_usable_size(ptr: P));
160 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % FIRST_32_SECOND_64(8U, 16U), 0U);
161 verifyAllocHookPtr(P);
162 verifyAllocHookSize(Size);
163
164 // An update to this warning in Clang now triggers in this line, but it's ok
165 // because the check is expecting a bad pointer and should fail.
166#if defined(__has_warning) && __has_warning("-Wfree-nonheap-object")
167#pragma GCC diagnostic push
168#pragma GCC diagnostic ignored "-Wfree-nonheap-object"
169#endif
170 EXPECT_DEATH(
171 free(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(P) | 1U)), "");
172#if defined(__has_warning) && __has_warning("-Wfree-nonheap-object")
173#pragma GCC diagnostic pop
174#endif
175
176 free(ptr: P);
177 verifyDeallocHookPtr(P);
178 EXPECT_DEATH(free(P), "");
179
180 P = malloc(size: 0U);
181 EXPECT_NE(P, nullptr);
182 free(ptr: P);
183
184 errno = 0;
185 EXPECT_EQ(malloc(SIZE_MAX), nullptr);
186 EXPECT_EQ(errno, ENOMEM);
187}
188
189TEST_F(ScudoWrappersCTest, Calloc) {
190 void *P = calloc(nmemb: 1U, size: Size);
191 EXPECT_NE(P, nullptr);
192 EXPECT_LE(Size, malloc_usable_size(ptr: P));
193 verifyAllocHookPtr(P);
194 verifyAllocHookSize(Size);
195 for (size_t I = 0; I < Size; I++)
196 EXPECT_EQ((reinterpret_cast<uint8_t *>(P))[I], 0U);
197 free(ptr: P);
198 verifyDeallocHookPtr(P);
199
200 P = calloc(nmemb: 1U, size: 0U);
201 EXPECT_NE(P, nullptr);
202 free(ptr: P);
203 P = calloc(nmemb: 0U, size: 1U);
204 EXPECT_NE(P, nullptr);
205 free(ptr: P);
206
207 errno = 0;
208 EXPECT_EQ(calloc(SIZE_MAX, size: 1U), nullptr);
209 EXPECT_EQ(errno, ENOMEM);
210 errno = 0;
211 EXPECT_EQ(calloc(nmemb: static_cast<size_t>(LONG_MAX) + 1U, size: 2U), nullptr);
212 if (SCUDO_ANDROID)
213 EXPECT_EQ(errno, ENOMEM);
214 errno = 0;
215 EXPECT_EQ(calloc(SIZE_MAX, SIZE_MAX), nullptr);
216 EXPECT_EQ(errno, ENOMEM);
217}
218
219TEST_F(ScudoWrappersCTest, SmallAlign) {
220 // Allocating pointers by the powers of 2 from 1 to 0x10000
221 // Using powers of 2 due to memalign using powers of 2 and test more sizes
222 constexpr size_t MaxSize = 0x10000;
223 std::vector<void *> ptrs;
224 // Reserving space to prevent further allocation during the test
225 ptrs.reserve((scudo::getLeastSignificantSetBitIndex(MaxSize) + 1) *
226 (scudo::getLeastSignificantSetBitIndex(MaxSize) + 1) * 3);
227 for (size_t Size = 1; Size <= MaxSize; Size <<= 1) {
228 for (size_t Align = 1; Align <= MaxSize; Align <<= 1) {
229 for (size_t Count = 0; Count < 3; ++Count) {
230 void *P = memalign(alignment: Align, size: Size);
231 EXPECT_TRUE(reinterpret_cast<uintptr_t>(P) % Align == 0);
232 ptrs.push_back(P);
233 }
234 }
235 }
236 for (void *ptr : ptrs)
237 free(ptr);
238}
239
240TEST_F(ScudoWrappersCTest, Memalign) {
241 void *P;
242 for (size_t I = FIRST_32_SECOND_64(2U, 3U); I <= 18U; I++) {
243 const size_t Alignment = 1U << I;
244
245 P = memalign(alignment: Alignment, size: Size);
246 EXPECT_NE(P, nullptr);
247 EXPECT_LE(Size, malloc_usable_size(ptr: P));
248 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
249 verifyAllocHookPtr(P);
250 verifyAllocHookSize(Size);
251 free(ptr: P);
252 verifyDeallocHookPtr(P);
253
254 P = nullptr;
255 EXPECT_EQ(posix_memalign(memptr: &P, alignment: Alignment, size: Size), 0);
256 EXPECT_NE(P, nullptr);
257 EXPECT_LE(Size, malloc_usable_size(ptr: P));
258 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
259 verifyAllocHookPtr(P);
260 verifyAllocHookSize(Size);
261 free(ptr: P);
262 verifyDeallocHookPtr(P);
263 }
264
265 EXPECT_EQ(memalign(alignment: 4096U, SIZE_MAX), nullptr);
266 EXPECT_EQ(posix_memalign(memptr: &P, alignment: 15U, size: Size), EINVAL);
267 EXPECT_EQ(posix_memalign(memptr: &P, alignment: 4096U, SIZE_MAX), ENOMEM);
268
269 // Android's memalign accepts non power-of-2 alignments, and 0.
270 if (SCUDO_ANDROID) {
271 for (size_t Alignment = 0U; Alignment <= 128U; Alignment++) {
272 P = memalign(alignment: Alignment, size: 1024U);
273 EXPECT_NE(P, nullptr);
274 verifyAllocHookPtr(P);
275 verifyAllocHookSize(Size);
276 free(ptr: P);
277 verifyDeallocHookPtr(P);
278 }
279 }
280}
281
282TEST_F(ScudoWrappersCTest, AlignedAlloc) {
283 const size_t Alignment = 4096U;
284 void *P = aligned_alloc(alignment: Alignment, size: Alignment * 4U);
285 EXPECT_NE(P, nullptr);
286 EXPECT_LE(Alignment * 4U, malloc_usable_size(ptr: P));
287 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
288 verifyAllocHookPtr(P);
289 verifyAllocHookSize(Alignment * 4U);
290 free(ptr: P);
291 verifyDeallocHookPtr(P);
292
293 errno = 0;
294 P = aligned_alloc(alignment: Alignment, size: Size);
295 EXPECT_EQ(P, nullptr);
296 EXPECT_EQ(errno, EINVAL);
297}
298
299TEST_F(ScudoWrappersCDeathTest, Realloc) {
300 invalidateHookPtrs();
301 // realloc(nullptr, N) is malloc(N)
302 void *P = realloc(ptr: nullptr, size: Size);
303 EXPECT_NE(P, nullptr);
304 verifyAllocHookPtr(P);
305 verifyAllocHookSize(Size);
306 free(ptr: P);
307 verifyDeallocHookPtr(P);
308
309 invalidateHookPtrs();
310 P = malloc(size: Size);
311 EXPECT_NE(P, nullptr);
312 // realloc(P, 0U) is free(P) and returns nullptr
313 EXPECT_EQ(realloc(ptr: P, size: 0U), nullptr);
314 verifyDeallocHookPtr(P);
315
316 P = malloc(size: Size);
317 EXPECT_NE(P, nullptr);
318 EXPECT_LE(Size, malloc_usable_size(ptr: P));
319 memset(s: P, c: 0x42, n: Size);
320
321 invalidateHookPtrs();
322 void *OldP = P;
323 P = realloc(ptr: P, size: Size * 2U);
324 EXPECT_NE(P, nullptr);
325 EXPECT_LE(Size * 2U, malloc_usable_size(ptr: P));
326 for (size_t I = 0; I < Size; I++)
327 EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
328 if (OldP == P) {
329 verifyDeallocHookPtr(OldP);
330 verifyAllocHookPtr(OldP);
331 } else {
332 verifyAllocHookPtr(P);
333 verifyAllocHookSize(Size * 2U);
334 verifyDeallocHookPtr(OldP);
335 }
336 verifyReallocHookPtrs(OldP, P, Size * 2U);
337
338 invalidateHookPtrs();
339 OldP = P;
340 P = realloc(ptr: P, size: Size / 2U);
341 EXPECT_NE(P, nullptr);
342 EXPECT_LE(Size / 2U, malloc_usable_size(ptr: P));
343 for (size_t I = 0; I < Size / 2U; I++)
344 EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
345 if (OldP == P) {
346 verifyDeallocHookPtr(OldP);
347 verifyAllocHookPtr(OldP);
348 } else {
349 verifyAllocHookPtr(P);
350 verifyAllocHookSize(Size / 2U);
351 }
352 verifyReallocHookPtrs(OldP, P, Size / 2U);
353 free(ptr: P);
354
355 EXPECT_DEATH(P = realloc(P, Size), "");
356
357 errno = 0;
358 EXPECT_EQ(realloc(ptr: nullptr, SIZE_MAX), nullptr);
359 EXPECT_EQ(errno, ENOMEM);
360 P = malloc(size: Size);
361 EXPECT_NE(P, nullptr);
362 errno = 0;
363 EXPECT_EQ(realloc(ptr: P, SIZE_MAX), nullptr);
364 EXPECT_EQ(errno, ENOMEM);
365 free(ptr: P);
366
367 // Android allows realloc of memalign pointers.
368 if (SCUDO_ANDROID) {
369 const size_t Alignment = 1024U;
370 P = memalign(alignment: Alignment, size: Size);
371 EXPECT_NE(P, nullptr);
372 EXPECT_LE(Size, malloc_usable_size(ptr: P));
373 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
374 memset(s: P, c: 0x42, n: Size);
375
376 P = realloc(ptr: P, size: Size * 2U);
377 EXPECT_NE(P, nullptr);
378 EXPECT_LE(Size * 2U, malloc_usable_size(ptr: P));
379 for (size_t I = 0; I < Size; I++)
380 EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
381 free(ptr: P);
382 }
383}
384
385#if !SCUDO_FUCHSIA
386TEST_F(ScudoWrappersCTest, MallOpt) {
387 errno = 0;
388 EXPECT_EQ(mallopt(param: -1000, val: 1), 0);
389 // mallopt doesn't set errno.
390 EXPECT_EQ(errno, 0);
391
392 EXPECT_EQ(mallopt(M_PURGE, val: 0), 1);
393
394 EXPECT_EQ(mallopt(M_DECAY_TIME, val: 1), 1);
395 EXPECT_EQ(mallopt(M_DECAY_TIME, val: 0), 1);
396 EXPECT_EQ(mallopt(M_DECAY_TIME, val: 1), 1);
397 EXPECT_EQ(mallopt(M_DECAY_TIME, val: 0), 1);
398
399 if (SCUDO_ANDROID) {
400 EXPECT_EQ(mallopt(M_CACHE_COUNT_MAX, val: 100), 1);
401 EXPECT_EQ(mallopt(M_CACHE_SIZE_MAX, val: 1024 * 1024 * 2), 1);
402 EXPECT_EQ(mallopt(M_TSDS_COUNT_MAX, val: 10), 1);
403 }
404}
405#endif
406
407TEST_F(ScudoWrappersCTest, OtherAlloc) {
408#if HAVE_PVALLOC
409 const size_t PageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
410
411 void *P = pvalloc(size: Size);
412 EXPECT_NE(P, nullptr);
413 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) & (PageSize - 1), 0U);
414 EXPECT_LE(PageSize, malloc_usable_size(ptr: P));
415 verifyAllocHookPtr(P);
416 // Size will be rounded up to PageSize.
417 verifyAllocHookSize(PageSize);
418 free(ptr: P);
419 verifyDeallocHookPtr(P);
420
421 EXPECT_EQ(pvalloc(SIZE_MAX), nullptr);
422
423 P = pvalloc(size: Size);
424 EXPECT_NE(P, nullptr);
425 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) & (PageSize - 1), 0U);
426 free(ptr: P);
427#endif
428
429#if HAVE_VALLOC
430 EXPECT_EQ(valloc(SIZE_MAX), nullptr);
431#endif
432}
433
434template<typename FieldType>
435void MallInfoTest() {
436 // mallinfo is deprecated.
437#pragma clang diagnostic push
438#pragma clang diagnostic ignored "-Wdeprecated-declarations"
439 const FieldType BypassQuarantineSize = 1024U;
440 struct mallinfo MI = mallinfo();
441 FieldType Allocated = MI.uordblks;
442 void *P = malloc(BypassQuarantineSize);
443 EXPECT_NE(P, nullptr);
444 MI = mallinfo();
445 EXPECT_GE(MI.uordblks, Allocated + BypassQuarantineSize);
446 EXPECT_GT(MI.hblkhd, static_cast<FieldType>(0));
447 FieldType Free = MI.fordblks;
448 free(ptr: P);
449 MI = mallinfo();
450 EXPECT_GE(MI.fordblks, Free + BypassQuarantineSize);
451#pragma clang diagnostic pop
452}
453
454#if !SCUDO_FUCHSIA
455TEST_F(ScudoWrappersCTest, MallInfo) {
456#if SCUDO_ANDROID
457 // Android accidentally set the fields to size_t instead of int.
458 MallInfoTest<size_t>();
459#else
460 MallInfoTest<int>();
461#endif
462}
463#endif
464
465#if __GLIBC_PREREQ(2, 33) || SCUDO_ANDROID
466TEST_F(ScudoWrappersCTest, MallInfo2) {
467 const size_t BypassQuarantineSize = 1024U;
468 struct mallinfo2 MI = mallinfo2();
469 size_t Allocated = MI.uordblks;
470 void *P = malloc(size: BypassQuarantineSize);
471 EXPECT_NE(P, nullptr);
472 MI = mallinfo2();
473 EXPECT_GE(MI.uordblks, Allocated + BypassQuarantineSize);
474 EXPECT_GT(MI.hblkhd, 0U);
475 size_t Free = MI.fordblks;
476 free(ptr: P);
477 MI = mallinfo2();
478 EXPECT_GE(MI.fordblks, Free + BypassQuarantineSize);
479}
480#endif
481
482static uintptr_t BoundaryP;
483static size_t Count;
484
485static void callback(uintptr_t Base, UNUSED size_t Size, UNUSED void *Arg) {
486 if (scudo::archSupportsMemoryTagging()) {
487 Base = scudo::untagPointer(Ptr: Base);
488 BoundaryP = scudo::untagPointer(Ptr: BoundaryP);
489 }
490 if (Base == BoundaryP)
491 Count++;
492}
493
494// Verify that a block located on an iteration boundary is not mis-accounted.
495// To achieve this, we allocate a chunk for which the backing block will be
496// aligned on a page, then run the malloc_iterate on both the pages that the
497// block is a boundary for. It must only be seen once by the callback function.
498TEST_F(ScudoWrappersCTest, MallocIterateBoundary) {
499 const size_t PageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
500#if SCUDO_ANDROID
501 // Android uses a 16 byte alignment for both 32 bit and 64 bit.
502 const size_t BlockDelta = 16U;
503#else
504 const size_t BlockDelta = FIRST_32_SECOND_64(8U, 16U);
505#endif
506 const size_t SpecialSize = PageSize - BlockDelta;
507
508 // We aren't guaranteed that any size class is exactly a page wide. So we need
509 // to keep making allocations until we get an allocation that starts exactly
510 // on a page boundary. The BlockDelta value is expected to be the number of
511 // bytes to subtract from a returned pointer to get to the actual start of
512 // the pointer in the size class. In practice, this means BlockDelta should
513 // be set to the minimum alignment in bytes for the allocation.
514 //
515 // With a 16-byte block alignment and 4096-byte page size, each allocation has
516 // a probability of (1 - (16/4096)) of failing to meet the alignment
517 // requirements, and the probability of failing 65536 times is
518 // (1 - (16/4096))^65536 < 10^-112. So if we still haven't succeeded after
519 // 65536 tries, give up.
520 uintptr_t Block;
521 void *P = nullptr;
522 for (unsigned I = 0; I != 65536; ++I) {
523 void *PrevP = P;
524 P = malloc(size: SpecialSize);
525 EXPECT_NE(P, nullptr);
526 *reinterpret_cast<void **>(P) = PrevP;
527 BoundaryP = reinterpret_cast<uintptr_t>(P);
528 Block = BoundaryP - BlockDelta;
529 if ((Block & (PageSize - 1)) == 0U)
530 break;
531 }
532 EXPECT_EQ((Block & (PageSize - 1)), 0U);
533
534 Count = 0U;
535 malloc_disable();
536 malloc_iterate(base: Block - PageSize, size: PageSize, callback, arg: nullptr);
537 malloc_iterate(base: Block, size: PageSize, callback, arg: nullptr);
538 malloc_enable();
539 EXPECT_EQ(Count, 1U);
540
541 while (P) {
542 void *NextP = *reinterpret_cast<void **>(P);
543 free(ptr: P);
544 P = NextP;
545 }
546}
547
548// Fuchsia doesn't have alarm, fork or malloc_info.
549#if !SCUDO_FUCHSIA
550TEST_F(ScudoWrappersCDeathTest, MallocDisableDeadlock) {
551 // We expect heap operations within a disable/enable scope to deadlock.
552 EXPECT_DEATH(
553 {
554 void *P = malloc(Size);
555 EXPECT_NE(P, nullptr);
556 free(P);
557 malloc_disable();
558 alarm(1);
559 P = malloc(Size);
560 malloc_enable();
561 },
562 "");
563}
564
565TEST_F(ScudoWrappersCTest, MallocInfo) {
566 // Use volatile so that the allocations don't get optimized away.
567 void *volatile P1 = malloc(size: 1234);
568 void *volatile P2 = malloc(size: 4321);
569
570 char Buffer[16384];
571 FILE *F = fmemopen(s: Buffer, len: sizeof(Buffer), modes: "w+");
572 EXPECT_NE(F, nullptr);
573 errno = 0;
574 EXPECT_EQ(malloc_info(options: 0, fp: F), 0);
575 EXPECT_EQ(errno, 0);
576 fclose(stream: F);
577 EXPECT_EQ(strncmp(s1: Buffer, s2: "<malloc version=\"scudo-", n: 23), 0);
578 EXPECT_NE(nullptr, strstr(haystack: Buffer, needle: "<alloc size=\"1234\" count=\""));
579 EXPECT_NE(nullptr, strstr(haystack: Buffer, needle: "<alloc size=\"4321\" count=\""));
580
581 free(ptr: P1);
582 free(ptr: P2);
583}
584
585TEST_F(ScudoWrappersCDeathTest, Fork) {
586 void *P;
587 pid_t Pid = fork();
588 EXPECT_GE(Pid, 0) << strerror(errno);
589 if (Pid == 0) {
590 P = malloc(size: Size);
591 EXPECT_NE(P, nullptr);
592 memset(s: P, c: 0x42, n: Size);
593 free(ptr: P);
594 _exit(status: 0);
595 }
596 waitpid(Pid, nullptr, 0);
597 P = malloc(size: Size);
598 EXPECT_NE(P, nullptr);
599 memset(s: P, c: 0x42, n: Size);
600 free(ptr: P);
601
602 // fork should stall if the allocator has been disabled.
603 EXPECT_DEATH(
604 {
605 malloc_disable();
606 alarm(1);
607 Pid = fork();
608 EXPECT_GE(Pid, 0);
609 },
610 "");
611}
612
613static pthread_mutex_t Mutex;
614static pthread_cond_t Conditional = PTHREAD_COND_INITIALIZER;
615static bool Ready;
616
617static void *enableMalloc(UNUSED void *Unused) {
618 // Initialize the allocator for this thread.
619 void *P = malloc(size: Size);
620 EXPECT_NE(P, nullptr);
621 memset(s: P, c: 0x42, n: Size);
622 free(ptr: P);
623
624 // Signal the main thread we are ready.
625 pthread_mutex_lock(&Mutex);
626 Ready = true;
627 pthread_cond_signal(&Conditional);
628 pthread_mutex_unlock(&Mutex);
629
630 // Wait for the malloc_disable & fork, then enable the allocator again.
631 sleep(seconds: 1);
632 malloc_enable();
633
634 return nullptr;
635}
636
637TEST_F(ScudoWrappersCTest, DisableForkEnable) {
638 pthread_t ThreadId;
639 Ready = false;
640 EXPECT_EQ(pthread_create(&ThreadId, nullptr, &enableMalloc, nullptr), 0);
641
642 // Wait for the thread to be warmed up.
643 pthread_mutex_lock(&Mutex);
644 while (!Ready)
645 pthread_cond_wait(&Conditional, &Mutex);
646 pthread_mutex_unlock(&Mutex);
647
648 // Disable the allocator and fork. fork should succeed after malloc_enable.
649 malloc_disable();
650 pid_t Pid = fork();
651 EXPECT_GE(Pid, 0);
652 if (Pid == 0) {
653 void *P = malloc(size: Size);
654 EXPECT_NE(P, nullptr);
655 memset(s: P, c: 0x42, n: Size);
656 free(ptr: P);
657 _exit(status: 0);
658 }
659 waitpid(Pid, nullptr, 0);
660 EXPECT_EQ(pthread_join(ThreadId, 0), 0);
661}
662
663#endif // SCUDO_FUCHSIA
664

source code of compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp