libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 50c9e5efea6913cf97b4b1afd8c57f886f552d32
parent fe8a9c6181424520383dd7ff7e30a082158e4b04
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Sun, 10 Oct 2021 21:16:25 +0300

Added custom memory poisoning for MemoryPool

Diffstat:
Mconfigure.ac | 111++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/include/mhd_options.h | 27++++++++++++++++++++++++++-
Msrc/microhttpd/memorypool.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/testcurl/test_toolarge.c | 6++++++
4 files changed, 203 insertions(+), 16 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -2648,6 +2648,8 @@ AS_VAR_IF([enable_sanitizers], ["yes"], ) AS_VAR_IF([mhd_cv_cc_sanitizer_address],["yes"], [ + AC_DEFINE([MHD_ASAN_ACTIVE], [1], [Define to '1' if you have address sanitizer enabled]) + AC_CHECK_HEADERS([sanitizer/asan_interface.h], [], [], [AC_INCLUDES_DEFAULT]) AX_APPEND_FLAG([-fsanitize=address], [san_FLAGS]) enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }address" AC_CACHE_CHECK([whether leak detect is supported], [mhd_cv_cc_sanitizer_address_leak], @@ -2691,6 +2693,104 @@ AS_VAR_IF([enable_sanitizers], ["yes"], enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }pointer subtract" ] ) + AS_VAR_IF([ac_cv_header_sanitizer_asan_interface_h],["yes"], + [ + AC_CACHE_CHECK([whether '__attribute__((no_sanitize("pointer-compare","pointer-subtract")))' works], [mhd_cv_func_attribute_nosanitize_ptr], + [ + ASAN_OPTIONS="exitcode=88:detect_invalid_pointer_pairs=3:halt_on_error=1" + export ASAN_OPTIONS + CFLAGS="${saved_CFLAGS} ${san_CFLAGS} ${san_FLAGS} ${errattr_CFLAGS}" + AC_RUN_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +#include <stdlib.h> + +__attribute__((no_sanitize("pointer-compare","pointer-subtract"))) +int ptr_process(void *ptr1, void *ptr2) +{ + if ((char*)ptr1 <= (char*)ptr2) + return (int) ((char*)ptr2 - (char*)ptr1); + return (int) ((char*)ptr1 - (char*)ptr2); +} + ]], + [[ + int *a = (int*) malloc (sizeof(int)*4); + int *b = (int*) malloc (sizeof(long)*6); + int c = ptr_process(a, b); + if (c) + { + free (b); + free (a); + return 0; + } + free (a); + free (b); + ]] + ) + ], + [mhd_cv_func_attribute_nosanitize_ptr=yes], [mhd_cv_func_attribute_nosanitize_ptr=no], + [ + # Cross-compiling with sanitizers?? + mhd_cv_func_attribute_nosanitize_ptr=no + ] + ) + AS_UNSET([ASAN_OPTIONS]) + ] + ) + AS_VAR_IF([mhd_cv_func_attribute_nosanitize_ptr], ["yes"], + [AC_DEFINE([FUNC_ATTR_PTRCOMPARE_WOKRS],[1],[Define to '1' if '__attribute__((no_sanitize("pointer-compare","pointer-subtract")))' works])], + [ + AC_CACHE_CHECK([whether '__attribute__((no_sanitize("address")))' works for pointers compare], [mhd_cv_func_attribute_nosanitize_addr], + [ + ASAN_OPTIONS="exitcode=88:detect_invalid_pointer_pairs=3:halt_on_error=1" + export ASAN_OPTIONS + CFLAGS="${saved_CFLAGS} ${san_CFLAGS} ${san_FLAGS} ${errattr_CFLAGS}" + AC_RUN_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +#include <stdlib.h> + +__attribute__((no_sanitize("address"))) +int ptr_process(void *ptr1, void *ptr2) +{ + if ((char*)ptr1 <= (char*)ptr2) + return (int) ((char*)ptr2 - (char*)ptr1); + return (int) ((char*)ptr1 - (char*)ptr2); +} + ]], + [[ + int *a = (int*) malloc (sizeof(int)*4); + int *b = (int*) malloc (sizeof(long)*6); + int c = ptr_process(a, b); + if (c) + { + free (b); + free (a); + return 0; + } + free (a); + free (b); + ]] + ) + ], + [mhd_cv_func_attribute_nosanitize_addr=yes], [mhd_cv_func_attribute_nosanitize_addr=no], + [ + # Cross-compiling with sanitizers?? + mhd_cv_func_attribute_nosanitize_addr=no + ] + ) + AS_UNSET([ASAN_OPTIONS]) + ] + ) + AS_VAR_IF([mhd_cv_func_attribute_nosanitize_addr], ["yes"], + [AC_DEFINE([FUNC_ATTR_NOSANITIZE_WORKS],[1],[Define to '1' if '__attribute__((no_sanitize("address")))' works for pointers compare])] + ) + ] + ) + ] + ) ] ) dnl Ensure that '#' will be processed correctly @@ -2812,6 +2912,15 @@ int main(void) ) AS_IF([test -z "${enabled_sanitizers}"], [AC_MSG_ERROR([cannot find any sanitizer supported by $CC])]) + AC_MSG_CHECKING([whether to enable user memory poisoning]) + AS_IF([test "x${mhd_cv_cc_sanitizer_address}" = "xyes" && test "x${ac_cv_header_sanitizer_asan_interface_h}" = "xyes" && \ + (test "x${mhd_cv_func_attribute_nosanitize_ptr}" = "xyes" || test "x${mhd_cv_func_attribute_nosanitize_addr}" = "xyes")], + [ + AC_DEFINE([MHD_ASAN_POISON_ACTIVE], [1], [Define to '1' if user memory poison is used]) + enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }user-poison" + AC_MSG_RESULT([yes]) + ], [AC_MSG_RESULT([no])] + ) AS_VAR_IF([mhd_cv_cc_sanitizer_address],["yes"], [ AX_APPEND_FLAG([-D_FORTIFY_SOURCE=0], [san_CFLAGS]) @@ -2829,7 +2938,7 @@ int main(void) AM_ASAN_OPTIONS="exitcode=88:strict_string_checks=1:detect_stack_use_after_return=1" AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:check_initialization_order=1:strict_init_order=1:redzone=64" AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:max_free_fill_size=1024:detect_invalid_pointer_pairs=3" - AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:handle_ioctl=1:halt_on_error=1" + AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:handle_ioctl=1:allow_user_poisoning=1:halt_on_error=1" AS_VAR_IF([mhd_cv_cc_sanitizer_address_leak], ["yes"], [AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:detect_leaks=1"]) AM_UBSAN_OPTIONS="exitcode=87:print_stacktrace=1:halt_on_error=1" diff --git a/src/include/mhd_options.h b/src/include/mhd_options.h @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - Copyright (C) 2016 Karlson2k (Evgeny Grin) + Copyright (C) 2016-2021 Karlson2k (Evgeny Grin) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -142,4 +142,29 @@ #define MHD_FAVOR_FAST_CODE 1 #endif /* !MHD_FAVOR_FAST_CODE && !MHD_FAVOR_SMALL_CODE */ +#ifndef MHD_ASAN_ACTIVE +#if (defined(__GNUC__) || defined(_MSC_VER)) && defined(__SANITIZE_ADDRESS__) +#define MHD_ASAN_ACTIVE 1 +#elif defined(__has_feature) +#if __has_feature (address_sanitizer) +#define MHD_ASAN_ACTIVE 1 +#endif /* __has_feature(address_sanitizer) */ +#endif /* __has_feature */ +#endif /* MHD_ASAN_ACTIVE */ + +#if defined(MHD_ASAN_ACTIVE) && defined(HAVE_SANITIZER_ASAN_INTERFACE_H) && \ + (defined(FUNC_ATTR_PTRCOMPARE_WOKRS) || defined(FUNC_ATTR_NOSANITIZE_WORKS)) +#ifndef MHD_ASAN_POISON_ACTIVE +/* Manual ASAN poisoning could be used */ +#warning User memory poisoning is not active +#endif /* ! MHD_ASAN_POISON_ACTIVE */ +#define _MHD_USE_ASAN_POISON 1 +#else /* ! (MHD_ASAN_ACTIVE && HAVE_SANITIZER_ASAN_INTERFACE_H && + (FUNC_ATTR_PTRCOMPARE_WOKRS || FUNC_ATTR_NOSANITIZE_WORKS)) */ +#ifdef MHD_ASAN_POISON_ACTIVE +#error User memory poisoning is active, but conditions are not suitable +#endif /* MHD_ASAN_POISON_ACTIVE */ +#endif /* ! (MHD_ASAN_ACTIVE && HAVE_SANITIZER_ASAN_INTERFACE_H && + (FUNC_ATTR_PTRCOMPARE_WOKRS || FUNC_ATTR_NOSANITIZE_WORKS)) */ + #endif /* MHD_OPTIONS_H */ diff --git a/src/microhttpd/memorypool.c b/src/microhttpd/memorypool.c @@ -45,6 +45,11 @@ #define MHD_SC_PAGESIZE _SC_PAGESIZE #endif /* _SC_PAGESIZE */ #endif /* HAVE_SYSCONF */ +#include "mhd_limits.h" /* for SIZE_MAX */ + +#ifdef MHD_ASAN_POISON_ACTIVE +#include <sanitizer/asan_interface.h> +#endif /* _MHD_USE_ASAN_POISON */ /* define MAP_ANONYMOUS for Mac OS X */ #if defined(MAP_ANON) && ! defined(MAP_ANONYMOUS) @@ -67,6 +72,28 @@ #define ROUND_TO_ALIGN(n) (((n) + (ALIGN_SIZE - 1)) \ / (ALIGN_SIZE) *(ALIGN_SIZE)) + +#ifndef MHD_ASAN_POISON_ACTIVE +#define _MHD_NOSANITIZE_PTRS /**/ +#define _MHD_RED_ZONE_SIZE (0) +#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) ROUND_TO_ALIGN(n) +#define _MHD_POISON_MEMORY(pointer, size) /**/ +#define _MHD_UNPOISON_MEMORY(pointer, size) /**/ +#else /* MHD_ASAN_POISON_ACTIVE */ +#if defined(FUNC_ATTR_PTRCOMPARE_WOKRS) +#define _MHD_NOSANITIZE_PTRS \ + __attribute__((no_sanitize("pointer-compare","pointer-subtract"))) +#elif defined(FUNC_ATTR_NOSANITIZE_WORKS) +#define _MHD_NOSANITIZE_PTRS __attribute__((no_sanitize("address"))) +#endif +#define _MHD_RED_ZONE_SIZE (ALIGN_SIZE) +#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) (ROUND_TO_ALIGN(n) + _MHD_RED_ZONE_SIZE) +#define _MHD_POISON_MEMORY(pointer, size) \ + ASAN_POISON_MEMORY_REGION ((pointer), (size)) +#define _MHD_UNPOISON_MEMORY(pointer, size) \ + ASAN_UNPOISON_MEMORY_REGION ((pointer), (size)) +#endif /* MHD_ASAN_POISON_ACTIVE */ + #if defined(PAGE_SIZE) && (0 < (PAGE_SIZE + 0)) #define MHD_DEF_PAGE_SIZE_ PAGE_SIZE #elif defined(PAGESIZE) && (0 < (PAGESIZE + 0)) @@ -205,6 +232,7 @@ MHD_pool_create (size_t max) pool->end = alloc_size; pool->size = alloc_size; mhd_assert (0 < alloc_size); + _MHD_POISON_MEMORY (pool->memory, pool->size); return pool; } @@ -222,6 +250,7 @@ MHD_pool_destroy (struct MemoryPool *pool) mhd_assert (pool->end >= pool->pos); mhd_assert (pool->size >= pool->end - pool->pos); + _MHD_POISON_MEMORY (pool->memory, pool->size); if (! pool->is_mmap) free (pool->memory); else @@ -250,7 +279,11 @@ MHD_pool_get_free (struct MemoryPool *pool) { mhd_assert (pool->end >= pool->pos); mhd_assert (pool->size >= pool->end - pool->pos); - return (pool->end - pool->pos); +#ifdef MHD_ASAN_POISON_ACTIVE + if ((pool->end - pool->pos) <= _MHD_RED_ZONE_SIZE) + return 0; +#endif /* _MHD_USE_ASAN_POISON */ + return (pool->end - pool->pos) - _MHD_RED_ZONE_SIZE; } @@ -275,7 +308,7 @@ MHD_pool_allocate (struct MemoryPool *pool, mhd_assert (pool->end >= pool->pos); mhd_assert (pool->size >= pool->end - pool->pos); - asize = ROUND_TO_ALIGN (size); + asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size); if ( (0 == asize) && (0 != size) ) return NULL; /* size too close to SIZE_MAX */ if ( (pool->pos + asize > pool->end) || @@ -291,6 +324,7 @@ MHD_pool_allocate (struct MemoryPool *pool, ret = &pool->memory[pool->pos]; pool->pos += asize; } + _MHD_UNPOISON_MEMORY (ret, size); return ret; } @@ -299,7 +333,7 @@ MHD_pool_allocate (struct MemoryPool *pool, * Try to allocate @a size bytes memory area from the @a pool. * * If allocation fails, @a required_bytes is updated with size required to be - * freed in the @a pool from relocatable area to allocate requested number + * freed in the @a pool from rellocatable area to allocate requested number * of bytes. * Allocated memory area is always not rellocatable ("from end"). * @@ -311,7 +345,7 @@ MHD_pool_allocate (struct MemoryPool *pool, * Cannot be NULL. * @return the pointer to allocated memory area if succeed, * NULL if the pool doesn't have enough space, required_bytes is updated - * with amount of space needed to be freed in relocatable area or + * with amount of space needed to be freed in rellocatable area or * set to SIZE_MAX if requested size is too large for the pool. */ void * @@ -324,7 +358,7 @@ MHD_pool_try_alloc (struct MemoryPool *pool, mhd_assert (pool->end >= pool->pos); mhd_assert (pool->size >= pool->end - pool->pos); - asize = ROUND_TO_ALIGN (size); + asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size); if ( (0 == asize) && (0 != size) ) { /* size is too close to SIZE_MAX, very unlikely */ *required_bytes = SIZE_MAX; @@ -333,6 +367,8 @@ MHD_pool_try_alloc (struct MemoryPool *pool, if ( (pool->pos + asize > pool->end) || (pool->pos + asize < pool->pos)) { + mhd_assert ((pool->end - pool->pos) == \ + ROUND_TO_ALIGN (pool->end - pool->pos)); if (asize <= pool->end) *required_bytes = asize - (pool->end - pool->pos); else @@ -341,6 +377,7 @@ MHD_pool_try_alloc (struct MemoryPool *pool, } ret = &pool->memory[pool->end - asize]; pool->end -= asize; + _MHD_UNPOISON_MEMORY (ret, size); return ret; } @@ -362,7 +399,7 @@ MHD_pool_try_alloc (struct MemoryPool *pool, * NULL if the pool cannot support @a new_size * bytes (old continues to be valid for @a old_size) */ -void * +_MHD_NOSANITIZE_PTRS void * MHD_pool_reallocate (struct MemoryPool *pool, void *old, size_t old_size, @@ -374,11 +411,11 @@ MHD_pool_reallocate (struct MemoryPool *pool, mhd_assert (pool->end >= pool->pos); mhd_assert (pool->size >= pool->end - pool->pos); mhd_assert (old != NULL || old_size == 0); - mhd_assert (old == NULL || pool->memory <= (uint8_t*) old); mhd_assert (pool->size >= old_size); + mhd_assert (old == NULL || pool->memory <= (uint8_t*) old); /* (old == NULL || pool->memory + pool->size >= (uint8_t*) old + old_size) */ mhd_assert (old == NULL || \ - (pool->size) >= \ + (pool->size - _MHD_RED_ZONE_SIZE) >= \ (((size_t) (((uint8_t*) old) - pool->memory)) + old_size)); /* Blocks "from the end" must not be reallocated */ /* (old == NULL || old_size == 0 || pool->memory + pool->pos > (uint8_t*) old) */ @@ -386,7 +423,7 @@ MHD_pool_reallocate (struct MemoryPool *pool, pool->pos > (size_t) ((uint8_t*) old - pool->memory)); mhd_assert (old == NULL || old_size == 0 || \ (size_t) (((uint8_t*) old) - pool->memory) + old_size <= \ - pool->end); + pool->end - _MHD_RED_ZONE_SIZE); if (0 != old_size) { /* Have previously allocated data */ @@ -396,10 +433,13 @@ MHD_pool_reallocate (struct MemoryPool *pool, if (shrinking) { /* Shrinking in-place, zero-out freed part */ memset ((uint8_t*) old + new_size, 0, old_size - new_size); + _MHD_POISON_MEMORY ((uint8_t*) old + new_size, old_size - new_size); } - if (pool->pos == ROUND_TO_ALIGN (old_offset + old_size)) + if (pool->pos == + ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + old_size)) { /* "old" block is the last allocated block */ - const size_t new_apos = ROUND_TO_ALIGN (old_offset + new_size); + const size_t new_apos = + ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + new_size); if (! shrinking) { /* Grow in-place, check for enough space. */ if ( (new_apos > pool->end) || @@ -408,13 +448,14 @@ MHD_pool_reallocate (struct MemoryPool *pool, } /* Resized in-place */ pool->pos = new_apos; + _MHD_UNPOISON_MEMORY (old, new_size); return old; } if (shrinking) return old; /* Resized in-place, freed part remains allocated */ } /* Need to allocate new block */ - asize = ROUND_TO_ALIGN (new_size); + asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size); if ( ( (0 == asize) && (0 != new_size) ) || /* Value wrap, too large new_size. */ (asize > pool->end - pool->pos) ) /* Not enough space */ @@ -423,12 +464,14 @@ MHD_pool_reallocate (struct MemoryPool *pool, new_blc = pool->memory + pool->pos; pool->pos += asize; + _MHD_UNPOISON_MEMORY (new_blc, new_size); if (0 != old_size) { /* Move data to new block, old block remains allocated */ memcpy (new_blc, old, old_size); /* Zero-out old block */ memset (old, 0, old_size); + _MHD_POISON_MEMORY (old, old_size); } return new_blc; } @@ -447,7 +490,7 @@ MHD_pool_reallocate (struct MemoryPool *pool, * (should be larger or equal to @a copy_bytes) * @return addr new address of @a keep (if it had to change) */ -void * +_MHD_NOSANITIZE_PTRS void * MHD_pool_reset (struct MemoryPool *pool, void *keep, size_t copy_bytes, @@ -463,6 +506,7 @@ MHD_pool_reset (struct MemoryPool *pool, mhd_assert (keep == NULL || \ pool->size >= \ ((size_t) ((uint8_t*) keep - pool->memory)) + copy_bytes); + _MHD_UNPOISON_MEMORY (pool->memory, new_size); if ( (NULL != keep) && (keep != pool->memory) ) { @@ -477,6 +521,7 @@ MHD_pool_reset (struct MemoryPool *pool, size_t to_zero; /** Size of area to zero-out */ to_zero = pool->size - copy_bytes; + _MHD_UNPOISON_MEMORY (pool->memory + copy_bytes, to_zero); #ifdef _WIN32 if (pool->is_mmap) { @@ -506,8 +551,10 @@ MHD_pool_reset (struct MemoryPool *pool, 0, to_zero); } - pool->pos = ROUND_TO_ALIGN (new_size); + pool->pos = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size); pool->end = pool->size; + _MHD_POISON_MEMORY (((uint8_t*) pool->memory) + new_size, \ + pool->size - new_size); return pool->memory; } diff --git a/src/testcurl/test_toolarge.c b/src/testcurl/test_toolarge.c @@ -193,8 +193,14 @@ _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) #define BUFFER_SIZE 1024 +#define MHD_ASAN_ACTIVE 1 + /* The size of the test element that must pass the test */ +#ifndef MHD_ASAN_POISON_ACTIVE #define TEST_OK_SIZE (BUFFER_SIZE - 384) +#else /* MHD_ASAN_POISON_ACTIVE */ +#define TEST_OK_SIZE (BUFFER_SIZE - 384 - 80) +#endif /* MHD_ASAN_POISON_ACTIVE */ /* The size of the test element where tests are started */ #define TEST_START_SIZE (TEST_OK_SIZE - 16)