this repo has no description
1#include <errno.h>
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5#include <sys/cdefs.h>
6#include <sys/mman.h>
7#include <mach/vm_page_size.h>
8#include <os/overflow.h>
9
10#ifdef __LP64__
11#define GMALLOC_MAGIC 0xdeadbeefdeadbeef
12#else
13#define GMALLOC_MAGIC 0xdeadbeef
14#endif
15
16struct gmalloc_header {
17 size_t magic;
18 size_t size;
19 void *mmap_base;
20 size_t mmap_size;
21 size_t magic_again;
22};
23
24static int gmalloc_protect_before;
25static int gmalloc_fill_space;
26static int gmalloc_allow_reads;
27static int gmalloc_strict_size;
28static int gmalloc_check_header = 1;
29
30void __malloc_init(const char *apple[]) {
31 (void) apple;
32
33 if (getenv("MALLOC_PROTECT_BEFORE")) gmalloc_protect_before = 1;
34 if (getenv("MALLOC_FILL_SPACE")) gmalloc_fill_space = 1;
35 if (getenv("MALLOC_ALLOW_READS")) gmalloc_allow_reads = 1;
36 if (getenv("MALLOC_STRICT_SIZE")) gmalloc_strict_size = 1;
37
38 const char *check_header = getenv("MALLOC_CHECK_HEADER");
39 if (check_header && (!strcmp(check_header, "0") || !strcmp(check_header, "NO"))) {
40 gmalloc_check_header = 0;
41 }
42}
43
44static inline size_t round_up(size_t size, size_t increment) {
45 if ((size & (increment - 1)) == 0) {
46 return size;
47 }
48 return (size | (increment - 1)) + 1;
49}
50
51static inline void *round_down(void *address, size_t align) {
52 uintptr_t v = (uintptr_t) address;
53 return (void *) (v & ~(align - 1));
54}
55
56static struct gmalloc_header *header_for_ptr(void *ptr) {
57 return (struct gmalloc_header *) ((char *) ptr - sizeof(struct gmalloc_header));
58}
59
60static void *do_alloc(size_t payload_size, size_t align) {
61 // Decide how much memory we're going to allocate,
62 // not counting the guard page.
63 size_t accessible_size = round_up(payload_size, align);
64 if (gmalloc_protect_before) {
65 // In protect-before mode, the header will reside
66 // on the guard page, so need to add it here.
67 } else {
68 accessible_size += sizeof(struct gmalloc_header);
69 }
70 accessible_size = round_up(accessible_size, vm_page_size);
71
72 void *mmap_base = mmap(
73 NULL, accessible_size + vm_page_size,
74 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
75 -1, 0
76 );
77 if (mmap_base == MAP_FAILED) {
78 return NULL;
79 }
80
81 void *guard = gmalloc_protect_before ? mmap_base : mmap_base + accessible_size;
82 void *accessible = gmalloc_protect_before ? mmap_base + vm_page_size : mmap_base;
83 void *payload = gmalloc_protect_before ? accessible : accessible + accessible_size - payload_size;
84 payload = round_down(payload, align);
85
86 if (gmalloc_fill_space) {
87 memset(accessible, 0x55, accessible_size);
88 }
89
90 struct gmalloc_header *header = header_for_ptr(payload);
91 header->magic = header->magic_again = GMALLOC_MAGIC;
92 header->size = payload_size;
93 header->mmap_base = mmap_base;
94 header->mmap_size = accessible_size + vm_page_size;
95
96 int guard_page_prot = gmalloc_allow_reads ? PROT_READ : PROT_NONE;
97 int rc = mprotect(guard, vm_page_size, guard_page_prot);
98 if (rc < 0) {
99 int saved_errno = errno;
100 munmap(mmap_base, accessible_size + vm_page_size);
101 errno = saved_errno;
102 return NULL;
103 }
104
105 return payload;
106}
107
108void *aligned_alloc(size_t align, size_t size) {
109 void *ptr = do_alloc(size, align);
110
111 if (ptr == NULL) {
112 int saved_errno = errno;
113 const char msg[] = "Failed to alloc: ";
114 write(2, msg, sizeof(msg) - 1);
115 // Do not use strerror(), because it allocates.
116 extern const char *const sys_errlist[];
117 write(2, sys_errlist[saved_errno], strlen(sys_errlist[saved_errno]));
118 write(2, "\n", 1);
119
120 errno = saved_errno;
121 return NULL;
122 }
123
124 return ptr;
125}
126
127void *malloc(size_t size) {
128 return aligned_alloc(gmalloc_strict_size ? 1 : 16, size);
129}
130
131int posix_memalign(void **ptr, size_t align, size_t size) {
132 *ptr = aligned_alloc(align, size);
133 if (*ptr == NULL) {
134 return ENOMEM;
135 }
136 return 0;
137}
138
139void *calloc(size_t num, size_t size) {
140 void *ptr = malloc(num * size);
141 memset(ptr, 0, num * size);
142 return ptr;
143}
144
145static void unlock_and_check_header(const struct gmalloc_header *header) {
146 if (gmalloc_protect_before && !gmalloc_allow_reads) {
147 mprotect(round_down((void *) header, vm_page_size), vm_page_size, PROT_READ);
148 }
149
150 if (!gmalloc_check_header) {
151 return;
152 }
153
154 if (header->magic != GMALLOC_MAGIC || header->magic_again != GMALLOC_MAGIC) {
155 abort();
156 }
157}
158
159void free(void *ptr) {
160 if (ptr == NULL) {
161 return;
162 }
163
164 struct gmalloc_header *header = header_for_ptr(ptr);
165 unlock_and_check_header(header);
166
167 munmap(header->mmap_base, header->mmap_size);
168}
169
170void *realloc(void *old_ptr, size_t new_size) {
171 size_t size_to_copy;
172 if (old_ptr == NULL) {
173 size_to_copy = 0;
174 } else {
175 struct gmalloc_header *header = header_for_ptr(old_ptr);
176 unlock_and_check_header(header);
177 size_to_copy = header->size;
178 }
179 if (size_to_copy > new_size) {
180 size_to_copy = new_size;
181 }
182
183 void *new_ptr = malloc(new_size);
184 if (new_ptr == NULL) {
185 return NULL;
186 }
187 memcpy(new_ptr, old_ptr, size_to_copy);
188 free(old_ptr);
189
190 return new_ptr;
191}
192
193void *reallocarray(void *old_ptr, size_t num, size_t size) __DARWIN_EXTSN(reallocarray);
194void *reallocarray(void *old_ptr, size_t num, size_t size) {
195 size_t new_size;
196 if (os_mul_overflow(num, size, &new_size)) {
197 errno = ENOMEM;
198 return NULL;
199 }
200 return realloc(old_ptr, new_size);
201}
202
203void *reallocarrayf(void *old_ptr, size_t num, size_t size) __DARWIN_EXTSN(reallocarrayf);
204void *reallocarrayf(void *old_ptr, size_t num, size_t size) {
205 size_t new_size;
206 if (os_mul_overflow(num, size, &new_size)) {
207 errno = ENOMEM;
208 return NULL;
209 }
210 void *new_ptr = realloc(old_ptr, new_size);
211 if (new_ptr == NULL) {
212 free(old_ptr);
213 }
214 return new_ptr;
215}
216
217// Zone versions
218typedef void malloc_zone_t;
219void *malloc_zone_malloc(malloc_zone_t *zone, size_t size) {
220 (void) zone;
221 return malloc(size);
222}
223
224void *malloc_zone_calloc(malloc_zone_t *zone, size_t num, size_t size) {
225 (void) zone;
226 return calloc(num, size);
227}
228
229void *malloc_zone_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
230 (void) zone;
231 return realloc(ptr, size);
232}
233
234void *malloc_zone_memalign(malloc_zone_t *zone, size_t align, size_t size) {
235 (void) zone;
236 void *ptr;
237 posix_memalign(&ptr, align, size);
238 return ptr;
239}
240
241void malloc_zone_free(malloc_zone_t *zone, void *ptr) {
242 (void) zone;
243 free(ptr);
244}