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
179 // Verify a double free causes an abort.
180 // Don't simply free(P) since EXPECT_DEATH will do a number of
181 // allocations before creating a new process. There is a possibility
182 // that the previously freed P is reused, therefore, in the new
183 // process doing free(P) is not a double free.
184 EXPECT_DEATH(
185 {
186 // Note: volatile here prevents the calls from being optimized out.
187 void *volatile Ptr = malloc(Size);
188 free(Ptr);
189 free(Ptr);
190 },
191 "");
192
193 P = malloc(size: 0U);
194 EXPECT_NE(P, nullptr);
195 free(ptr: P);
196
197 errno = 0;
198 EXPECT_EQ(malloc(SIZE_MAX), nullptr);
199 EXPECT_EQ(errno, ENOMEM);
200}
201
202TEST_F(ScudoWrappersCTest, Calloc) {
203 void *P = calloc(nmemb: 1U, size: Size);
204 EXPECT_NE(P, nullptr);
205 EXPECT_LE(Size, malloc_usable_size(ptr: P));
206 verifyAllocHookPtr(P);
207 verifyAllocHookSize(Size);
208 for (size_t I = 0; I < Size; I++)
209 EXPECT_EQ((reinterpret_cast<uint8_t *>(P))[I], 0U);
210 free(ptr: P);
211 verifyDeallocHookPtr(P);
212
213 P = calloc(nmemb: 1U, size: 0U);
214 EXPECT_NE(P, nullptr);
215 free(ptr: P);
216 P = calloc(nmemb: 0U, size: 1U);
217 EXPECT_NE(P, nullptr);
218 free(ptr: P);
219
220 errno = 0;
221 EXPECT_EQ(calloc(SIZE_MAX, size: 1U), nullptr);
222 EXPECT_EQ(errno, ENOMEM);
223 errno = 0;
224 EXPECT_EQ(calloc(nmemb: static_cast<size_t>(LONG_MAX) + 1U, size: 2U), nullptr);
225 if (SCUDO_ANDROID)
226 EXPECT_EQ(errno, ENOMEM);
227 errno = 0;
228 EXPECT_EQ(calloc(SIZE_MAX, SIZE_MAX), nullptr);
229 EXPECT_EQ(errno, ENOMEM);
230}
231
232TEST_F(ScudoWrappersCTest, SmallAlign) {
233 // Allocating pointers by the powers of 2 from 1 to 0x10000
234 // Using powers of 2 due to memalign using powers of 2 and test more sizes
235 constexpr size_t MaxSize = 0x10000;
236 std::vector<void *> ptrs;
237 // Reserving space to prevent further allocation during the test
238 ptrs.reserve((scudo::getLeastSignificantSetBitIndex(MaxSize) + 1) *
239 (scudo::getLeastSignificantSetBitIndex(MaxSize) + 1) * 3);
240 for (size_t Size = 1; Size <= MaxSize; Size <<= 1) {
241 for (size_t Align = 1; Align <= MaxSize; Align <<= 1) {
242 for (size_t Count = 0; Count < 3; ++Count) {
243 void *P = memalign(alignment: Align, size: Size);
244 EXPECT_TRUE(reinterpret_cast<uintptr_t>(P) % Align == 0);
245 ptrs.push_back(P);
246 }
247 }
248 }
249 for (void *ptr : ptrs)
250 free(ptr);
251}
252
253TEST_F(ScudoWrappersCTest, Memalign) {
254 void *P;
255 for (size_t I = FIRST_32_SECOND_64(2U, 3U); I <= 18U; I++) {
256 const size_t Alignment = 1U << I;
257
258 P = memalign(alignment: Alignment, size: Size);
259 EXPECT_NE(P, nullptr);
260 EXPECT_LE(Size, malloc_usable_size(ptr: P));
261 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
262 verifyAllocHookPtr(P);
263 verifyAllocHookSize(Size);
264 free(ptr: P);
265 verifyDeallocHookPtr(P);
266
267 P = nullptr;
268 EXPECT_EQ(posix_memalign(memptr: &P, alignment: Alignment, size: Size), 0);
269 EXPECT_NE(P, nullptr);
270 EXPECT_LE(Size, malloc_usable_size(ptr: P));
271 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
272 verifyAllocHookPtr(P);
273 verifyAllocHookSize(Size);
274 free(ptr: P);
275 verifyDeallocHookPtr(P);
276 }
277
278 EXPECT_EQ(memalign(alignment: 4096U, SIZE_MAX), nullptr);
279 EXPECT_EQ(posix_memalign(memptr: &P, alignment: 15U, size: Size), EINVAL);
280 EXPECT_EQ(posix_memalign(memptr: &P, alignment: 4096U, SIZE_MAX), ENOMEM);
281
282 // Android's memalign accepts non power-of-2 alignments, and 0.
283 if (SCUDO_ANDROID) {
284 for (size_t Alignment = 0U; Alignment <= 128U; Alignment++) {
285 P = memalign(alignment: Alignment, size: 1024U);
286 EXPECT_NE(P, nullptr);
287 verifyAllocHookPtr(P);
288 verifyAllocHookSize(Size);
289 free(ptr: P);
290 verifyDeallocHookPtr(P);
291 }
292 }
293}
294
295TEST_F(ScudoWrappersCTest, AlignedAlloc) {
296 const size_t Alignment = 4096U;
297 void *P = aligned_alloc(alignment: Alignment, size: Alignment * 4U);
298 EXPECT_NE(P, nullptr);
299 EXPECT_LE(Alignment * 4U, malloc_usable_size(ptr: P));
300 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
301 verifyAllocHookPtr(P);
302 verifyAllocHookSize(Alignment * 4U);
303 free(ptr: P);
304 verifyDeallocHookPtr(P);
305
306 errno = 0;
307 P = aligned_alloc(alignment: Alignment, size: Size);
308 EXPECT_EQ(P, nullptr);
309 EXPECT_EQ(errno, EINVAL);
310}
311
312TEST_F(ScudoWrappersCDeathTest, Realloc) {
313 invalidateHookPtrs();
314 // realloc(nullptr, N) is malloc(N)
315 void *P = realloc(ptr: nullptr, size: Size);
316 EXPECT_NE(P, nullptr);
317 verifyAllocHookPtr(P);
318 verifyAllocHookSize(Size);
319 free(ptr: P);
320 verifyDeallocHookPtr(P);
321
322 invalidateHookPtrs();
323 P = malloc(size: Size);
324 EXPECT_NE(P, nullptr);
325 // realloc(P, 0U) is free(P) and returns nullptr
326 EXPECT_EQ(realloc(ptr: P, size: 0U), nullptr);
327 verifyDeallocHookPtr(P);
328
329 P = malloc(size: Size);
330 EXPECT_NE(P, nullptr);
331 EXPECT_LE(Size, malloc_usable_size(ptr: P));
332 memset(s: P, c: 0x42, n: Size);
333
334 invalidateHookPtrs();
335 void *OldP = P;
336 P = realloc(ptr: P, size: Size * 2U);
337 EXPECT_NE(P, nullptr);
338 EXPECT_LE(Size * 2U, malloc_usable_size(ptr: P));
339 for (size_t I = 0; I < Size; I++)
340 EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
341 if (OldP == P) {
342 verifyDeallocHookPtr(OldP);
343 verifyAllocHookPtr(OldP);
344 } else {
345 verifyAllocHookPtr(P);
346 verifyAllocHookSize(Size * 2U);
347 verifyDeallocHookPtr(OldP);
348 }
349 verifyReallocHookPtrs(OldP, P, Size * 2U);
350
351 invalidateHookPtrs();
352 OldP = P;
353 P = realloc(ptr: P, size: Size / 2U);
354 EXPECT_NE(P, nullptr);
355 EXPECT_LE(Size / 2U, malloc_usable_size(ptr: P));
356 for (size_t I = 0; I < Size / 2U; I++)
357 EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
358 if (OldP == P) {
359 verifyDeallocHookPtr(OldP);
360 verifyAllocHookPtr(OldP);
361 } else {
362 verifyAllocHookPtr(P);
363 verifyAllocHookSize(Size / 2U);
364 }
365 verifyReallocHookPtrs(OldP, P, Size / 2U);
366 free(ptr: P);
367
368 EXPECT_DEATH(P = realloc(P, Size), "");
369
370 errno = 0;
371 EXPECT_EQ(realloc(ptr: nullptr, SIZE_MAX), nullptr);
372 EXPECT_EQ(errno, ENOMEM);
373 P = malloc(size: Size);
374 EXPECT_NE(P, nullptr);
375 errno = 0;
376 EXPECT_EQ(realloc(ptr: P, SIZE_MAX), nullptr);
377 EXPECT_EQ(errno, ENOMEM);
378 free(ptr: P);
379
380 // Android allows realloc of memalign pointers.
381 if (SCUDO_ANDROID) {
382 const size_t Alignment = 1024U;
383 P = memalign(alignment: Alignment, size: Size);
384 EXPECT_NE(P, nullptr);
385 EXPECT_LE(Size, malloc_usable_size(ptr: P));
386 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
387 memset(s: P, c: 0x42, n: Size);
388
389 P = realloc(ptr: P, size: Size * 2U);
390 EXPECT_NE(P, nullptr);
391 EXPECT_LE(Size * 2U, malloc_usable_size(ptr: P));
392 for (size_t I = 0; I < Size; I++)
393 EXPECT_EQ(0x42, (reinterpret_cast<uint8_t *>(P))[I]);
394 free(ptr: P);
395 }
396}
397
398#if !SCUDO_FUCHSIA
399TEST_F(ScudoWrappersCTest, MallOpt) {
400 errno = 0;
401 EXPECT_EQ(mallopt(param: -1000, val: 1), 0);
402 // mallopt doesn't set errno.
403 EXPECT_EQ(errno, 0);
404
405 EXPECT_EQ(mallopt(M_PURGE, val: 0), 1);
406
407 EXPECT_EQ(mallopt(M_DECAY_TIME, val: 1), 1);
408 EXPECT_EQ(mallopt(M_DECAY_TIME, val: 0), 1);
409 EXPECT_EQ(mallopt(M_DECAY_TIME, val: 1), 1);
410 EXPECT_EQ(mallopt(M_DECAY_TIME, val: 0), 1);
411
412 if (SCUDO_ANDROID) {
413 EXPECT_EQ(mallopt(M_CACHE_COUNT_MAX, val: 100), 1);
414 EXPECT_EQ(mallopt(M_CACHE_SIZE_MAX, val: 1024 * 1024 * 2), 1);
415 EXPECT_EQ(mallopt(M_TSDS_COUNT_MAX, val: 10), 1);
416 }
417}
418#endif
419
420TEST_F(ScudoWrappersCTest, OtherAlloc) {
421#if HAVE_PVALLOC
422 const size_t PageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
423
424 void *P = pvalloc(size: Size);
425 EXPECT_NE(P, nullptr);
426 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) & (PageSize - 1), 0U);
427 EXPECT_LE(PageSize, malloc_usable_size(ptr: P));
428 verifyAllocHookPtr(P);
429 // Size will be rounded up to PageSize.
430 verifyAllocHookSize(PageSize);
431 free(ptr: P);
432 verifyDeallocHookPtr(P);
433
434 EXPECT_EQ(pvalloc(SIZE_MAX), nullptr);
435
436 P = pvalloc(size: Size);
437 EXPECT_NE(P, nullptr);
438 EXPECT_EQ(reinterpret_cast<uintptr_t>(P) & (PageSize - 1), 0U);
439 free(ptr: P);
440#endif
441
442#if HAVE_VALLOC
443 EXPECT_EQ(valloc(SIZE_MAX), nullptr);
444#endif
445}
446
447template<typename FieldType>
448void MallInfoTest() {
449 // mallinfo is deprecated.
450#pragma clang diagnostic push
451#pragma clang diagnostic ignored "-Wdeprecated-declarations"
452 const FieldType BypassQuarantineSize = 1024U;
453 struct mallinfo MI = mallinfo();
454 FieldType Allocated = MI.uordblks;
455 void *P = malloc(BypassQuarantineSize);
456 EXPECT_NE(P, nullptr);
457 MI = mallinfo();
458 EXPECT_GE(MI.uordblks, Allocated + BypassQuarantineSize);
459 EXPECT_GT(MI.hblkhd, static_cast<FieldType>(0));
460 FieldType Free = MI.fordblks;
461 free(ptr: P);
462 MI = mallinfo();
463 EXPECT_GE(MI.fordblks, Free + BypassQuarantineSize);
464#pragma clang diagnostic pop
465}
466
467#if !SCUDO_FUCHSIA
468TEST_F(ScudoWrappersCTest, MallInfo) {
469#if SCUDO_ANDROID
470 // Android accidentally set the fields to size_t instead of int.
471 MallInfoTest<size_t>();
472#else
473 MallInfoTest<int>();
474#endif
475}
476#endif
477
478#if __GLIBC_PREREQ(2, 33) || SCUDO_ANDROID
479TEST_F(ScudoWrappersCTest, MallInfo2) {
480 const size_t BypassQuarantineSize = 1024U;
481 struct mallinfo2 MI = mallinfo2();
482 size_t Allocated = MI.uordblks;
483 void *P = malloc(size: BypassQuarantineSize);
484 EXPECT_NE(P, nullptr);
485 MI = mallinfo2();
486 EXPECT_GE(MI.uordblks, Allocated + BypassQuarantineSize);
487 EXPECT_GT(MI.hblkhd, 0U);
488 size_t Free = MI.fordblks;
489 free(ptr: P);
490 MI = mallinfo2();
491 EXPECT_GE(MI.fordblks, Free + BypassQuarantineSize);
492}
493#endif
494
495static uintptr_t BoundaryP;
496static size_t Count;
497
498static void callback(uintptr_t Base, UNUSED size_t Size, UNUSED void *Arg) {
499 if (scudo::archSupportsMemoryTagging()) {
500 Base = scudo::untagPointer(Ptr: Base);
501 BoundaryP = scudo::untagPointer(Ptr: BoundaryP);
502 }
503 if (Base == BoundaryP)
504 Count++;
505}
506
507// Verify that a block located on an iteration boundary is not mis-accounted.
508// To achieve this, we allocate a chunk for which the backing block will be
509// aligned on a page, then run the malloc_iterate on both the pages that the
510// block is a boundary for. It must only be seen once by the callback function.
511TEST_F(ScudoWrappersCTest, MallocIterateBoundary) {
512 const size_t PageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
513#if SCUDO_ANDROID
514 // Android uses a 16 byte alignment for both 32 bit and 64 bit.
515 const size_t BlockDelta = 16U;
516#else
517 const size_t BlockDelta = FIRST_32_SECOND_64(8U, 16U);
518#endif
519 const size_t SpecialSize = PageSize - BlockDelta;
520
521 // We aren't guaranteed that any size class is exactly a page wide. So we need
522 // to keep making allocations until we get an allocation that starts exactly
523 // on a page boundary. The BlockDelta value is expected to be the number of
524 // bytes to subtract from a returned pointer to get to the actual start of
525 // the pointer in the size class. In practice, this means BlockDelta should
526 // be set to the minimum alignment in bytes for the allocation.
527 //
528 // With a 16-byte block alignment and 4096-byte page size, each allocation has
529 // a probability of (1 - (16/4096)) of failing to meet the alignment
530 // requirements, and the probability of failing 65536 times is
531 // (1 - (16/4096))^65536 < 10^-112. So if we still haven't succeeded after
532 // 65536 tries, give up.
533 uintptr_t Block;
534 void *P = nullptr;
535 for (unsigned I = 0; I != 65536; ++I) {
536 void *PrevP = P;
537 P = malloc(size: SpecialSize);
538 EXPECT_NE(P, nullptr);
539 *reinterpret_cast<void **>(P) = PrevP;
540 BoundaryP = reinterpret_cast<uintptr_t>(P);
541 Block = BoundaryP - BlockDelta;
542 if ((Block & (PageSize - 1)) == 0U)
543 break;
544 }
545 EXPECT_EQ((Block & (PageSize - 1)), 0U);
546
547 Count = 0U;
548 malloc_disable();
549 malloc_iterate(base: Block - PageSize, size: PageSize, callback, arg: nullptr);
550 malloc_iterate(base: Block, size: PageSize, callback, arg: nullptr);
551 malloc_enable();
552 EXPECT_EQ(Count, 1U);
553
554 while (P) {
555 void *NextP = *reinterpret_cast<void **>(P);
556 free(ptr: P);
557 P = NextP;
558 }
559}
560
561// Fuchsia doesn't have alarm, fork or malloc_info.
562#if !SCUDO_FUCHSIA
563TEST_F(ScudoWrappersCDeathTest, MallocDisableDeadlock) {
564 // We expect heap operations within a disable/enable scope to deadlock.
565 EXPECT_DEATH(
566 {
567 void *P = malloc(Size);
568 EXPECT_NE(P, nullptr);
569 free(P);
570 malloc_disable();
571 alarm(1);
572 P = malloc(Size);
573 malloc_enable();
574 },
575 "");
576}
577
578TEST_F(ScudoWrappersCTest, MallocInfo) {
579 // Use volatile so that the allocations don't get optimized away.
580 void *volatile P1 = malloc(size: 1234);
581 void *volatile P2 = malloc(size: 4321);
582
583 char Buffer[16384];
584 FILE *F = fmemopen(s: Buffer, len: sizeof(Buffer), modes: "w+");
585 EXPECT_NE(F, nullptr);
586 errno = 0;
587 EXPECT_EQ(malloc_info(options: 0, fp: F), 0);
588 EXPECT_EQ(errno, 0);
589 fclose(stream: F);
590 EXPECT_EQ(strncmp(s1: Buffer, s2: "<malloc version=\"scudo-", n: 23), 0);
591 EXPECT_NE(nullptr, strstr(haystack: Buffer, needle: "<alloc size=\"1234\" count=\""));
592 EXPECT_NE(nullptr, strstr(haystack: Buffer, needle: "<alloc size=\"4321\" count=\""));
593
594 free(ptr: P1);
595 free(ptr: P2);
596}
597
598TEST_F(ScudoWrappersCDeathTest, Fork) {
599 void *P;
600 pid_t Pid = fork();
601 EXPECT_GE(Pid, 0) << strerror(errno);
602 if (Pid == 0) {
603 P = malloc(size: Size);
604 EXPECT_NE(P, nullptr);
605 memset(s: P, c: 0x42, n: Size);
606 free(ptr: P);
607 _exit(status: 0);
608 }
609 waitpid(Pid, nullptr, 0);
610 P = malloc(size: Size);
611 EXPECT_NE(P, nullptr);
612 memset(s: P, c: 0x42, n: Size);
613 free(ptr: P);
614
615 // fork should stall if the allocator has been disabled.
616 EXPECT_DEATH(
617 {
618 malloc_disable();
619 alarm(1);
620 Pid = fork();
621 EXPECT_GE(Pid, 0);
622 },
623 "");
624}
625
626static pthread_mutex_t Mutex;
627static pthread_cond_t Conditional = PTHREAD_COND_INITIALIZER;
628static bool Ready;
629
630static void *enableMalloc(UNUSED void *Unused) {
631 // Initialize the allocator for this thread.
632 void *P = malloc(size: Size);
633 EXPECT_NE(P, nullptr);
634 memset(s: P, c: 0x42, n: Size);
635 free(ptr: P);
636
637 // Signal the main thread we are ready.
638 pthread_mutex_lock(&Mutex);
639 Ready = true;
640 pthread_cond_signal(&Conditional);
641 pthread_mutex_unlock(&Mutex);
642
643 // Wait for the malloc_disable & fork, then enable the allocator again.
644 sleep(seconds: 1);
645 malloc_enable();
646
647 return nullptr;
648}
649
650TEST_F(ScudoWrappersCTest, DisableForkEnable) {
651 pthread_t ThreadId;
652 Ready = false;
653 EXPECT_EQ(pthread_create(&ThreadId, nullptr, &enableMalloc, nullptr), 0);
654
655 // Wait for the thread to be warmed up.
656 pthread_mutex_lock(&Mutex);
657 while (!Ready)
658 pthread_cond_wait(&Conditional, &Mutex);
659 pthread_mutex_unlock(&Mutex);
660
661 // Disable the allocator and fork. fork should succeed after malloc_enable.
662 malloc_disable();
663 pid_t Pid = fork();
664 EXPECT_GE(Pid, 0);
665 if (Pid == 0) {
666 void *P = malloc(size: Size);
667 EXPECT_NE(P, nullptr);
668 memset(s: P, c: 0x42, n: Size);
669 free(ptr: P);
670 _exit(status: 0);
671 }
672 waitpid(Pid, nullptr, 0);
673 EXPECT_EQ(pthread_join(ThreadId, 0), 0);
674}
675
676#endif // SCUDO_FUCHSIA
677

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