suricata
nallocinc.c
Go to the documentation of this file.
1 /*
2  MIT License
3 
4  Copyright (c) 2025 Catena cyber
5 
6  Permission is hereby granted, free of charge, to any person obtaining a copy
7  of this software and associated documentation files (the "Software"), to deal
8  in the Software without restriction, including without limitation the rights
9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  copies of the Software, and to permit persons to whom the Software is
11  furnished to do so, subject to the following conditions:
12 
13  The above copyright notice and this permission notice shall be included in all
14  copies or substantial portions of the Software.
15 
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  SOFTWARE.
23 */
24 
25 /* Nalloc fuzz : framework to make allocations and IO fail while fuzzing */
26 
27 /* Environment variables to control nalloc fuzz behavior :
28  * NALLOC_VERBOSE: set it to log failed allocations with their stacktraces
29  * NALLOC_FREQ: set it to control how frequently allocations fail
30  * value 0 disables nalloc (no allocations fail)
31  * value 1..31 : allocations fail always (1) or very rarely (31 -> 1 / 2^31)
32  * value 32 : allocations fail at a random rate between 5 and 20 for each run
33  */
34 
35 #ifndef NALLOC_INC_C_
36 #define NALLOC_INC_C_
37 
38 // enable it on linux, and only if we do not have MSAN
39 #if defined(__linux__)
40 #define FUZZER_ENABLE_NALLOC 1
41 #if defined(__has_feature)
42 #if __has_feature(memory_sanitizer)
43 #undef FUZZER_ENABLE_NALLOC
44 #endif // memory_sanitizer
45 #endif // __has_feature
46 #endif // __linux__
47 
48 #if !defined(FUZZER_ENABLE_NALLOC)
49 #define nalloc_init(x)
50 #define nalloc_restrict_file_prefix(x)
51 #define nalloc_start(x, y)
52 #define nalloc_end()
53 #else // defined(FUZZER_ENABLE_NALLOC)
54 
55 #if defined(__clang__) && defined(__has_feature)
56 #if __has_feature(address_sanitizer)
57 #define NALLOC_ASAN 1
58 #endif
59 #endif
60 
61 #include <errno.h>
62 #include <stdbool.h>
63 #include <stdint.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 
68 #ifdef __cplusplus
69 extern "C"
70 {
71 #endif
72 
73  static const uint32_t nalloc_crc32_table[] = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
74  0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
75  0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
76  0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8,
77  0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
78  0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52,
79  0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
80  0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c,
81  0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
82  0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19,
83  0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078,
84  0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
85  0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
86  0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d,
87  0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
88  0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3,
89  0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
90  0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31,
91  0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59,
92  0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56,
93  0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
94  0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
95  0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d,
96  0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2,
97  0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
98  0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610,
99  0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
100  0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099,
101  0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
102  0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093,
103  0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082,
104  0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
105  0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
106  0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af,
107  0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be,
108  0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1,
109  0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 };
110 
111  // Nallocfuzz data to take a decision
112  uint32_t nalloc_random_state = 0;
113  __thread unsigned int nalloc_running = 0;
114  bool nalloc_initialized = false;
115  uint32_t nalloc_runs = 0;
116 
117  // Nalloc fuzz parameters
118  uint32_t nalloc_bitmask = 0xFF;
119  bool nalloc_random_bitmask = true;
120  uint32_t nalloc_magic = 0x294cee63;
121  bool nalloc_verbose = false;
122  const char *nalloc_prefix = "";
123  uint32_t nalloc_prefix_len = 0;
124  char nalloc_prefix_dir[PATH_MAX];
125 
126 #ifdef NALLOC_ASAN
127  extern void __sanitizer_print_stack_trace(void);
128  extern void __sanitizer_symbolize_pc(
129  void *pc, const char *fmt, char *out_buf, size_t out_buf_size);
130 #endif
131 
132 #define NALLOC_PREFIX_MAX 1024
133 
134  __attribute__((noinline)) static void nalloc_restrict_file_prefix(uint8_t levels_up)
135  {
136 #ifdef NALLOC_ASAN
137  void *pc = __builtin_return_address(0);
138  __sanitizer_symbolize_pc(pc, "%s", nalloc_prefix_dir, sizeof(nalloc_prefix_dir) - 1);
139  uint16_t slashes[levels_up];
140  uint8_t slash_off = 0;
141  // /src/suricata/src/test/fuzz/file.c -3 levels -> /src/suricata/src/
142  for (uint16_t i = 0; i < PATH_MAX; i++) {
143  if (nalloc_prefix_dir[i] == 0) {
144  uint32_t cut = slashes[slash_off];
145  nalloc_prefix_dir[cut] = 0;
146  nalloc_prefix = nalloc_prefix_dir;
147  nalloc_prefix_len = cut;
148  return;
149  }
150  if (nalloc_prefix_dir[i] == '/') {
151  slashes[slash_off] = i;
152  slash_off = (slash_off + 1) % levels_up;
153  }
154  }
155 #endif
156  }
157 
158  // Generic init, using env variables to get parameters
159  static void nalloc_init(const char *prog)
160  {
161  if (nalloc_initialized) {
162  return;
163  }
164  nalloc_initialized = true;
165  char *bitmask = getenv("NALLOC_FREQ");
166  if (bitmask) {
167  long shift = strtol(bitmask, NULL, 10);
168  if (shift > 0 && shift < 31) {
169  nalloc_bitmask = 1 << shift;
170  nalloc_random_bitmask = false;
171  } else if (shift == 0) {
172  nalloc_random_bitmask = false;
173  nalloc_bitmask = 0;
174  }
175  } else if (prog == NULL || strstr(prog, "nalloc") == NULL) {
176  nalloc_random_bitmask = false;
177  nalloc_bitmask = 0;
178  return;
179  }
180 
181  char *verbose = getenv("NALLOC_VERBOSE");
182  if (verbose) {
183  nalloc_verbose = true;
184  }
185  char *rfp = getenv("NALLOC_RESTRICT_FILE_PREFIX");
186  if (rfp) {
187  nalloc_restrict_file_prefix((uint8_t)strtol(rfp, NULL, 10));
188  }
189  }
190 
191  // add one byte to the CRC
192  static inline void nalloc_random_update(uint8_t b)
193  {
194  nalloc_random_state = ((uint32_t)((uint32_t)nalloc_random_state << 8)) ^
195  nalloc_crc32_table[((nalloc_random_state >> 24) ^ b) & 0xFF];
196  }
197 
198  // Start the failure injections, using a buffer as seed
199  static int nalloc_start(const uint8_t *data, size_t size)
200  {
201  if (nalloc_random_bitmask) {
202  if (nalloc_random_state & 0x10) {
203  nalloc_bitmask = 0xFFFFFFFF;
204  } else {
205  nalloc_bitmask = 1 << (5 + (nalloc_random_state & 0xF));
206  }
207  } else if (nalloc_bitmask == 0) {
208  // nalloc disabled
209  return 0;
210  }
211  nalloc_random_state = 0;
212  for (size_t i = 0; i < size; i++) {
213  nalloc_random_update(data[i]);
214  }
215  if (__sync_fetch_and_add(&nalloc_running, 1)) {
216  __sync_fetch_and_sub(&nalloc_running, 1);
217  return 0;
218  }
219  nalloc_runs++;
220  return 1;
221  }
222 
223 #define nalloc_return_address() __builtin_return_address(1)
224 
225  // Stop the failure injections
226  static void nalloc_end(void)
227  {
228  __sync_fetch_and_sub(&nalloc_running, 1);
229  }
230 
231  __attribute__((noinline)) static bool nalloc_backtrace_exclude(
232  size_t size, const char *op, void *pc)
233  {
234 #ifdef NALLOC_ASAN
235  if (nalloc_prefix_len > 0) {
236  char data[NALLOC_PREFIX_MAX];
237  __sanitizer_symbolize_pc(pc, "%s", data, sizeof(data) - 1);
238  for (size_t i = 0; i < nalloc_prefix_len; i++) {
239  if (data[i] != nalloc_prefix[i]) {
240  return true;
241  }
242  }
243  }
244 #endif
245  if (nalloc_verbose) {
246  fprintf(stderr, "failed %s(%zu) \n", op, size);
247 #ifdef NALLOC_ASAN
248  __sanitizer_print_stack_trace();
249 #endif
250  }
251 
252  return false;
253  }
254 
255  //
256  static bool nalloc_fail(size_t size, const char *op)
257  {
258  // do not fail before thread init
259  if (nalloc_runs == 0) {
260  return false;
261  }
262  if (__sync_fetch_and_add(&nalloc_running, 1) != 1) {
263  // do not fail allocations outside of fuzzer input
264  // and do not fail inside of this function
265  __sync_fetch_and_sub(&nalloc_running, 1);
266  return false;
267  }
268  nalloc_random_update((uint8_t)size);
269  if (size >= 0x100) {
270  nalloc_random_update((uint8_t)(size >> 8));
271  if (size >= 0x10000) {
272  nalloc_random_update((uint8_t)(size >> 16));
273  // bigger may already fail or oom
274  }
275  }
276  if (((nalloc_random_state ^ nalloc_magic) & nalloc_bitmask) == 0) {
277  void *pc = NULL;
278 #ifdef NALLOC_ASAN
279 #pragma clang diagnostic push
280 #pragma clang diagnostic ignored "-Wframe-address"
281  pc = nalloc_return_address();
282 #pragma clang diagnostic pop
283 #endif
284  if (nalloc_backtrace_exclude(size, op, pc)) {
285  __sync_fetch_and_sub(&nalloc_running, 1);
286  return false;
287  }
288  __sync_fetch_and_sub(&nalloc_running, 1);
289  return true;
290  }
291  __sync_fetch_and_sub(&nalloc_running, 1);
292  return false;
293  }
294 
295 // ASAN interceptor for libc routines
296 #ifdef NALLOC_ASAN
297  extern void *__interceptor_malloc(size_t);
298  extern void *__interceptor_calloc(size_t, size_t);
299  extern void *__interceptor_realloc(void *, size_t);
300 
301  extern ssize_t __interceptor_read(int, void *, size_t);
302  extern ssize_t __interceptor_write(int, const void *, size_t);
303  extern ssize_t __interceptor_recv(int, void *, size_t, int);
304  extern ssize_t __interceptor_send(int, const void *, size_t, int);
305 
306 #define nalloc_malloc(s) __interceptor_malloc(s)
307 #define nalloc_calloc(s, n) __interceptor_calloc(s, n)
308 #define nalloc_realloc(p, s) __interceptor_realloc(p, s)
309 
310 #define nalloc_read(f, b, s) __interceptor_read(f, b, s)
311 #define nalloc_write(f, b, s) __interceptor_write(f, b, s)
312 #define nalloc_recv(f, b, s, x) __interceptor_recv(f, b, s, x)
313 #define nalloc_send(f, b, s, x) __interceptor_send(f, b, s, x)
314 
315 #else
316 extern void *__libc_malloc(size_t);
317 extern void *__libc_calloc(size_t, size_t);
318 extern void *__libc_realloc(void *, size_t);
319 
320 extern ssize_t __read(int, void *, size_t);
321 extern ssize_t __write(int, const void *, size_t);
322 extern ssize_t __recv(int, void *, size_t, int);
323 extern ssize_t __send(int, const void *, size_t, int);
324 
325 #define nalloc_malloc(s) __libc_malloc(s)
326 #define nalloc_calloc(s, n) __libc_calloc(s, n)
327 #define nalloc_realloc(p, s) __libc_realloc(p, s)
328 
329 #define nalloc_read(f, b, s) __read(f, b, s)
330 #define nalloc_write(f, b, s) __write(f, b, s)
331 #define nalloc_recv(f, b, s, x) __recv(f, b, s, x)
332 #define nalloc_send(f, b, s, x) __send(f, b, s, x)
333 #endif
334 
335  // nalloc standard function overwrites with pseudo-random failures
336  ssize_t read(int fd, void *buf, size_t count)
337  {
338  if (nalloc_fail(count, "read")) {
339  errno = EIO;
340  return -1;
341  }
342  return nalloc_read(fd, buf, count);
343  }
344 
345  ssize_t write(int fd, const void *buf, size_t count)
346  {
347  if (nalloc_fail(count, "write")) {
348  errno = EIO;
349  return -1;
350  }
351  return nalloc_write(fd, buf, count);
352  }
353 
354  ssize_t recv(int fd, void *buf, size_t count, int flags)
355  {
356  if (nalloc_fail(count, "recv")) {
357  errno = EIO;
358  return -1;
359  }
360  return nalloc_recv(fd, buf, count, flags);
361  }
362 
363  ssize_t send(int fd, const void *buf, size_t count, int flags)
364  {
365  if (nalloc_fail(count, "send")) {
366  errno = EIO;
367  return -1;
368  }
369  return nalloc_send(fd, buf, count, flags);
370  }
371 
372  void *calloc(size_t nmemb, size_t size)
373  {
374  if (nalloc_fail(size, "calloc")) {
375  errno = ENOMEM;
376  return NULL;
377  }
378  return nalloc_calloc(nmemb, size);
379  }
380 
381  void *malloc(size_t size)
382  {
383  if (nalloc_fail(size, "malloc")) {
384  errno = ENOMEM;
385  return NULL;
386  }
387  return nalloc_malloc(size);
388  }
389 
390  void *realloc(void *ptr, size_t size)
391  {
392  if (nalloc_fail(size, "realloc")) {
393  errno = ENOMEM;
394  return NULL;
395  }
396  return nalloc_realloc(ptr, size);
397  }
398 
399 #ifdef __cplusplus
400 } // extern "C" {
401 #endif
402 
403 #endif // defined(FUZZER_ENABLE_NALLOC)
404 
405 #endif // NALLOC_INC_C_
__attribute__
typedef __attribute__
DNP3 application header.
nalloc_init
#define nalloc_init(x)
Definition: nallocinc.c:49
flags
uint8_t flags
Definition: decode-gre.h:0
nalloc_start
#define nalloc_start(x, y)
Definition: nallocinc.c:51
nalloc_end
#define nalloc_end()
Definition: nallocinc.c:52
nalloc_restrict_file_prefix
#define nalloc_restrict_file_prefix(x)
Definition: nallocinc.c:50