| /* |
| american fuzzy lop++ - error-checking, memory-zeroing alloc routines |
| -------------------------------------------------------------------- |
| |
| Originally written by Michal Zalewski |
| |
| Now maintained by Marc Heuse <mh@mh-sec.de>, |
| Heiko Eißfeldt <heiko.eissfeldt@hexco.de>, |
| Andrea Fioraldi <andreafioraldi@gmail.com>, |
| Dominik Maier <mail@dmnk.co> |
| |
| Copyright 2016, 2017 Google Inc. All rights reserved. |
| Copyright 2019-2024 AFLplusplus Project. All rights reserved. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at: |
| |
| https://www.apache.org/licenses/LICENSE-2.0 |
| |
| This allocator is not designed to resist malicious attackers (the canaries |
| are small and predictable), but provides a robust and portable way to detect |
| use-after-free, off-by-one writes, stale pointers, and so on. |
| |
| */ |
| |
| #ifndef _HAVE_ALLOC_INL_H |
| #define _HAVE_ALLOC_INL_H |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stddef.h> |
| |
| #include "config.h" |
| #include "types.h" |
| #include "debug.h" |
| |
| /* Initial size used for afl_realloc */ |
| #define INITIAL_GROWTH_SIZE (64) |
| |
| // Be careful! _WANT_ORIGINAL_AFL_ALLOC is not compatible with custom mutators |
| |
| #ifndef _WANT_ORIGINAL_AFL_ALLOC |
| // AFL++ stuff without memory corruption checks - for speed |
| |
| /* User-facing macro to sprintf() to a dynamically allocated buffer. */ |
| |
| #define alloc_printf(_str...) \ |
| ({ \ |
| \ |
| u8 *_tmp; \ |
| s32 _len = snprintf(NULL, 0, _str); \ |
| if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \ |
| _tmp = ck_alloc(_len + 1); \ |
| snprintf((char *)_tmp, _len + 1, _str); \ |
| _tmp; \ |
| \ |
| }) |
| |
| /* Macro to enforce allocation limits as a last-resort defense against |
| integer overflows. */ |
| |
| #define ALLOC_CHECK_SIZE(_s) \ |
| do { \ |
| \ |
| if ((_s) > MAX_ALLOC) ABORT("Bad alloc request: %u bytes", (_s)); \ |
| \ |
| } while (0) |
| |
| /* Macro to check malloc() failures and the like. */ |
| |
| #define ALLOC_CHECK_RESULT(_r, _s) \ |
| do { \ |
| \ |
| if (!(_r)) ABORT("Out of memory: can't allocate %u bytes", (_s)); \ |
| \ |
| } while (0) |
| |
| /* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized |
| requests. */ |
| |
| static inline void *DFL_ck_alloc_nozero(u32 size) { |
| |
| void *ret; |
| |
| if (!size) { return NULL; } |
| |
| ALLOC_CHECK_SIZE(size); |
| ret = malloc(size); |
| ALLOC_CHECK_RESULT(ret, size); |
| |
| return (void *)ret; |
| |
| } |
| |
| /* Allocate a buffer, returning zeroed memory. |
| Returns null for 0 size */ |
| |
| static inline void *DFL_ck_alloc(u32 size) { |
| |
| void *mem; |
| |
| if (!size) { return NULL; } |
| mem = DFL_ck_alloc_nozero(size); |
| |
| return memset(mem, 0, size); |
| |
| } |
| |
| /* Free memory, checking for double free and corrupted heap. When DEBUG_BUILD |
| is set, the old memory will be also clobbered with 0xFF. */ |
| |
| static inline void DFL_ck_free(void *mem) { |
| |
| if (!mem) { return; } |
| |
| free(mem); |
| |
| } |
| |
| /* Re-allocate a buffer, checking for issues and zeroing any newly-added tail. |
| With DEBUG_BUILD, the buffer is always reallocated to a new addresses and the |
| old memory is clobbered with 0xFF. */ |
| |
| static inline void *DFL_ck_realloc(void *orig, u32 size) { |
| |
| void *ret; |
| |
| if (!size) { |
| |
| DFL_ck_free(orig); |
| return NULL; |
| |
| } |
| |
| ALLOC_CHECK_SIZE(size); |
| |
| /* Catch pointer issues sooner: force relocation and make sure that the |
| original buffer is wiped. */ |
| |
| ret = realloc(orig, size); |
| |
| ALLOC_CHECK_RESULT(ret, size); |
| |
| return (void *)ret; |
| |
| } |
| |
| /* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */ |
| |
| static inline u8 *DFL_ck_strdup(u8 *str) { |
| |
| u8 *ret; |
| u32 size; |
| |
| if (!str) { return NULL; } |
| |
| size = strlen((char *)str) + 1; |
| |
| ALLOC_CHECK_SIZE(size); |
| ret = (u8 *)malloc(size); |
| ALLOC_CHECK_RESULT(ret, size); |
| |
| return (u8 *)memcpy(ret, str, size); |
| |
| } |
| |
| /* In non-debug mode, we just do straightforward aliasing of the above |
| functions to user-visible names such as ck_alloc(). */ |
| |
| #define ck_alloc DFL_ck_alloc |
| #define ck_alloc_nozero DFL_ck_alloc_nozero |
| #define ck_realloc DFL_ck_realloc |
| #define ck_strdup DFL_ck_strdup |
| #define ck_free DFL_ck_free |
| |
| #define alloc_report() |
| |
| #else |
| // This is the original alloc-inl of stock afl |
| |
| /* User-facing macro to sprintf() to a dynamically allocated buffer. */ |
| |
| #define alloc_printf(_str...) \ |
| ({ \ |
| \ |
| u8 *_tmp; \ |
| s32 _len = snprintf(NULL, 0, _str); \ |
| if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \ |
| _tmp = ck_alloc(_len + 1); \ |
| snprintf((char *)_tmp, _len + 1, _str); \ |
| _tmp; \ |
| \ |
| }) |
| |
| /* Macro to enforce allocation limits as a last-resort defense against |
| integer overflows. */ |
| #define ALLOC_CHECK_SIZE(_s) \ |
| do { \ |
| \ |
| if ((_s) > MAX_ALLOC) ABORT("Bad alloc request: %u bytes", (_s)); \ |
| \ |
| } while (0) |
| |
| /* Macro to check malloc() failures and the like. */ |
| |
| #define ALLOC_CHECK_RESULT(_r, _s) \ |
| do { \ |
| \ |
| if (!(_r)) ABORT("Out of memory: can't allocate %u bytes", (_s)); \ |
| \ |
| } while (0) |
| |
| /* Magic tokens used to mark used / freed chunks. */ |
| |
| #define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */ |
| #define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */ |
| #define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */ |
| |
| /* Positions of guard tokens in relation to the user-visible pointer. */ |
| |
| #define ALLOC_C1(_ptr) (((u32 *)(_ptr))[-2]) |
| #define ALLOC_S(_ptr) (((u32 *)(_ptr))[-1]) |
| #define ALLOC_C2(_ptr) (((u8 *)(_ptr))[ALLOC_S(_ptr)]) |
| |
| #define ALLOC_OFF_HEAD 8 |
| #define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1) |
| |
| /* Sanity-checking macros for pointers. */ |
| |
| #define CHECK_PTR(_p) \ |
| do { \ |
| \ |
| if (_p) { \ |
| \ |
| if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) { \ |
| \ |
| if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \ |
| ABORT("Use after free."); \ |
| else \ |
| ABORT("Corrupted head alloc canary."); \ |
| \ |
| } \ |
| if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \ |
| ABORT("Corrupted tail alloc canary."); \ |
| \ |
| } \ |
| \ |
| } while (0) |
| |
| #define CHECK_PTR_EXPR(_p) \ |
| ({ \ |
| \ |
| typeof(_p) _tmp = (_p); \ |
| CHECK_PTR(_tmp); \ |
| _tmp; \ |
| \ |
| }) |
| |
| /* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized |
| requests. */ |
| |
| static inline void *DFL_ck_alloc_nozero(u32 size) { |
| |
| void *ret; |
| |
| if (!size) return NULL; |
| |
| ALLOC_CHECK_SIZE(size); |
| ret = malloc(size + ALLOC_OFF_TOTAL); |
| ALLOC_CHECK_RESULT(ret, size); |
| |
| ret = (char *)ret + ALLOC_OFF_HEAD; |
| |
| ALLOC_C1(ret) = ALLOC_MAGIC_C1; |
| ALLOC_S(ret) = size; |
| ALLOC_C2(ret) = ALLOC_MAGIC_C2; |
| |
| return ret; |
| |
| } |
| |
| /* Allocate a buffer, returning zeroed memory. */ |
| |
| static inline void *DFL_ck_alloc(u32 size) { |
| |
| void *mem; |
| |
| if (!size) return NULL; |
| mem = DFL_ck_alloc_nozero(size); |
| |
| return memset(mem, 0, size); |
| |
| } |
| |
| /* Free memory, checking for double free and corrupted heap. When DEBUG_BUILD |
| is set, the old memory will be also clobbered with 0xFF. */ |
| |
| static inline void DFL_ck_free(void *mem) { |
| |
| if (!mem) return; |
| |
| CHECK_PTR(mem); |
| #ifdef DEBUG_BUILD |
| |
| /* Catch pointer issues sooner. */ |
| memset(mem, 0xFF, ALLOC_S(mem)); |
| |
| #endif /* DEBUG_BUILD */ |
| |
| ALLOC_C1(mem) = ALLOC_MAGIC_F; |
| |
| free((char *)mem - ALLOC_OFF_HEAD); |
| |
| } |
| |
| /* Re-allocate a buffer, checking for issues and zeroing any newly-added tail. |
| With DEBUG_BUILD, the buffer is always reallocated to a new addresses and the |
| old memory is clobbered with 0xFF. */ |
| |
| static inline void *DFL_ck_realloc(void *orig, u32 size) { |
| |
| void *ret; |
| u32 old_size = 0; |
| |
| if (!size) { |
| |
| DFL_ck_free(orig); |
| return NULL; |
| |
| } |
| |
| if (orig) { |
| |
| CHECK_PTR(orig); |
| |
| #ifndef DEBUG_BUILD |
| ALLOC_C1(orig) = ALLOC_MAGIC_F; |
| #endif /* !DEBUG_BUILD */ |
| |
| old_size = ALLOC_S(orig); |
| orig = (char *)orig - ALLOC_OFF_HEAD; |
| |
| ALLOC_CHECK_SIZE(old_size); |
| |
| } |
| |
| ALLOC_CHECK_SIZE(size); |
| |
| #ifndef DEBUG_BUILD |
| |
| ret = realloc(orig, size + ALLOC_OFF_TOTAL); |
| ALLOC_CHECK_RESULT(ret, size); |
| |
| #else |
| |
| /* Catch pointer issues sooner: force relocation and make sure that the |
| original buffer is wiped. */ |
| |
| ret = malloc(size + ALLOC_OFF_TOTAL); |
| ALLOC_CHECK_RESULT(ret, size); |
| |
| if (orig) { |
| |
| memcpy((char *)ret + ALLOC_OFF_HEAD, (char *)orig + ALLOC_OFF_HEAD, |
| MIN(size, old_size)); |
| memset((char *)orig + ALLOC_OFF_HEAD, 0xFF, old_size); |
| |
| ALLOC_C1((char *)orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; |
| |
| free(orig); |
| |
| } |
| |
| #endif /* ^!DEBUG_BUILD */ |
| |
| ret = (char *)ret + ALLOC_OFF_HEAD; |
| |
| ALLOC_C1(ret) = ALLOC_MAGIC_C1; |
| ALLOC_S(ret) = size; |
| ALLOC_C2(ret) = ALLOC_MAGIC_C2; |
| |
| if (size > old_size) memset((char *)ret + old_size, 0, size - old_size); |
| |
| return ret; |
| |
| } |
| |
| /* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */ |
| |
| static inline u8 *DFL_ck_strdup(u8 *str) { |
| |
| void *ret; |
| u32 size; |
| |
| if (!str) return NULL; |
| |
| size = strlen((char *)str) + 1; |
| |
| ALLOC_CHECK_SIZE(size); |
| ret = malloc(size + ALLOC_OFF_TOTAL); |
| ALLOC_CHECK_RESULT(ret, size); |
| |
| ret = (char *)ret + ALLOC_OFF_HEAD; |
| |
| ALLOC_C1(ret) = ALLOC_MAGIC_C1; |
| ALLOC_S(ret) = size; |
| ALLOC_C2(ret) = ALLOC_MAGIC_C2; |
| |
| return memcpy(ret, str, size); |
| |
| } |
| |
| #ifndef DEBUG_BUILD |
| |
| /* In non-debug mode, we just do straightforward aliasing of the above |
| functions to user-visible names such as ck_alloc(). */ |
| |
| #define ck_alloc DFL_ck_alloc |
| #define ck_alloc_nozero DFL_ck_alloc_nozero |
| #define ck_realloc DFL_ck_realloc |
| #define ck_strdup DFL_ck_strdup |
| #define ck_free DFL_ck_free |
| |
| #define alloc_report() |
| |
| #else |
| |
| /* In debugging mode, we also track allocations to detect memory leaks, and |
| the flow goes through one more layer of indirection. */ |
| |
| /* Alloc tracking data structures: */ |
| |
| #define ALLOC_BUCKETS 4096 |
| |
| struct TRK_obj { |
| |
| void *ptr; |
| char *file, *func; |
| u32 line; |
| |
| }; |
| |
| #ifdef AFL_MAIN |
| |
| struct TRK_obj *TRK[ALLOC_BUCKETS]; |
| u32 TRK_cnt[ALLOC_BUCKETS]; |
| |
| #define alloc_report() TRK_report() |
| |
| #else |
| |
| extern struct TRK_obj *TRK[ALLOC_BUCKETS]; |
| extern u32 TRK_cnt[ALLOC_BUCKETS]; |
| |
| #define alloc_report() |
| |
| #endif /* ^AFL_MAIN */ |
| |
| /* Bucket-assigning function for a given pointer: */ |
| |
| #define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS) |
| |
| /* Add a new entry to the list of allocated objects. */ |
| |
| static inline void TRK_alloc_buf(void *ptr, const char *file, const char *func, |
| u32 line) { |
| |
| u32 i, bucket; |
| |
| if (!ptr) return; |
| |
| bucket = TRKH(ptr); |
| |
| /* Find a free slot in the list of entries for that bucket. */ |
| |
| for (i = 0; i < TRK_cnt[bucket]; i++) |
| |
| if (!TRK[bucket][i].ptr) { |
| |
| TRK[bucket][i].ptr = ptr; |
| TRK[bucket][i].file = (char *)file; |
| TRK[bucket][i].func = (char *)func; |
| TRK[bucket][i].line = line; |
| return; |
| |
| } |
| |
| /* No space available - allocate more. */ |
| |
| TRK[bucket] = DFL_ck_realloc(TRK[bucket], |
| (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); |
| |
| TRK[bucket][i].ptr = ptr; |
| TRK[bucket][i].file = (char *)file; |
| TRK[bucket][i].func = (char *)func; |
| TRK[bucket][i].line = line; |
| |
| TRK_cnt[bucket]++; |
| |
| } |
| |
| /* Remove entry from the list of allocated objects. */ |
| |
| static inline void TRK_free_buf(void *ptr, const char *file, const char *func, |
| u32 line) { |
| |
| u32 i, bucket; |
| |
| if (!ptr) return; |
| |
| bucket = TRKH(ptr); |
| |
| /* Find the element on the list... */ |
| |
| for (i = 0; i < TRK_cnt[bucket]; i++) |
| |
| if (TRK[bucket][i].ptr == ptr) { |
| |
| TRK[bucket][i].ptr = 0; |
| return; |
| |
| } |
| |
| WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)", func, file, |
| line); |
| |
| } |
| |
| /* Do a final report on all non-deallocated objects. */ |
| |
| static inline void TRK_report(void) { |
| |
| u32 i, bucket; |
| |
| fflush(0); |
| |
| for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++) |
| for (i = 0; i < TRK_cnt[bucket]; i++) |
| if (TRK[bucket][i].ptr) |
| WARNF("ALLOC: Memory never freed, created in %s (%s:%u)", |
| TRK[bucket][i].func, TRK[bucket][i].file, TRK[bucket][i].line); |
| |
| } |
| |
| /* Simple wrappers for non-debugging functions: */ |
| |
| static inline void *TRK_ck_alloc(u32 size, const char *file, const char *func, |
| u32 line) { |
| |
| void *ret = DFL_ck_alloc(size); |
| TRK_alloc_buf(ret, file, func, line); |
| return ret; |
| |
| } |
| |
| static inline void *TRK_ck_realloc(void *orig, u32 size, const char *file, |
| const char *func, u32 line) { |
| |
| void *ret = DFL_ck_realloc(orig, size); |
| TRK_free_buf(orig, file, func, line); |
| TRK_alloc_buf(ret, file, func, line); |
| return ret; |
| |
| } |
| |
| static inline void *TRK_ck_strdup(u8 *str, const char *file, const char *func, |
| u32 line) { |
| |
| void *ret = DFL_ck_strdup(str); |
| TRK_alloc_buf(ret, file, func, line); |
| return ret; |
| |
| } |
| |
| static inline void TRK_ck_free(void *ptr, const char *file, const char *func, |
| u32 line) { |
| |
| TRK_free_buf(ptr, file, func, line); |
| DFL_ck_free(ptr); |
| |
| } |
| |
| /* Aliasing user-facing names to tracking functions: */ |
| |
| #define ck_alloc(_p1) TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__) |
| |
| #define ck_alloc_nozero(_p1) \ |
| TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__) |
| |
| #define ck_realloc(_p1, _p2) \ |
| TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) |
| |
| #define ck_strdup(_p1) TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__) |
| |
| #define ck_free(_p1) TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__) |
| |
| #endif /* ^!DEBUG_BUILD */ |
| |
| #endif /* _WANT_ORIGINAL_AFL_ALLOC */ |
| |
| /* This function calculates the next power of 2 greater or equal its argument. |
| @return The rounded up power of 2 (if no overflow) or 0 on overflow. |
| */ |
| static inline size_t next_pow2(size_t in) { |
| |
| // Commented this out as this behavior doesn't change, according to unittests |
| // if (in == 0 || in > (size_t)-1) { |
| |
| // |
| // return 0; /* avoid undefined behaviour under-/overflow |
| // */ |
| // |
| // } |
| |
| size_t out = in - 1; |
| out |= out >> 1; |
| out |= out >> 2; |
| out |= out >> 4; |
| out |= out >> 8; |
| out |= out >> 16; |
| return out + 1; |
| |
| } |
| |
| /* AFL alloc buffer, the struct is here so we don't need to do fancy ptr |
| * arithmetics */ |
| struct afl_alloc_buf { |
| |
| /* The complete allocated size, including the header of len |
| * AFL_ALLOC_SIZE_OFFSET */ |
| size_t complete_size; |
| /* ptr to the first element of the actual buffer */ |
| u8 buf[0]; |
| |
| }; |
| |
| #define AFL_ALLOC_SIZE_OFFSET (offsetof(struct afl_alloc_buf, buf)) |
| |
| /* Returns the container element to this ptr */ |
| static inline struct afl_alloc_buf *afl_alloc_bufptr(void *buf) { |
| |
| return (struct afl_alloc_buf *)((u8 *)buf - AFL_ALLOC_SIZE_OFFSET); |
| |
| } |
| |
| /* Gets the maximum size of the buf contents (ptr->complete_size - |
| * AFL_ALLOC_SIZE_OFFSET) */ |
| static inline size_t afl_alloc_bufsize(void *buf) { |
| |
| return afl_alloc_bufptr(buf)->complete_size - AFL_ALLOC_SIZE_OFFSET; |
| |
| } |
| |
| /* This function makes sure *size is > size_needed after call. |
| It will realloc *buf otherwise. |
| *size will grow exponentially as per: |
| https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ |
| Will return NULL and free *buf if size_needed is <1 or realloc failed. |
| @return For convenience, this function returns *buf. |
| */ |
| static inline void *afl_realloc(void **buf, size_t size_needed) { |
| |
| struct afl_alloc_buf *new_buf = NULL; |
| |
| size_t current_size = 0; |
| size_t next_size = 0; |
| |
| if (likely(*buf)) { |
| |
| /* the size is always stored at buf - 1*size_t */ |
| new_buf = (struct afl_alloc_buf *)afl_alloc_bufptr(*buf); |
| current_size = new_buf->complete_size; |
| |
| } |
| |
| size_needed += AFL_ALLOC_SIZE_OFFSET; |
| |
| /* No need to realloc */ |
| if (likely(current_size >= size_needed)) { return *buf; } |
| |
| /* No initial size was set */ |
| if (size_needed < INITIAL_GROWTH_SIZE) { |
| |
| next_size = INITIAL_GROWTH_SIZE; |
| |
| } else { |
| |
| /* grow exponentially */ |
| next_size = next_pow2(size_needed); |
| |
| /* handle overflow: fall back to the original size_needed */ |
| if (unlikely(!next_size)) { next_size = size_needed; } |
| |
| } |
| |
| /* alloc */ |
| struct afl_alloc_buf *newer_buf = |
| (struct afl_alloc_buf *)realloc(new_buf, next_size); |
| if (unlikely(!newer_buf)) { |
| |
| free(new_buf); // avoid a leak |
| *buf = NULL; |
| return NULL; |
| |
| } |
| |
| new_buf = newer_buf; |
| memset(((u8 *)new_buf) + current_size, 0, next_size - current_size); |
| |
| new_buf->complete_size = next_size; |
| *buf = (void *)(new_buf->buf); |
| return *buf; |
| |
| } |
| |
| /* afl_realloc_exact uses afl alloc buffers but sets it to a specific size */ |
| |
| static inline void *afl_realloc_exact(void **buf, size_t size_needed) { |
| |
| struct afl_alloc_buf *new_buf = NULL; |
| |
| size_t current_size = 0; |
| |
| if (likely(*buf)) { |
| |
| /* the size is always stored at buf - 1*size_t */ |
| new_buf = (struct afl_alloc_buf *)afl_alloc_bufptr(*buf); |
| current_size = new_buf->complete_size; |
| |
| } |
| |
| size_needed += AFL_ALLOC_SIZE_OFFSET; |
| |
| /* No need to realloc */ |
| if (unlikely(current_size == size_needed)) { return *buf; } |
| |
| /* alloc */ |
| struct afl_alloc_buf *newer_buf = |
| (struct afl_alloc_buf *)realloc(new_buf, size_needed); |
| if (unlikely(!newer_buf)) { |
| |
| free(new_buf); // avoid a leak |
| *buf = NULL; |
| return NULL; |
| |
| } else { |
| |
| new_buf = newer_buf; |
| |
| } |
| |
| new_buf->complete_size = size_needed; |
| *buf = (void *)(new_buf->buf); |
| return *buf; |
| |
| } |
| |
| static inline void afl_free(void *buf) { |
| |
| if (buf) { free(afl_alloc_bufptr(buf)); } |
| |
| } |
| |
| /* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ |
| static inline void afl_swap_bufs(void **buf1, void **buf2) { |
| |
| void *scratch_buf = *buf1; |
| *buf1 = *buf2; |
| *buf2 = scratch_buf; |
| |
| } |
| |
| #undef INITIAL_GROWTH_SIZE |
| |
| #endif /* ! _HAVE_ALLOC_INL_H */ |
| |