GNU libmicrohttpd 1.0.9
Loading...
Searching...
No Matches
memorypool.c
Go to the documentation of this file.
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007--2024 Daniel Pittman and Christian Grothoff
4 Copyright (C) 2014--2024 Evgeny Grin (Karlson2k)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
27#include "memorypool.h"
28#ifdef HAVE_STDLIB_H
29#include <stdlib.h>
30#endif /* HAVE_STDLIB_H */
31#include <string.h>
32#include <stdint.h>
33#include "mhd_assert.h"
34#include "mhd_check.h"
35#ifdef HAVE_SYS_MMAN_H
36#include <sys/mman.h>
37#endif
38#ifdef _WIN32
39#include <windows.h>
40#endif
41#ifdef HAVE_SYSCONF
42#include <unistd.h>
43#if defined(_SC_PAGE_SIZE)
44#define MHD_SC_PAGESIZE _SC_PAGE_SIZE
45#elif defined(_SC_PAGESIZE)
46#define MHD_SC_PAGESIZE _SC_PAGESIZE
47#endif /* _SC_PAGESIZE */
48#endif /* HAVE_SYSCONF */
49#include "mhd_limits.h" /* for SIZE_MAX, PAGESIZE / PAGE_SIZE */
50
51#if defined(MHD_USE_PAGESIZE_MACRO) || defined(MHD_USE_PAGE_SIZE_MACRO)
52#ifndef HAVE_SYSCONF /* Avoid duplicate include */
53#include <unistd.h>
54#endif /* HAVE_SYSCONF */
55#ifdef HAVE_SYS_PARAM_H
56#include <sys/param.h>
57#endif /* HAVE_SYS_PARAM_H */
58#endif /* MHD_USE_PAGESIZE_MACRO || MHD_USE_PAGE_SIZE_MACRO */
59
63#define _MHD_FALLBACK_PAGE_SIZE (4096)
64
65#if defined(MHD_USE_PAGESIZE_MACRO)
66#define MHD_DEF_PAGE_SIZE_ PAGESIZE
67#elif defined(MHD_USE_PAGE_SIZE_MACRO)
68#define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
69#else /* ! PAGESIZE */
70#define MHD_DEF_PAGE_SIZE_ _MHD_FALLBACK_PAGE_SIZE
71#endif /* ! PAGESIZE */
72
73
74#ifdef MHD_ASAN_POISON_ACTIVE
75#include <sanitizer/asan_interface.h>
76#endif /* MHD_ASAN_POISON_ACTIVE */
77
78/* define MAP_ANONYMOUS for Mac OS X */
79#if defined(MAP_ANON) && ! defined(MAP_ANONYMOUS)
80#define MAP_ANONYMOUS MAP_ANON
81#endif
82#if defined(_WIN32)
83#define MAP_FAILED NULL
84#elif ! defined(MAP_FAILED)
85#define MAP_FAILED ((void*) -1)
86#endif
87
91#define ALIGN_SIZE (2 * sizeof(void*))
92
96#define ROUND_TO_ALIGN(n) (((n) + (ALIGN_SIZE - 1)) \
97 / (ALIGN_SIZE) *(ALIGN_SIZE))
98
99/* ROUND_TO_ALIGN_PLUS_RED_ZONE() rounds up and, when user poisoning is
100 active, adds a red zone, so its result is always >= its argument --
101 unless it wrapped. Every caller that passes a caller-supplied size
102 must therefore reject the result with
103
104 if (asize < size)
105
106 and not by testing for zero. Testing for zero is correct only in the
107 build where the red zone is 0: ROUND_TO_ALIGN() lands exactly on 0 for
108 the top ALIGN_SIZE-1 values of size_t, so a zero result is the only
109 wrapped outcome there. Adding a red zone moves that outcome to
110 _MHD_RED_ZONE_SIZE, which is non-zero and small enough to pass the
111 remaining-space test below it, so a zero test silently stops firing in
112 exactly the build that has the extra instrumentation. Sizes that are
113 already bounded by pool->size (MHD_pool_reset(), the block_offset +
114 block_size expressions) cannot wrap and need no such test -- but they
115 do need the trailing red zone clamped to pool->size, see the two sites
116 that do so. */
117
118
119#ifndef MHD_ASAN_POISON_ACTIVE
120#define _MHD_NOSANITIZE_PTRS
121#define _MHD_RED_ZONE_SIZE (0)
122#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) ROUND_TO_ALIGN(n)
123#define _MHD_POISON_MEMORY(pointer, size) (void)0
124#define _MHD_UNPOISON_MEMORY(pointer, size) (void)0
128#define mp_ptr_le_(p1,p2) \
129 (((const uint8_t*)(p1)) <= ((const uint8_t*)(p2)))
134#define mp_ptr_diff_(p1,p2) \
135 ((size_t)(((const uint8_t*)(p1)) - ((const uint8_t*)(p2))))
136#else /* MHD_ASAN_POISON_ACTIVE */
137#define _MHD_RED_ZONE_SIZE (ALIGN_SIZE)
138#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) (ROUND_TO_ALIGN(n) + _MHD_RED_ZONE_SIZE)
139#define _MHD_POISON_MEMORY(pointer, size) \
140 ASAN_POISON_MEMORY_REGION ((pointer), (size))
141#define _MHD_UNPOISON_MEMORY(pointer, size) \
142 ASAN_UNPOISON_MEMORY_REGION ((pointer), (size))
143#if defined(FUNC_PTRCOMPARE_CAST_WORKAROUND_WORKS)
147#define mp_ptr_le_(p1,p2) \
148 (((uintptr_t)((const void*)(p1))) <= ((uintptr_t)((const void*)(p2))))
153#define mp_ptr_diff_(p1,p2) \
154 ((size_t)(((uintptr_t)((const uint8_t*)(p1))) - \
155 ((uintptr_t)((const uint8_t*)(p2)))))
156#elif defined(FUNC_ATTR_PTRCOMPARE_WORKS) && \
157 defined(FUNC_ATTR_PTRSUBTRACT_WORKS)
158#ifdef _DEBUG
162__attribute__((no_sanitize ("pointer-compare"))) static bool
163mp_ptr_le_ (const void *p1, const void *p2)
164{
165 return (((const uint8_t *) p1) <= ((const uint8_t *) p2));
166}
167
168
169#endif /* _DEBUG */
170
171
176__attribute__((no_sanitize ("pointer-subtract"))) static size_t
177mp_ptr_diff_ (const void *p1, const void *p2)
178{
179 return (size_t) (((const uint8_t *) p1) - ((const uint8_t *) p2));
180}
181
182
183#elif defined(FUNC_ATTR_NOSANITIZE_WORKS)
184#ifdef _DEBUG
188__attribute__((no_sanitize ("address"))) static bool
189mp_ptr_le_ (const void *p1, const void *p2)
190{
191 return (((const uint8_t *) p1) <= ((const uint8_t *) p2));
192}
193
194
195#endif /* _DEBUG */
196
201__attribute__((no_sanitize ("address"))) static size_t
202mp_ptr_diff_ (const void *p1, const void *p2)
203{
204 return (size_t) (((const uint8_t *) p1) - ((const uint8_t *) p2));
205}
206
207
208#else /* ! FUNC_ATTR_NOSANITIZE_WORKS */
209#error User-poisoning cannot be used
210#endif /* ! FUNC_ATTR_NOSANITIZE_WORKS */
211#endif /* MHD_ASAN_POISON_ACTIVE */
212
216static size_t MHD_sys_page_size_ = (size_t)
217#if defined(MHD_USE_PAGESIZE_MACRO_STATIC)
218 PAGESIZE;
219#elif defined(MHD_USE_PAGE_SIZE_MACRO_STATIC)
220 PAGE_SIZE;
221#else /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
222 _MHD_FALLBACK_PAGE_SIZE; /* Default fallback value */
223#endif /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
224
228void
230{
231#ifdef MHD_SC_PAGESIZE
232 long result;
233 result = sysconf (MHD_SC_PAGESIZE);
234 if (-1 != result)
235 MHD_sys_page_size_ = (size_t) result;
236 else
238#elif defined(_WIN32)
239 SYSTEM_INFO si;
240 GetSystemInfo (&si);
241 MHD_sys_page_size_ = (size_t) si.dwPageSize;
242#else
244#endif /* _WIN32 */
246}
247
248
253struct MemoryPool
254{
255
259 uint8_t *memory;
260
264 size_t size;
265
269 size_t pos;
270
274 size_t end;
275
279 bool is_mmap;
280};
281
282
289struct MemoryPool *
290MHD_pool_create (size_t max)
291{
292 struct MemoryPool *pool;
293 size_t alloc_size;
294
295 mhd_assert (max > 0);
296 alloc_size = 0;
297 pool = malloc (sizeof (struct MemoryPool));
298 if (NULL == pool)
299 return NULL;
300#if defined(MAP_ANONYMOUS) || defined(_WIN32)
301 if ( (max <= 32 * 1024) ||
302 (max < MHD_sys_page_size_ * 4 / 3) )
303 {
304 pool->memory = MAP_FAILED;
305 }
306 else
307 {
308 /* Round up allocation to page granularity. */
309 alloc_size = max + MHD_sys_page_size_ - 1;
310 alloc_size -= alloc_size % MHD_sys_page_size_;
311#if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
312 pool->memory = mmap (NULL,
313 alloc_size,
314 PROT_READ | PROT_WRITE,
315 MAP_PRIVATE | MAP_ANONYMOUS,
316 -1,
317 0);
318#elif defined(_WIN32)
319 pool->memory = VirtualAlloc (NULL,
320 alloc_size,
321 MEM_COMMIT | MEM_RESERVE,
322 PAGE_READWRITE);
323#endif /* _WIN32 */
324 }
325#else /* ! _WIN32 && ! MAP_ANONYMOUS */
326 pool->memory = MAP_FAILED;
327#endif /* ! _WIN32 && ! MAP_ANONYMOUS */
328 if (MAP_FAILED == pool->memory)
329 {
330 alloc_size = ROUND_TO_ALIGN (max);
331 pool->memory = malloc (alloc_size);
332 if (NULL == pool->memory)
333 {
334 free (pool);
335 return NULL;
336 }
337 pool->is_mmap = false;
338 }
339#if defined(MAP_ANONYMOUS) || defined(_WIN32)
340 else
341 {
342 pool->is_mmap = true;
343 }
344#endif /* _WIN32 || MAP_ANONYMOUS */
345 mhd_assert (0 == (((uintptr_t) pool->memory) % ALIGN_SIZE));
346 pool->pos = 0;
347 pool->end = alloc_size;
348 pool->size = alloc_size;
349 mhd_assert (0 < alloc_size);
350 _MHD_POISON_MEMORY (pool->memory, pool->size);
351 return pool;
352}
353
354
360void
361MHD_pool_destroy (struct MemoryPool *pool)
362{
363 if (NULL == pool)
364 return;
365
366 mhd_assert (pool->end >= pool->pos);
367 mhd_assert (pool->size >= pool->end - pool->pos);
368 mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
369 _MHD_UNPOISON_MEMORY (pool->memory, pool->size);
370 if (! pool->is_mmap)
371 free (pool->memory);
372 else
373#if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
374 munmap (pool->memory,
375 pool->size);
376#elif defined(_WIN32)
377 VirtualFree (pool->memory,
378 0,
379 MEM_RELEASE);
380#else
381 abort ();
382#endif
383 free (pool);
384}
385
386
393size_t
394MHD_pool_get_free (struct MemoryPool *pool)
395{
396 mhd_assert (pool->end >= pool->pos);
397 mhd_assert (pool->size >= pool->end - pool->pos);
398 mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
399#ifdef MHD_ASAN_POISON_ACTIVE
400 if ((pool->end - pool->pos) <= _MHD_RED_ZONE_SIZE)
401 return 0;
402#endif /* MHD_ASAN_POISON_ACTIVE */
403 return (pool->end - pool->pos) - _MHD_RED_ZONE_SIZE;
404}
405
406
418void *
419MHD_pool_allocate (struct MemoryPool *pool,
420 size_t size,
421 bool from_end)
422{
423 void *ret;
424 size_t asize;
425
426 mhd_assert (pool->end >= pool->pos);
427 mhd_assert (pool->size >= pool->end - pool->pos);
428 mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
429 asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
430 if (asize < size)
431 return NULL; /* Value wrap, @a size is too close to SIZE_MAX */
432 if (asize > pool->end - pool->pos)
433 return NULL;
434 if (from_end)
435 {
436 ret = &pool->memory[pool->end - asize];
437 pool->end -= asize;
438 }
439 else
440 {
441 ret = &pool->memory[pool->pos];
442 pool->pos += asize;
443 }
444 _MHD_UNPOISON_MEMORY (ret, size);
445 return ret;
446}
447
448
459bool
460MHD_pool_is_resizable_inplace (struct MemoryPool *pool,
461 void *block,
462 size_t block_size)
463{
464 mhd_assert (pool->end >= pool->pos);
465 mhd_assert (pool->size >= pool->end - pool->pos);
466 mhd_assert (block != NULL || block_size == 0);
467 mhd_assert (pool->size >= block_size);
468 if (NULL != block)
469 {
470 const size_t block_offset = mp_ptr_diff_ (block, pool->memory);
471 mhd_assert (mp_ptr_le_ (pool->memory, block));
472 mhd_assert (pool->size >= block_offset);
473 mhd_assert (pool->size >= block_offset + block_size);
474 return (pool->pos ==
475 ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size));
476 }
477 return false; /* Unallocated blocks cannot be resized in-place */
478}
479
480
500void *
501MHD_pool_try_alloc (struct MemoryPool *pool,
502 size_t size,
503 size_t *required_bytes)
504{
505 void *ret;
506 size_t asize;
507
508 mhd_assert (pool->end >= pool->pos);
509 mhd_assert (pool->size >= pool->end - pool->pos);
510 mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
511 asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
512 if (asize < size)
513 { /* Value wrap, @a size is too close to SIZE_MAX, very unlikely */
514 *required_bytes = SIZE_MAX;
515 return NULL;
516 }
517 if (asize > pool->end - pool->pos)
518 {
519 mhd_assert ((pool->end - pool->pos) == \
520 ROUND_TO_ALIGN (pool->end - pool->pos));
521 if (asize <= pool->end)
522 *required_bytes = asize - (pool->end - pool->pos);
523 else
524 *required_bytes = SIZE_MAX;
525 return NULL;
526 }
527 *required_bytes = 0;
528 ret = &pool->memory[pool->end - asize];
529 pool->end -= asize;
530 _MHD_UNPOISON_MEMORY (ret, size);
531 return ret;
532}
533
534
552void *
553MHD_pool_reallocate (struct MemoryPool *pool,
554 void *old,
555 size_t old_size,
556 size_t new_size)
557{
558 size_t asize;
559 uint8_t *new_blc;
560
561 mhd_assert (pool->end >= pool->pos);
562 mhd_assert (pool->size >= pool->end - pool->pos);
563 mhd_assert (old != NULL || old_size == 0);
564 mhd_assert (pool->size >= old_size);
565 mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
566#if defined(MHD_ASAN_POISON_ACTIVE) && defined(HAVE___ASAN_REGION_IS_POISONED)
567 mhd_assert (NULL == __asan_region_is_poisoned (old, old_size));
568#endif /* MHD_ASAN_POISON_ACTIVE && HAVE___ASAN_REGION_IS_POISONED */
569
570 /* No block can ever be larger than the pool, so reject that here once
571 rather than in each of the paths below. This is what stops a
572 wrapping @a new_size: both remaining size tests are made on values
573 that ROUND_TO_ALIGN_PLUS_RED_ZONE() has already wrapped back to
574 something small and plausible, so neither of them fires and the
575 caller is handed a block it believes is nearly SIZE_MAX bytes long.
576 The documented contract is to return NULL when the pool cannot
577 support @a new_size bytes. */
578 if (new_size > pool->size)
579 return NULL;
580
581 if (NULL != old)
582 { /* Have previously allocated data */
583 const size_t old_offset = mp_ptr_diff_ (old, pool->memory);
584 const bool shrinking = (old_size > new_size);
585
586 /* 'old' and 'old_size' come from the caller and bound both the
587 memset() that shrinking performs a few lines below and the
588 memcpy (new_blc, old, old_size) at the end of this function, so this
589 is the precondition of two unbounded copies and must hold in release
590 builds too. Failing it returns NULL, aka
591 "the pool cannot satisfy this request" and which leaves
592 'old' valid, so the connection is failed by the caller instead of
593 corrupting the pool.
594 The lower bound stays an assertion because mp_ptr_le_() only exists in
595 debug builds when user-poisoning is active; it is subsumed anyway,
596 because mp_ptr_diff_() wraps for a pointer below pool->memory and the
597 resulting huge 'old_offset' fails the bound below. */
598 mhd_assert (mp_ptr_le_ (pool->memory, old));
599 /* (pool->memory + pool->size >= (uint8_t*) old + old_size) */
600 MHD_CHECK_RET_ ((pool->size - _MHD_RED_ZONE_SIZE) >=
601 (old_offset + old_size),
602 NULL);
603 /* Blocks "from the end" must not be reallocated */
604 /* (old_size == 0 || pool->memory + pool->pos > (uint8_t*) old) */
605 mhd_assert ((old_size == 0) || \
606 (pool->pos > old_offset));
607 mhd_assert ((old_size == 0) || \
608 ((pool->end - _MHD_RED_ZONE_SIZE) >= (old_offset + old_size)));
609 /* Try resizing in-place */
610 if (shrinking)
611 { /* Shrinking in-place, zero-out freed part */
612 memset ((uint8_t *) old + new_size, 0, old_size - new_size);
613 _MHD_POISON_MEMORY ((uint8_t *) old + new_size, old_size - new_size);
614 }
615 if (pool->pos ==
616 ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + old_size))
617 { /* "old" block is the last allocated block */
618 const size_t new_apos =
619 ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + new_size);
620 if (! shrinking)
621 { /* Grow in-place, check for enough space. */
622 /* Reject a wrapping @a new_size before looking at 'new_apos'.
623 'old_offset + new_size' can wrap all the way round and land
624 back inside [pool->pos, pool->end], and the two tests below
625 then both pass: the caller is handed a block it believes is
626 nearly SIZE_MAX bytes long. The "allocate a new block" path
627 further down already has an explicit wrap test; this is the
628 same check for the in-place path. */
629 if (new_size > pool->size - old_offset)
630 return NULL; /* Value wrap, or beyond the pool */
631 if ( (new_apos > pool->end) ||
632 (new_apos < pool->pos) ) /* Value wrap */
633 return NULL; /* No space */
634 }
635 /* Resized in-place */
636 pool->pos = new_apos;
637 _MHD_UNPOISON_MEMORY (old, new_size);
638 return old;
639 }
640 if (shrinking)
641 return old; /* Resized in-place, freed part remains allocated */
642 }
643 /* Need to allocate new block */
644 asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
645 if ( (asize < new_size) || /* Value wrap, too large new_size. */
646 (asize > pool->end - pool->pos) ) /* Not enough space */
647 return NULL;
648
649 new_blc = pool->memory + pool->pos;
650 pool->pos += asize;
651
652 _MHD_UNPOISON_MEMORY (new_blc, new_size);
653 if (0 != old_size)
654 {
655 /* Move data to new block, old block remains allocated */
656 memcpy (new_blc, old, old_size);
657 /* Zero-out old block */
658 memset (old, 0, old_size);
659 _MHD_POISON_MEMORY (old, old_size);
660 }
661 return new_blc;
662}
663
664
677void
678MHD_pool_deallocate (struct MemoryPool *pool,
679 void *block,
680 size_t block_size)
681{
682 mhd_assert (pool->end >= pool->pos);
683 mhd_assert (pool->size >= pool->end - pool->pos);
684 mhd_assert (block != NULL || block_size == 0);
685 mhd_assert (pool->size >= block_size);
686 mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
687
688 if (NULL != block)
689 { /* Have previously allocated data */
690 const size_t block_offset = mp_ptr_diff_ (block, pool->memory);
691 mhd_assert (mp_ptr_le_ (pool->memory, block));
692 mhd_assert (block_offset <= pool->size);
693 /* A block allocated "from the end" starts at or after pool->end, a
694 "normal" block starts before it. Those two ranges meet when the
695 pool is exactly full (pool->pos == pool->end), so an end block may
696 legitimately start at pool->pos; only a normal block may not. */
697 mhd_assert ((block_offset >= pool->end) || \
698 (block_offset != pool->pos) || (block_size == 0));
699 /* Zero-out deallocated region */
700 if (0 != block_size)
701 {
702 memset (block, 0, block_size);
703 _MHD_POISON_MEMORY (block, block_size);
704 }
705#if ! defined(MHD_FAVOR_SMALL_CODE) && ! defined(MHD_ASAN_POISON_ACTIVE)
706 else
707 return; /* Zero size, no need to do anything */
708#endif /* ! MHD_FAVOR_SMALL_CODE && ! MHD_ASAN_POISON_ACTIVE */
709 if (block_offset < pool->end)
710 {
711 /* "Normal" block, not allocated "from the end".
712 The test is against pool->end, not pool->pos: when the pool is
713 exactly full the two are equal, and a block allocated "from the
714 end" then also satisfies 'block_offset <= pool->pos', so it
715 would be mistaken for a normal block and never returned. */
716 size_t alg_end =
717 ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size);
718 /* Clamped exactly as MHD_pool_reset() clamps pool->pos, and for the
719 same reason: a block that reaches the end of the pool has no room
720 for a trailing red zone, so its recorded end is pool->size. The
721 two have to agree or the "is this the last block" test below can
722 never match such a block. */
723 if (alg_end > pool->size)
724 alg_end = pool->size;
725 mhd_assert (alg_end <= pool->pos);
726 if (alg_end == pool->pos)
727 {
728 /* The last allocated block, return deallocated block to the pool */
729 size_t alg_start = ROUND_TO_ALIGN (block_offset);
730 mhd_assert (alg_start >= block_offset);
731#if defined(MHD_ASAN_POISON_ACTIVE)
732 if (alg_start != block_offset)
733 {
734 _MHD_POISON_MEMORY (pool->memory + block_offset, \
735 alg_start - block_offset);
736 }
737 else if (0 != alg_start)
738 {
739 bool need_red_zone_before;
740 mhd_assert (_MHD_RED_ZONE_SIZE <= alg_start);
741#if defined(HAVE___ASAN_REGION_IS_POISONED)
742 need_red_zone_before =
743 (NULL == __asan_region_is_poisoned (pool->memory
744 + alg_start
747#elif defined(HAVE___ASAN_ADDRESS_IS_POISONED)
748 need_red_zone_before =
749 (0 == __asan_address_is_poisoned (pool->memory + alg_start - 1));
750#else /* ! HAVE___ASAN_ADDRESS_IS_POISONED */
751 need_red_zone_before = true; /* Unknown, assume new red zone needed */
752#endif /* ! HAVE___ASAN_ADDRESS_IS_POISONED */
753 if (need_red_zone_before)
754 {
755 _MHD_POISON_MEMORY (pool->memory + alg_start, _MHD_RED_ZONE_SIZE);
756 alg_start += _MHD_RED_ZONE_SIZE;
757 }
758 }
759#endif /* MHD_ASAN_POISON_ACTIVE */
760 mhd_assert (alg_start <= pool->pos);
761 mhd_assert (alg_start == ROUND_TO_ALIGN (alg_start));
762 pool->pos = alg_start;
763 }
764 }
765 else
766 {
767 /* Allocated "from the end" block. */
768 /* The size and the pointers of such block should not be manipulated by
769 MHD code (block split is disallowed). */
770 mhd_assert (block_offset >= pool->end);
771 mhd_assert (ROUND_TO_ALIGN (block_offset) == block_offset);
772 if (block_offset == pool->end)
773 {
774 /* The last allocated block, return deallocated block to the pool */
775 const size_t alg_end =
776 ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size);
777 pool->end = alg_end;
778 }
779 }
780 }
781}
782
783
797void *
798MHD_pool_reset (struct MemoryPool *pool,
799 void *keep,
800 size_t copy_bytes,
801 size_t new_size)
802{
803 mhd_assert (pool->end >= pool->pos);
804 mhd_assert (pool->size >= pool->end - pool->pos);
805 mhd_assert (copy_bytes <= new_size);
806 mhd_assert (new_size <= pool->size);
807 mhd_assert (copy_bytes <= pool->size);
808 mhd_assert (keep != NULL || copy_bytes == 0);
809 mhd_assert (keep == NULL || mp_ptr_le_ (pool->memory, keep));
810 /* (keep == NULL || pool->memory + pool->size >= (uint8_t*) keep + copy_bytes) */
811 mhd_assert ((keep == NULL) || \
812 (pool->size >= mp_ptr_diff_ (keep, pool->memory) + copy_bytes));
813#if defined(MHD_ASAN_POISON_ACTIVE) && defined(HAVE___ASAN_REGION_IS_POISONED)
814 mhd_assert (NULL == __asan_region_is_poisoned (keep, copy_bytes));
815#endif /* MHD_ASAN_POISON_ACTIVE && HAVE___ASAN_REGION_IS_POISONED */
816 _MHD_UNPOISON_MEMORY (pool->memory, new_size);
817 if ( (NULL != keep) &&
818 (keep != pool->memory) )
819 {
820 if (0 != copy_bytes)
821 memmove (pool->memory,
822 keep,
823 copy_bytes);
824 }
825 /* technically not needed, but safer to zero out */
826 if (pool->size > copy_bytes)
827 {
828 size_t to_zero;
830 to_zero = pool->size - copy_bytes;
831 _MHD_UNPOISON_MEMORY (pool->memory + copy_bytes, to_zero);
832#ifdef _WIN32
833 if (pool->is_mmap)
834 {
835 size_t to_recommit;
836 uint8_t *recommit_addr;
837 /* Round down to page size */
838 to_recommit = to_zero - to_zero % MHD_sys_page_size_;
839 recommit_addr = pool->memory + pool->size - to_recommit;
840
841 /* De-committing and re-committing again clear memory and make
842 * pages free / available for other needs until accessed. */
843 if (VirtualFree (recommit_addr,
844 to_recommit,
845 MEM_DECOMMIT))
846 {
847 to_zero -= to_recommit;
848
849 if (recommit_addr != VirtualAlloc (recommit_addr,
850 to_recommit,
851 MEM_COMMIT,
852 PAGE_READWRITE))
853 abort (); /* Serious error, must never happen */
854 }
855 }
856#endif /* _WIN32 */
857 memset (&pool->memory[copy_bytes],
858 0,
859 to_zero);
860 }
861 pool->pos = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
862 /* The red zone is a gap kept *inside* the pool so that an overrun of the
863 block lands in poisoned bytes. There is no room for it once the kept
864 block reaches the end of the pool, and none is needed there either --
865 past pool->size the allocation's own ASAN red zone takes over. Without
866 this clamp pool->pos would be set past pool->end for any @a new_size
867 within a red zone of pool->size, breaking the pool->end >= pool->pos
868 invariant that every other entry point asserts. In the build with no
869 user poisoning _MHD_RED_ZONE_SIZE is 0 and this can never fire. */
870 if (pool->pos > pool->size)
871 pool->pos = pool->size;
872 pool->end = pool->size;
873 _MHD_POISON_MEMORY (((uint8_t *) pool->memory) + new_size, \
874 pool->size - new_size);
875 return pool->memory;
876}
877
878
879/* end of memorypool.c */
void MHD_init_mem_pools_(void)
Definition memorypool.c:229
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition memorypool.c:553
void MHD_pool_destroy(struct MemoryPool *pool)
Definition memorypool.c:361
#define mp_ptr_diff_(p1, p2)
Definition memorypool.c:134
bool MHD_pool_is_resizable_inplace(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:460
#define MHD_DEF_PAGE_SIZE_
Definition memorypool.c:70
#define ALIGN_SIZE
Definition memorypool.c:91
#define _MHD_FALLBACK_PAGE_SIZE
Definition memorypool.c:63
void MHD_pool_deallocate(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:678
#define MAP_FAILED
Definition memorypool.c:85
void * MHD_pool_try_alloc(struct MemoryPool *pool, size_t size, size_t *required_bytes)
Definition memorypool.c:501
#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n)
Definition memorypool.c:122
#define _MHD_UNPOISON_MEMORY(pointer, size)
Definition memorypool.c:124
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition memorypool.c:394
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition memorypool.c:798
static size_t MHD_sys_page_size_
Definition memorypool.c:216
struct MemoryPool * MHD_pool_create(size_t max)
Definition memorypool.c:290
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, bool from_end)
Definition memorypool.c:419
#define mp_ptr_le_(p1, p2)
Definition memorypool.c:128
#define ROUND_TO_ALIGN(n)
Definition memorypool.c:96
#define _MHD_POISON_MEMORY(pointer, size)
Definition memorypool.c:123
#define _MHD_RED_ZONE_SIZE
Definition memorypool.c:121
memory pool; mostly used for efficient (de)allocation for each connection and bounding memory use for...
macros for mhd_assert()
#define mhd_assert(ignore)
Definition mhd_assert.h:45
macros for MHD_CHECK_(), always-compiled memory-safety invariants
#define MHD_CHECK_RET_(expr, retval)
Definition mhd_check.h:137
limits values definitions
#define SIZE_MAX
Definition mhd_limits.h:111
#define NULL