1 /*
2 * alloc-inject.c
3 * allocator fault injection for fuzzing harnesses
4 *
5 * SPDX-License-Identifier: pkgconf
6 *
7 * Copyright (c) 2026 pkgconf authors (see AUTHORS).
8 *
9 * Permission to use, copy, modify, and/or distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * This software is provided 'as is' and without any warranty, express or
14 * implied. In no event shall the authors be liable for any damages arising
15 * from the use of this software.
16 */
17
18 #include <stddef.h>
19 #include "alloc-inject.h"
20
21 /* the library's allocator calls are redirected here via -Wl,--wrap, so __real_*
22 * reaches the real (sanitizer-instrumented) allocator. injection is only active
23 * while armed, which a harness does exclusively around the code under test.
24 */
25 static bool alloc_armed = false;
26 static unsigned long alloc_seen = 0;
27 static unsigned long alloc_fail_at = 0;
28 static bool alloc_fired = false;
29
30 void *__real_malloc(size_t size);
31 void *__real_calloc(size_t nmemb, size_t size);
32 void *__real_realloc(void *ptr, size_t size);
33 void *__real_reallocarray(void *ptr, size_t nmemb, size_t size);
34 char *__real_strdup(const char *s);
35 char *__real_strndup(const char *s, size_t n);
36
37 void *__wrap_malloc(size_t size);
38 void *__wrap_calloc(size_t nmemb, size_t size);
39 void *__wrap_realloc(void *ptr, size_t size);
40 void *__wrap_reallocarray(void *ptr, size_t nmemb, size_t size);
41 char *__wrap_strdup(const char *s);
42 char *__wrap_strndup(const char *s, size_t n);
43
44 void
alloc_inject_arm(unsigned long fail_at)45 alloc_inject_arm(unsigned long fail_at)
46 {
47 alloc_seen = 0;
48 alloc_fail_at = fail_at;
49 alloc_fired = false;
50 alloc_armed = true;
51 }
52
53 void
alloc_inject_disarm(void)54 alloc_inject_disarm(void)
55 {
56 alloc_armed = false;
57 }
58
59 bool
alloc_inject_fired(void)60 alloc_inject_fired(void)
61 {
62 return alloc_fired;
63 }
64
65 static bool
alloc_should_fail(void)66 alloc_should_fail(void)
67 {
68 if (!alloc_armed)
69 return false;
70
71 if (++alloc_seen != alloc_fail_at)
72 return false;
73
74 alloc_fired = true;
75 return true;
76 }
77
78 void *
__wrap_malloc(size_t size)79 __wrap_malloc(size_t size)
80 {
81 if (alloc_should_fail())
82 return NULL;
83
84 return __real_malloc(size);
85 }
86
87 void *
__wrap_calloc(size_t nmemb,size_t size)88 __wrap_calloc(size_t nmemb, size_t size)
89 {
90 if (alloc_should_fail())
91 return NULL;
92
93 return __real_calloc(nmemb, size);
94 }
95
96 void *
__wrap_realloc(void * ptr,size_t size)97 __wrap_realloc(void *ptr, size_t size)
98 {
99 if (alloc_should_fail())
100 return NULL;
101
102 return __real_realloc(ptr, size);
103 }
104
105 void *
__wrap_reallocarray(void * ptr,size_t nmemb,size_t size)106 __wrap_reallocarray(void *ptr, size_t nmemb, size_t size)
107 {
108 if (alloc_should_fail())
109 return NULL;
110
111 return __real_reallocarray(ptr, nmemb, size);
112 }
113
114 char *
__wrap_strdup(const char * s)115 __wrap_strdup(const char *s)
116 {
117 if (alloc_should_fail())
118 return NULL;
119
120 return __real_strdup(s);
121 }
122
123 char *
__wrap_strndup(const char * s,size_t n)124 __wrap_strndup(const char *s, size_t n)
125 {
126 if (alloc_should_fail())
127 return NULL;
128
129 return __real_strndup(s, n);
130 }
131