expand_heap.c 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. #include <limits.h>
  2. #include <stdint.h>
  3. #include <errno.h>
  4. #include <sys/mman.h>
  5. #include "libc.h"
  6. #include "syscall.h"
  7. /* This function returns true if the interval [old,new]
  8. * intersects the 'len'-sized interval below &libc.auxv
  9. * (interpreted as the main-thread stack) or below &b
  10. * (the current stack). It is used to defend against
  11. * buggy brk implementations that can cross the stack. */
  12. static int traverses_stack_p(uintptr_t old, uintptr_t new)
  13. {
  14. const uintptr_t len = 8<<20;
  15. uintptr_t a, b;
  16. b = (uintptr_t)libc.auxv;
  17. a = b > len ? b-len : 0;
  18. if (new>a && old<b) return 1;
  19. b = (uintptr_t)&b;
  20. a = b > len ? b-len : 0;
  21. if (new>a && old<b) return 1;
  22. return 0;
  23. }
  24. void *__mmap(void *, size_t, int, int, int, off_t);
  25. /* Expand the heap in-place if brk can be used, or otherwise via mmap,
  26. * using an exponential lower bound on growth by mmap to make
  27. * fragmentation asymptotically irrelevant. The size argument is both
  28. * an input and an output, since the caller needs to know the size
  29. * allocated, which will be larger than requested due to page alignment
  30. * and mmap minimum size rules. The caller is responsible for locking
  31. * to prevent concurrent calls. */
  32. void *__expand_heap(size_t *pn)
  33. {
  34. static uintptr_t brk;
  35. static unsigned mmap_step;
  36. size_t n = *pn;
  37. if (n > SIZE_MAX/2 - PAGE_SIZE) {
  38. errno = ENOMEM;
  39. return 0;
  40. }
  41. n += -n & PAGE_SIZE-1;
  42. if (!brk) {
  43. brk = __syscall(SYS_brk, 0);
  44. brk += -brk & PAGE_SIZE-1;
  45. }
  46. if (n < SIZE_MAX-brk && !traverses_stack_p(brk, brk+n)
  47. && __syscall(SYS_brk, brk+n)==brk+n) {
  48. *pn = n;
  49. brk += n;
  50. return (void *)(brk-n);
  51. }
  52. size_t min = (size_t)PAGE_SIZE << mmap_step/2;
  53. if (n < min) n = min;
  54. void *area = __mmap(0, n, PROT_READ|PROT_WRITE,
  55. MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  56. if (area == MAP_FAILED) return 0;
  57. *pn = n;
  58. mmap_step++;
  59. return area;
  60. }