xref: /freebsd/contrib/pkgconf/fuzzer/alloc-inject.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
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