diff options
author | otto <> | 2008-07-28 04:56:38 +0000 |
---|---|---|
committer | otto <> | 2008-07-28 04:56:38 +0000 |
commit | b27d6e846482338e09c058638005f2b0c06b1832 (patch) | |
tree | 5414fccbabb7e78e967120c9a6cd8fe4965a07b3 | |
parent | 05b581861619291083a9b67ba66338b4a17535c8 (diff) | |
download | openbsd-b27d6e846482338e09c058638005f2b0c06b1832.tar.gz openbsd-b27d6e846482338e09c058638005f2b0c06b1832.tar.bz2 openbsd-b27d6e846482338e09c058638005f2b0c06b1832.zip |
Almost complete rewrite of malloc, to have a more efficient data
structure of tracking pages returned by mmap(). Lots of testing by
lots of people, thanks to you all.
ok djm@ (for a slighly earlier version) deraadt@
-rw-r--r-- | src/lib/libc/stdlib/malloc.c | 2278 |
1 files changed, 835 insertions, 1443 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index 6042e47e69..ae8cd8157b 100644 --- a/src/lib/libc/stdlib/malloc.c +++ b/src/lib/libc/stdlib/malloc.c | |||
@@ -1,471 +1,324 @@ | |||
1 | /* $OpenBSD: malloc.c,v 1.91 2008/06/13 21:18:42 otto Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.92 2008/07/28 04:56:38 otto Exp $ */ |
2 | /* | ||
3 | * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
2 | 17 | ||
3 | /* | 18 | /* |
19 | * Parts of this code, mainly the sub page sized chunk management code is | ||
20 | * derived from the malloc implementation with the following license: | ||
21 | */ | ||
22 | /* | ||
4 | * ---------------------------------------------------------------------------- | 23 | * ---------------------------------------------------------------------------- |
5 | * "THE BEER-WARE LICENSE" (Revision 42): | 24 | * "THE BEER-WARE LICENSE" (Revision 42): |
6 | * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you | 25 | * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you |
7 | * can do whatever you want with this stuff. If we meet some day, and you think | 26 | * can do whatever you want with this stuff. If we meet some day, and you think |
8 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp | 27 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp |
9 | * ---------------------------------------------------------------------------- | 28 | * ---------------------------------------------------------------------------- |
10 | */ | 29 | */ |
11 | 30 | ||
12 | /* | 31 | /* #define MALLOC_STATS */ |
13 | * Defining MALLOC_EXTRA_SANITY will enable extra checks which are | ||
14 | * related to internal conditions and consistency in malloc.c. This has | ||
15 | * a noticeable runtime performance hit, and generally will not do you | ||
16 | * any good unless you fiddle with the internals of malloc or want | ||
17 | * to catch random pointer corruption as early as possible. | ||
18 | */ | ||
19 | #ifndef MALLOC_EXTRA_SANITY | ||
20 | #undef MALLOC_EXTRA_SANITY | ||
21 | #endif | ||
22 | |||
23 | /* | ||
24 | * Defining MALLOC_STATS will enable you to call malloc_dump() and set | ||
25 | * the [dD] options in the MALLOC_OPTIONS environment variable. | ||
26 | * It has no run-time performance hit, but does pull in stdio... | ||
27 | */ | ||
28 | #ifndef MALLOC_STATS | ||
29 | #undef MALLOC_STATS | ||
30 | #endif | ||
31 | |||
32 | /* | ||
33 | * What to use for Junk. This is the byte value we use to fill with | ||
34 | * when the 'J' option is enabled. | ||
35 | */ | ||
36 | #define SOME_JUNK 0xd0 /* as in "Duh" :-) */ | ||
37 | 32 | ||
38 | #include <sys/types.h> | 33 | #include <sys/types.h> |
39 | #include <sys/time.h> | ||
40 | #include <sys/resource.h> | ||
41 | #include <sys/param.h> | 34 | #include <sys/param.h> |
42 | #include <sys/mman.h> | 35 | #include <sys/mman.h> |
43 | #include <sys/uio.h> | 36 | #include <sys/uio.h> |
44 | #include <stdio.h> | 37 | #include <errno.h> |
38 | #include <stdint.h> | ||
45 | #include <stdlib.h> | 39 | #include <stdlib.h> |
46 | #include <string.h> | 40 | #include <string.h> |
41 | #include <stdio.h> | ||
47 | #include <unistd.h> | 42 | #include <unistd.h> |
43 | |||
44 | #ifdef MALLOC_STATS | ||
48 | #include <fcntl.h> | 45 | #include <fcntl.h> |
49 | #include <limits.h> | 46 | #endif |
50 | #include <errno.h> | ||
51 | #include <err.h> | ||
52 | 47 | ||
53 | #include "thread_private.h" | 48 | #include "thread_private.h" |
54 | 49 | ||
55 | /* | 50 | #define MALLOC_MINSHIFT 4 |
56 | * The basic parameters you can tweak. | 51 | #define MALLOC_MAXSHIFT 16 |
57 | * | ||
58 | * malloc_pageshift pagesize = 1 << malloc_pageshift | ||
59 | * It's probably best if this is the native | ||
60 | * page size, but it shouldn't have to be. | ||
61 | * | ||
62 | * malloc_minsize minimum size of an allocation in bytes. | ||
63 | * If this is too small it's too much work | ||
64 | * to manage them. This is also the smallest | ||
65 | * unit of alignment used for the storage | ||
66 | * returned by malloc/realloc. | ||
67 | * | ||
68 | */ | ||
69 | |||
70 | #if defined(__sparc__) | ||
71 | #define malloc_pageshift 13U | ||
72 | #endif /* __sparc__ */ | ||
73 | |||
74 | #ifndef malloc_pageshift | ||
75 | #define malloc_pageshift (PGSHIFT) | ||
76 | #endif | ||
77 | 52 | ||
78 | #ifndef malloc_minsize | 53 | #if defined(__sparc__) && !defined(__sparcv9__) |
79 | #define malloc_minsize 16UL | 54 | #define MALLOC_PAGESHIFT (13U) |
55 | #else | ||
56 | #define MALLOC_PAGESHIFT (PGSHIFT) | ||
80 | #endif | 57 | #endif |
81 | 58 | ||
82 | #if !defined(malloc_pagesize) | 59 | #define MALLOC_PAGESIZE (1UL << MALLOC_PAGESHIFT) |
83 | #define malloc_pagesize (1UL<<malloc_pageshift) | 60 | #define MALLOC_MINSIZE (1UL << MALLOC_MINSHIFT) |
84 | #endif | 61 | #define MALLOC_PAGEMASK (MALLOC_PAGESIZE - 1) |
62 | #define MASK_POINTER(p) ((void *)(((uintptr_t)(p)) & ~MALLOC_PAGEMASK)) | ||
85 | 63 | ||
86 | /* How many bits per u_long in the bitmap */ | 64 | #define MALLOC_MAXCHUNK (1 << (MALLOC_PAGESHIFT-1)) |
87 | #define MALLOC_BITS (NBBY * sizeof(u_long)) | 65 | #define MALLOC_MAXCACHE 256 |
66 | #define MALLOC_DELAYED_CHUNKS 16 /* should be power of 2 */ | ||
88 | 67 | ||
68 | #define PAGEROUND(x) (((x) + (MALLOC_PAGEMASK)) & ~MALLOC_PAGEMASK) | ||
89 | 69 | ||
90 | /* | 70 | /* |
91 | * No user serviceable parts behind this point. | 71 | * What to use for Junk. This is the byte value we use to fill with |
92 | * | 72 | * when the 'J' option is enabled. Use SOME_JUNK right after alloc, |
93 | * This structure describes a page worth of chunks. | 73 | * and SOME_FREEJUNK right before free. |
94 | */ | ||
95 | struct pginfo { | ||
96 | struct pginfo *next; /* next on the free list */ | ||
97 | void *page; /* Pointer to the page */ | ||
98 | u_short size; /* size of this page's chunks */ | ||
99 | u_short shift; /* How far to shift for this size chunks */ | ||
100 | u_short free; /* How many free chunks */ | ||
101 | u_short total; /* How many chunk */ | ||
102 | u_long bits[(malloc_pagesize / malloc_minsize) / MALLOC_BITS];/* Which chunks are free */ | ||
103 | }; | ||
104 | |||
105 | /* | ||
106 | * This structure describes a number of free pages. | ||
107 | */ | ||
108 | struct pgfree { | ||
109 | struct pgfree *next; /* next run of free pages */ | ||
110 | struct pgfree *prev; /* prev run of free pages */ | ||
111 | void *page; /* pointer to free pages */ | ||
112 | void *pdir; /* pointer to the base page's dir */ | ||
113 | size_t size; /* number of bytes free */ | ||
114 | }; | ||
115 | |||
116 | /* | ||
117 | * Magic values to put in the page_directory | ||
118 | */ | 74 | */ |
119 | #define MALLOC_NOT_MINE ((struct pginfo*) 0) | 75 | #define SOME_JUNK 0xd0 /* as in "Duh" :-) */ |
120 | #define MALLOC_FREE ((struct pginfo*) 1) | 76 | #define SOME_FREEJUNK 0xdf |
121 | #define MALLOC_FIRST ((struct pginfo*) 2) | ||
122 | #define MALLOC_FOLLOW ((struct pginfo*) 3) | ||
123 | #define MALLOC_MAGIC ((struct pginfo*) 4) | ||
124 | |||
125 | #if ((1UL<<malloc_pageshift) != malloc_pagesize) | ||
126 | #error "(1UL<<malloc_pageshift) != malloc_pagesize" | ||
127 | #endif | ||
128 | |||
129 | #ifndef malloc_maxsize | ||
130 | #define malloc_maxsize ((malloc_pagesize)>>1) | ||
131 | #endif | ||
132 | |||
133 | /* A mask for the offset inside a page. */ | ||
134 | #define malloc_pagemask ((malloc_pagesize)-1) | ||
135 | |||
136 | #define pageround(foo) (((foo) + (malloc_pagemask)) & ~malloc_pagemask) | ||
137 | #define ptr2index(foo) (((u_long)(foo) >> malloc_pageshift)+malloc_pageshift) | ||
138 | #define index2ptr(idx) ((void*)(((idx)-malloc_pageshift)<<malloc_pageshift)) | ||
139 | 77 | ||
140 | /* Set when initialization has been done */ | 78 | #define MMAP(sz) mmap(NULL, (size_t)(sz), PROT_READ | PROT_WRITE, \ |
141 | static unsigned int malloc_started; | 79 | MAP_ANON | MAP_PRIVATE, -1, (off_t) 0) |
142 | 80 | ||
143 | /* Number of free pages we cache */ | 81 | struct region_info { |
144 | static unsigned int malloc_cache = 16; | 82 | void *p; /* page; low bits used to mark chunks */ |
145 | 83 | uintptr_t size; /* size for pages, or chunk_info pointer */ | |
146 | /* Structure used for linking discrete directory pages. */ | ||
147 | struct pdinfo { | ||
148 | struct pginfo **base; | ||
149 | struct pdinfo *prev; | ||
150 | struct pdinfo *next; | ||
151 | u_long dirnum; | ||
152 | }; | 84 | }; |
153 | static struct pdinfo *last_dir; /* Caches to the last and previous */ | ||
154 | static struct pdinfo *prev_dir; /* referenced directory pages. */ | ||
155 | |||
156 | static size_t pdi_off; | ||
157 | static u_long pdi_mod; | ||
158 | #define PD_IDX(num) ((num) / (malloc_pagesize/sizeof(struct pginfo *))) | ||
159 | #define PD_OFF(num) ((num) & ((malloc_pagesize/sizeof(struct pginfo *))-1)) | ||
160 | #define PI_IDX(index) ((index) / pdi_mod) | ||
161 | #define PI_OFF(index) ((index) % pdi_mod) | ||
162 | |||
163 | /* The last index in the page directory we care about */ | ||
164 | static u_long last_index; | ||
165 | |||
166 | /* Pointer to page directory. Allocated "as if with" malloc */ | ||
167 | static struct pginfo **page_dir; | ||
168 | |||
169 | /* Free pages line up here */ | ||
170 | static struct pgfree free_list; | ||
171 | |||
172 | /* Abort(), user doesn't handle problems. */ | ||
173 | static int malloc_abort = 2; | ||
174 | |||
175 | /* Are we trying to die ? */ | ||
176 | static int suicide; | ||
177 | 85 | ||
86 | struct dir_info { | ||
87 | u_int32_t canary1; | ||
88 | struct region_info *r; /* region slots */ | ||
89 | size_t regions_total; /* number of region slots */ | ||
90 | size_t regions_bits; /* log2 of total */ | ||
91 | size_t regions_free; /* number of free slots */ | ||
92 | /* list of free chunk info structs */ | ||
93 | struct chunk_info *chunk_info_list; | ||
94 | /* lists of chunks with free slots */ | ||
95 | struct chunk_info *chunk_dir[MALLOC_MAXSHIFT]; | ||
96 | size_t free_regions_size; /* free pages cached */ | ||
97 | /* free pages cache */ | ||
98 | struct region_info free_regions[MALLOC_MAXCACHE]; | ||
99 | /* delayed free chunk slots */ | ||
100 | void *delayed_chunks[MALLOC_DELAYED_CHUNKS]; | ||
178 | #ifdef MALLOC_STATS | 101 | #ifdef MALLOC_STATS |
179 | /* dump statistics */ | 102 | size_t inserts; |
180 | static int malloc_stats; | 103 | size_t insert_collisions; |
181 | #endif | 104 | size_t finds; |
182 | 105 | size_t find_collisions; | |
183 | /* avoid outputting warnings? */ | 106 | size_t deletes; |
184 | static int malloc_silent; | 107 | size_t delete_moves; |
185 | 108 | #define STATS_INC(x) ((x)++) | |
186 | /* always realloc ? */ | 109 | #define STATS_ZERO(x) ((x) = 0) |
187 | static int malloc_realloc; | 110 | #else |
188 | 111 | #define STATS_INC(x) /* nothing */ | |
189 | /* mprotect free pages PROT_NONE? */ | 112 | #define STATS_ZERO(x) /* nothing */ |
190 | static int malloc_freeprot; | 113 | #endif /* MALLOC_STATS */ |
191 | 114 | u_int32_t canary2; | |
192 | /* use guard pages after allocations? */ | 115 | }; |
193 | static size_t malloc_guard = 0; | ||
194 | static size_t malloc_guarded; | ||
195 | /* align pointers to end of page? */ | ||
196 | static int malloc_ptrguard; | ||
197 | |||
198 | static int malloc_hint; | ||
199 | |||
200 | /* xmalloc behaviour ? */ | ||
201 | static int malloc_xmalloc; | ||
202 | |||
203 | /* zero fill ? */ | ||
204 | static int malloc_zero; | ||
205 | |||
206 | /* junk fill ? */ | ||
207 | static int malloc_junk; | ||
208 | 116 | ||
209 | #ifdef __FreeBSD__ | ||
210 | /* utrace ? */ | ||
211 | static int malloc_utrace; | ||
212 | 117 | ||
213 | struct ut { | 118 | /* |
214 | void *p; | 119 | * This structure describes a page worth of chunks. |
215 | size_t s; | 120 | * |
216 | void *r; | 121 | * How many bits per u_long in the bitmap |
122 | */ | ||
123 | #define MALLOC_BITS (NBBY * sizeof(u_long)) | ||
124 | struct chunk_info { | ||
125 | struct chunk_info *next; /* next on the free list */ | ||
126 | void *page; /* pointer to the page */ | ||
127 | u_int32_t canary; | ||
128 | u_short size; /* size of this page's chunks */ | ||
129 | u_short shift; /* how far to shift for this size */ | ||
130 | u_short free; /* how many free chunks */ | ||
131 | u_short total; /* how many chunk */ | ||
132 | /* which chunks are free */ | ||
133 | u_long bits[(MALLOC_PAGESIZE / MALLOC_MINSIZE) / MALLOC_BITS]; | ||
217 | }; | 134 | }; |
218 | 135 | ||
219 | void utrace(struct ut *, int); | 136 | static struct dir_info g_pool; |
137 | static char *malloc_func; /* current function */ | ||
138 | char *malloc_options; /* compile-time options */ | ||
139 | |||
140 | static int malloc_abort = 1; /* abort() on error */ | ||
141 | static int malloc_active; /* status of malloc */ | ||
142 | static int malloc_freeprot; /* mprotect free pages PROT_NONE? */ | ||
143 | static int malloc_hint; /* call madvice on free pages? */ | ||
144 | static int malloc_junk; /* junk fill? */ | ||
145 | static int malloc_move; /* move allocations to end of page? */ | ||
146 | static int malloc_realloc; /* always realloc? */ | ||
147 | static int malloc_silent; /* avoid outputting warnings? */ | ||
148 | static int malloc_xmalloc; /* xmalloc behaviour? */ | ||
149 | static int malloc_zero; /* zero fill? */ | ||
150 | static size_t malloc_guard; /* use guard pages after allocations? */ | ||
151 | |||
152 | static u_int malloc_cache = 64; /* free pages we cache */ | ||
153 | static size_t malloc_guarded; /* bytes used for guards */ | ||
154 | static size_t malloc_used; /* bytes allocated */ | ||
220 | 155 | ||
221 | #define UTRACE(a, b, c) \ | 156 | #ifdef MALLOC_STATS |
222 | if (malloc_utrace) \ | 157 | static int malloc_stats; /* dump statistics at end */ |
223 | {struct ut u; u.p=a; u.s = b; u.r=c; utrace(&u, sizeof u);} | ||
224 | #else /* !__FreeBSD__ */ | ||
225 | #define UTRACE(a,b,c) | ||
226 | #endif | 158 | #endif |
227 | 159 | ||
228 | /* Status of malloc. */ | 160 | static size_t rbytesused; /* random bytes used */ |
229 | static int malloc_active; | 161 | static u_char rbytes[4096]; /* random bytes */ |
230 | 162 | static u_char getrbyte(void); | |
231 | /* Allocated memory. */ | ||
232 | static size_t malloc_used; | ||
233 | |||
234 | /* My last break. */ | ||
235 | static caddr_t malloc_brk; | ||
236 | 163 | ||
237 | /* One location cache for free-list holders. */ | 164 | extern char *__progname; |
238 | static struct pgfree *px; | ||
239 | |||
240 | /* Compile-time options. */ | ||
241 | char *malloc_options; | ||
242 | |||
243 | /* Name of the current public function. */ | ||
244 | static char *malloc_func; | ||
245 | |||
246 | #define MMAP(size) \ | ||
247 | mmap((void *)0, (size), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, \ | ||
248 | -1, (off_t)0) | ||
249 | 165 | ||
250 | /* | 166 | /* low bits of r->p determine size: 0 means >= page size and p->size holding |
251 | * Necessary function declarations. | 167 | * real size, otherwise r->size is a shift count, or 1 for malloc(0) |
252 | */ | 168 | */ |
253 | static void *imalloc(size_t size); | 169 | #define REALSIZE(sz, r) \ |
254 | static void ifree(void *ptr); | 170 | (sz) = (uintptr_t)(r)->p & MALLOC_PAGEMASK, \ |
255 | static void *irealloc(void *ptr, size_t size); | 171 | (sz) = ((sz) == 0 ? (r)->size : ((sz) == 1 ? 0 : (1 << ((sz)-1)))) |
256 | static void *malloc_bytes(size_t size); | ||
257 | |||
258 | static struct pginfo *pginfo_list; | ||
259 | 172 | ||
260 | static struct pgfree *pgfree_list; | 173 | static inline size_t |
174 | hash(void *p) | ||
175 | { | ||
176 | size_t sum; | ||
177 | union { | ||
178 | uintptr_t p; | ||
179 | unsigned short a[sizeof(void *) / sizeof(short)]; | ||
180 | } u; | ||
181 | u.p = (uintptr_t)p >> MALLOC_PAGESHIFT; | ||
182 | sum = u.a[0]; | ||
183 | sum = (sum << 7) - sum + u.a[1]; | ||
184 | #ifdef __LP64__ | ||
185 | sum = (sum << 7) - sum + u.a[2]; | ||
186 | sum = (sum << 7) - sum + u.a[3]; | ||
187 | #endif | ||
188 | return sum; | ||
189 | } | ||
261 | 190 | ||
262 | static struct pgfree * | 191 | #ifdef MALLOC_STATS |
263 | alloc_pgfree(void) | 192 | static void |
193 | dump_chunk(int fd, struct chunk_info *p, int fromfreelist) | ||
264 | { | 194 | { |
265 | struct pgfree *p; | 195 | char buf[64]; |
266 | int i; | ||
267 | 196 | ||
268 | if (pgfree_list == NULL) { | 197 | while (p) { |
269 | p = MMAP(malloc_pagesize); | 198 | snprintf(buf, sizeof(buf), "chunk %d %d/%d %p\n", p->size, |
270 | if (p == MAP_FAILED) | 199 | p->free, p->total, p->page); |
271 | return NULL; | 200 | write(fd, buf, strlen(buf)); |
272 | for (i = 0; i < malloc_pagesize / sizeof(*p); i++) { | 201 | if (!fromfreelist) |
273 | p[i].next = pgfree_list; | 202 | break; |
274 | pgfree_list = &p[i]; | 203 | p = p->next; |
204 | if (p != NULL) { | ||
205 | snprintf(buf, sizeof(buf), " "); | ||
206 | write(fd, buf, strlen(buf)); | ||
275 | } | 207 | } |
276 | } | 208 | } |
277 | p = pgfree_list; | ||
278 | pgfree_list = p->next; | ||
279 | memset(p, 0, sizeof *p); | ||
280 | return p; | ||
281 | } | 209 | } |
282 | 210 | ||
283 | static struct pginfo * | 211 | static void |
284 | alloc_pginfo(void) | 212 | dump_free_chunk_info(int fd, struct dir_info *d) |
285 | { | 213 | { |
286 | struct pginfo *p; | 214 | char buf[64]; |
287 | int i; | 215 | int i; |
288 | 216 | ||
289 | if (pginfo_list == NULL) { | 217 | snprintf(buf, sizeof(buf), "Free chunk structs:\n"); |
290 | p = MMAP(malloc_pagesize); | 218 | write(fd, buf, strlen(buf)); |
291 | if (p == MAP_FAILED) | 219 | for (i = 0; i < MALLOC_MAXSHIFT; i++) { |
292 | return NULL; | 220 | struct chunk_info *p = d->chunk_dir[i]; |
293 | for (i = 0; i < malloc_pagesize / sizeof(*p); i++) { | 221 | if (p != NULL) { |
294 | p[i].next = pginfo_list; | 222 | snprintf(buf, sizeof(buf), "%2d) ", i); |
295 | pginfo_list = &p[i]; | 223 | write(fd, buf, strlen(buf)); |
224 | dump_chunk(fd, p, 1); | ||
296 | } | 225 | } |
297 | } | 226 | } |
298 | p = pginfo_list; | ||
299 | pginfo_list = p->next; | ||
300 | memset(p, 0, sizeof *p); | ||
301 | return p; | ||
302 | } | ||
303 | 227 | ||
304 | static void | ||
305 | put_pgfree(struct pgfree *p) | ||
306 | { | ||
307 | p->next = pgfree_list; | ||
308 | pgfree_list = p; | ||
309 | } | 228 | } |
310 | 229 | ||
311 | static void | 230 | static void |
312 | put_pginfo(struct pginfo *p) | 231 | dump_free_page_info(int fd, struct dir_info *d) |
313 | { | ||
314 | p->next = pginfo_list; | ||
315 | pginfo_list = p; | ||
316 | } | ||
317 | |||
318 | /* | ||
319 | * Function for page directory lookup. | ||
320 | */ | ||
321 | static int | ||
322 | pdir_lookup(u_long index, struct pdinfo ** pdi) | ||
323 | { | 232 | { |
324 | struct pdinfo *spi; | 233 | char buf[64]; |
325 | u_long pidx = PI_IDX(index); | 234 | int i; |
326 | |||
327 | if (last_dir != NULL && PD_IDX(last_dir->dirnum) == pidx) | ||
328 | *pdi = last_dir; | ||
329 | else if (prev_dir != NULL && PD_IDX(prev_dir->dirnum) == pidx) | ||
330 | *pdi = prev_dir; | ||
331 | else if (last_dir != NULL && prev_dir != NULL) { | ||
332 | if ((PD_IDX(last_dir->dirnum) > pidx) ? | ||
333 | (PD_IDX(last_dir->dirnum) - pidx) : | ||
334 | (pidx - PD_IDX(last_dir->dirnum)) | ||
335 | < (PD_IDX(prev_dir->dirnum) > pidx) ? | ||
336 | (PD_IDX(prev_dir->dirnum) - pidx) : | ||
337 | (pidx - PD_IDX(prev_dir->dirnum))) | ||
338 | *pdi = last_dir; | ||
339 | else | ||
340 | *pdi = prev_dir; | ||
341 | |||
342 | if (PD_IDX((*pdi)->dirnum) > pidx) { | ||
343 | for (spi = (*pdi)->prev; | ||
344 | spi != NULL && PD_IDX(spi->dirnum) > pidx; | ||
345 | spi = spi->prev) | ||
346 | *pdi = spi; | ||
347 | if (spi != NULL) | ||
348 | *pdi = spi; | ||
349 | } else | ||
350 | for (spi = (*pdi)->next; | ||
351 | spi != NULL && PD_IDX(spi->dirnum) <= pidx; | ||
352 | spi = spi->next) | ||
353 | *pdi = spi; | ||
354 | } else { | ||
355 | *pdi = (struct pdinfo *) ((caddr_t) page_dir + pdi_off); | ||
356 | for (spi = *pdi; | ||
357 | spi != NULL && PD_IDX(spi->dirnum) <= pidx; | ||
358 | spi = spi->next) | ||
359 | *pdi = spi; | ||
360 | } | ||
361 | |||
362 | return ((PD_IDX((*pdi)->dirnum) == pidx) ? 0 : | ||
363 | (PD_IDX((*pdi)->dirnum) > pidx) ? 1 : -1); | ||
364 | } | ||
365 | 235 | ||
366 | #ifdef MALLOC_STATS | 236 | snprintf(buf, sizeof(buf), "Free pages cached: %zu\n", |
367 | void | 237 | d->free_regions_size); |
368 | malloc_dump(int fd) | 238 | write(fd, buf, strlen(buf)); |
369 | { | 239 | for (i = 0; i < malloc_cache; i++) { |
370 | char buf[1024]; | 240 | if (d->free_regions[i].p != NULL) { |
371 | struct pginfo **pd; | 241 | snprintf(buf, sizeof(buf), "%2d) ", i); |
372 | struct pgfree *pf; | ||
373 | struct pdinfo *pi; | ||
374 | u_long j; | ||
375 | |||
376 | pd = page_dir; | ||
377 | pi = (struct pdinfo *) ((caddr_t) pd + pdi_off); | ||
378 | |||
379 | /* print out all the pages */ | ||
380 | for (j = 0; j <= last_index;) { | ||
381 | snprintf(buf, sizeof buf, "%08lx %5lu ", j << malloc_pageshift, j); | ||
382 | write(fd, buf, strlen(buf)); | ||
383 | if (pd[PI_OFF(j)] == MALLOC_NOT_MINE) { | ||
384 | for (j++; j <= last_index && pd[PI_OFF(j)] == MALLOC_NOT_MINE;) { | ||
385 | if (!PI_OFF(++j)) { | ||
386 | if ((pi = pi->next) == NULL || | ||
387 | PD_IDX(pi->dirnum) != PI_IDX(j)) | ||
388 | break; | ||
389 | pd = pi->base; | ||
390 | j += pdi_mod; | ||
391 | } | ||
392 | } | ||
393 | j--; | ||
394 | snprintf(buf, sizeof buf, ".. %5lu not mine\n", j); | ||
395 | write(fd, buf, strlen(buf)); | ||
396 | } else if (pd[PI_OFF(j)] == MALLOC_FREE) { | ||
397 | for (j++; j <= last_index && pd[PI_OFF(j)] == MALLOC_FREE;) { | ||
398 | if (!PI_OFF(++j)) { | ||
399 | if ((pi = pi->next) == NULL || | ||
400 | PD_IDX(pi->dirnum) != PI_IDX(j)) | ||
401 | break; | ||
402 | pd = pi->base; | ||
403 | j += pdi_mod; | ||
404 | } | ||
405 | } | ||
406 | j--; | ||
407 | snprintf(buf, sizeof buf, ".. %5lu free\n", j); | ||
408 | write(fd, buf, strlen(buf)); | ||
409 | } else if (pd[PI_OFF(j)] == MALLOC_FIRST) { | ||
410 | for (j++; j <= last_index && pd[PI_OFF(j)] == MALLOC_FOLLOW;) { | ||
411 | if (!PI_OFF(++j)) { | ||
412 | if ((pi = pi->next) == NULL || | ||
413 | PD_IDX(pi->dirnum) != PI_IDX(j)) | ||
414 | break; | ||
415 | pd = pi->base; | ||
416 | j += pdi_mod; | ||
417 | } | ||
418 | } | ||
419 | j--; | ||
420 | snprintf(buf, sizeof buf, ".. %5lu in use\n", j); | ||
421 | write(fd, buf, strlen(buf)); | ||
422 | } else if (pd[PI_OFF(j)] < MALLOC_MAGIC) { | ||
423 | snprintf(buf, sizeof buf, "(%p)\n", pd[PI_OFF(j)]); | ||
424 | write(fd, buf, strlen(buf)); | 242 | write(fd, buf, strlen(buf)); |
425 | } else { | 243 | snprintf(buf, sizeof(buf), "free at %p: %zu\n", |
426 | snprintf(buf, sizeof buf, "%p %d (of %d) x %d @ %p --> %p\n", | 244 | d->free_regions[i].p, d->free_regions[i].size); |
427 | pd[PI_OFF(j)], pd[PI_OFF(j)]->free, | ||
428 | pd[PI_OFF(j)]->total, pd[PI_OFF(j)]->size, | ||
429 | pd[PI_OFF(j)]->page, pd[PI_OFF(j)]->next); | ||
430 | write(fd, buf, strlen(buf)); | 245 | write(fd, buf, strlen(buf)); |
431 | } | 246 | } |
432 | if (!PI_OFF(++j)) { | ||
433 | if ((pi = pi->next) == NULL) | ||
434 | break; | ||
435 | pd = pi->base; | ||
436 | j += (1 + PD_IDX(pi->dirnum) - PI_IDX(j)) * pdi_mod; | ||
437 | } | ||
438 | } | 247 | } |
248 | } | ||
439 | 249 | ||
440 | for (pf = free_list.next; pf; pf = pf->next) { | 250 | static void |
441 | snprintf(buf, sizeof buf, "Free: @%p [%p...%p[ %ld ->%p <-%p\n", | 251 | malloc_dump1(int fd, struct dir_info *d) |
442 | pf, pf->page, (char *)pf->page + pf->size, | 252 | { |
443 | pf->size, pf->prev, pf->next); | 253 | char buf[64]; |
444 | write(fd, buf, strlen(buf)); | 254 | size_t i, realsize; |
445 | if (pf == pf->next) { | ||
446 | snprintf(buf, sizeof buf, "Free_list loops\n"); | ||
447 | write(fd, buf, strlen(buf)); | ||
448 | break; | ||
449 | } | ||
450 | } | ||
451 | 255 | ||
452 | /* print out various info */ | 256 | snprintf(buf, sizeof(buf), "Malloc dir of %s at %p\n", __progname, d); |
453 | snprintf(buf, sizeof buf, "Minsize\t%lu\n", malloc_minsize); | ||
454 | write(fd, buf, strlen(buf)); | 257 | write(fd, buf, strlen(buf)); |
455 | snprintf(buf, sizeof buf, "Maxsize\t%lu\n", malloc_maxsize); | 258 | snprintf(buf, sizeof(buf), "Regions slots %zu\n", d->regions_total); |
456 | write(fd, buf, strlen(buf)); | 259 | write(fd, buf, strlen(buf)); |
457 | snprintf(buf, sizeof buf, "Pagesize\t%lu\n", malloc_pagesize); | 260 | snprintf(buf, sizeof(buf), "Finds %zu/%zu %f\n", d->finds, |
261 | d->find_collisions, | ||
262 | 1.0 + (double)d->find_collisions / d->finds); | ||
458 | write(fd, buf, strlen(buf)); | 263 | write(fd, buf, strlen(buf)); |
459 | snprintf(buf, sizeof buf, "Pageshift\t%u\n", malloc_pageshift); | 264 | snprintf(buf, sizeof(buf), "Inserts %zu/%zu %f\n", d->inserts, |
265 | d->insert_collisions, | ||
266 | 1.0 + (double)d->insert_collisions / d->inserts); | ||
460 | write(fd, buf, strlen(buf)); | 267 | write(fd, buf, strlen(buf)); |
461 | snprintf(buf, sizeof buf, "In use\t%lu\n", (u_long) malloc_used); | 268 | snprintf(buf, sizeof(buf), "Deletes %zu/%zu\n", d->deletes, |
269 | d->delete_moves); | ||
462 | write(fd, buf, strlen(buf)); | 270 | write(fd, buf, strlen(buf)); |
463 | snprintf(buf, sizeof buf, "Guarded\t%lu\n", (u_long) malloc_guarded); | 271 | snprintf(buf, sizeof(buf), "Regions slots free %zu\n", d->regions_free); |
272 | write(fd, buf, strlen(buf)); | ||
273 | for (i = 0; i < d->regions_total; i++) { | ||
274 | if (d->r[i].p != NULL) { | ||
275 | size_t h = hash(d->r[i].p) & | ||
276 | (d->regions_total - 1); | ||
277 | snprintf(buf, sizeof(buf), "%4zx) #%zx %zd ", | ||
278 | i, h, h - i); | ||
279 | write(fd, buf, strlen(buf)); | ||
280 | REALSIZE(realsize, &d->r[i]); | ||
281 | if (realsize > MALLOC_MAXCHUNK) { | ||
282 | snprintf(buf, sizeof(buf), | ||
283 | "%p: %zu\n", d->r[i].p, realsize); | ||
284 | write(fd, buf, strlen(buf)); | ||
285 | } else | ||
286 | dump_chunk(fd, | ||
287 | (struct chunk_info *)d->r[i].size, 0); | ||
288 | } | ||
289 | } | ||
290 | dump_free_chunk_info(fd, d); | ||
291 | dump_free_page_info(fd, d); | ||
292 | snprintf(buf, sizeof(buf), "In use %zu\n", malloc_used); | ||
293 | write(fd, buf, strlen(buf)); | ||
294 | snprintf(buf, sizeof(buf), "Guarded %zu\n", malloc_guarded); | ||
464 | write(fd, buf, strlen(buf)); | 295 | write(fd, buf, strlen(buf)); |
465 | } | 296 | } |
297 | |||
298 | |||
299 | void | ||
300 | malloc_dump(int fd) | ||
301 | { | ||
302 | malloc_dump1(fd, &g_pool); | ||
303 | } | ||
304 | |||
305 | static void | ||
306 | malloc_exit(void) | ||
307 | { | ||
308 | char *q = "malloc() warning: Couldn't dump stats\n"; | ||
309 | int save_errno = errno, fd; | ||
310 | |||
311 | fd = open("malloc.out", O_RDWR|O_APPEND); | ||
312 | if (fd != -1) { | ||
313 | malloc_dump(fd); | ||
314 | close(fd); | ||
315 | } else | ||
316 | write(STDERR_FILENO, q, strlen(q)); | ||
317 | errno = save_errno; | ||
318 | } | ||
466 | #endif /* MALLOC_STATS */ | 319 | #endif /* MALLOC_STATS */ |
467 | 320 | ||
468 | extern char *__progname; | 321 | |
469 | 322 | ||
470 | static void | 323 | static void |
471 | wrterror(char *p) | 324 | wrterror(char *p) |
@@ -485,12 +338,11 @@ wrterror(char *p) | |||
485 | iov[4].iov_len = 1; | 338 | iov[4].iov_len = 1; |
486 | writev(STDERR_FILENO, iov, 5); | 339 | writev(STDERR_FILENO, iov, 5); |
487 | 340 | ||
488 | suicide = 1; | ||
489 | #ifdef MALLOC_STATS | 341 | #ifdef MALLOC_STATS |
490 | if (malloc_stats) | 342 | if (malloc_stats) |
491 | malloc_dump(STDERR_FILENO); | 343 | malloc_dump(STDERR_FILENO); |
492 | #endif /* MALLOC_STATS */ | 344 | #endif /* MALLOC_STATS */ |
493 | malloc_active--; | 345 | //malloc_active--; |
494 | if (malloc_abort) | 346 | if (malloc_abort) |
495 | abort(); | 347 | abort(); |
496 | } | 348 | } |
@@ -520,145 +372,153 @@ wrtwarning(char *p) | |||
520 | writev(STDERR_FILENO, iov, 5); | 372 | writev(STDERR_FILENO, iov, 5); |
521 | } | 373 | } |
522 | 374 | ||
523 | #ifdef MALLOC_STATS | ||
524 | static void | ||
525 | malloc_exit(void) | ||
526 | { | ||
527 | char *q = "malloc() warning: Couldn't dump stats\n"; | ||
528 | int save_errno = errno, fd; | ||
529 | |||
530 | fd = open("malloc.out", O_RDWR|O_APPEND); | ||
531 | if (fd != -1) { | ||
532 | malloc_dump(fd); | ||
533 | close(fd); | ||
534 | } else | ||
535 | write(STDERR_FILENO, q, strlen(q)); | ||
536 | errno = save_errno; | ||
537 | } | ||
538 | #endif /* MALLOC_STATS */ | ||
539 | |||
540 | /* | 375 | /* |
541 | * Allocate a number of pages from the OS | 376 | * Cache maintenance. We keep at most malloc_cache pages cached. |
377 | * If the cache is becoming full, unmap pages in the cache for real, | ||
378 | * and then add the region to the cache | ||
379 | * Opposed to the regular region data structure, the sizes in the | ||
380 | * cache are in MALLOC_PAGESIZE units. | ||
542 | */ | 381 | */ |
543 | static void * | 382 | static void |
544 | map_pages(size_t pages) | 383 | unmap(struct dir_info *d, void *p, size_t sz) |
545 | { | 384 | { |
546 | struct pdinfo *pi, *spi; | 385 | size_t psz = PAGEROUND(sz) >> MALLOC_PAGESHIFT; |
547 | struct pginfo **pd; | 386 | size_t rsz, tounmap; |
548 | u_long idx, pidx, lidx; | 387 | struct region_info *r; |
549 | caddr_t result, tail; | 388 | u_int i, offset; |
550 | u_long index, lindex; | 389 | |
551 | void *pdregion = NULL; | 390 | if (psz > malloc_cache) { |
552 | size_t dirs, cnt; | 391 | if (munmap(p, sz)) |
553 | 392 | wrterror("unmap"); | |
554 | pages <<= malloc_pageshift; | 393 | malloc_used -= sz; |
555 | result = MMAP(pages + malloc_guard); | 394 | return; |
556 | if (result == MAP_FAILED) { | ||
557 | #ifdef MALLOC_EXTRA_SANITY | ||
558 | wrtwarning("(ES): map_pages fails"); | ||
559 | #endif /* MALLOC_EXTRA_SANITY */ | ||
560 | errno = ENOMEM; | ||
561 | return (NULL); | ||
562 | } | ||
563 | index = ptr2index(result); | ||
564 | tail = result + pages + malloc_guard; | ||
565 | lindex = ptr2index(tail) - 1; | ||
566 | if (malloc_guard) | ||
567 | mprotect(result + pages, malloc_guard, PROT_NONE); | ||
568 | |||
569 | pidx = PI_IDX(index); | ||
570 | lidx = PI_IDX(lindex); | ||
571 | |||
572 | if (tail > malloc_brk) { | ||
573 | malloc_brk = tail; | ||
574 | last_index = lindex; | ||
575 | } | ||
576 | |||
577 | dirs = lidx - pidx; | ||
578 | |||
579 | /* Insert directory pages, if needed. */ | ||
580 | if (pdir_lookup(index, &pi) != 0) | ||
581 | dirs++; | ||
582 | |||
583 | if (dirs > 0) { | ||
584 | pdregion = MMAP(malloc_pagesize * dirs); | ||
585 | if (pdregion == MAP_FAILED) { | ||
586 | munmap(result, tail - result); | ||
587 | #ifdef MALLOC_EXTRA_SANITY | ||
588 | wrtwarning("(ES): map_pages fails"); | ||
589 | #endif | ||
590 | errno = ENOMEM; | ||
591 | return (NULL); | ||
592 | } | ||
593 | } | 395 | } |
594 | 396 | tounmap = 0; | |
595 | cnt = 0; | 397 | rsz = malloc_cache - d->free_regions_size; |
596 | for (idx = pidx, spi = pi; idx <= lidx; idx++) { | 398 | if (psz > rsz) |
597 | if (pi == NULL || PD_IDX(pi->dirnum) != idx) { | 399 | tounmap = psz - rsz; |
598 | pd = (struct pginfo **)((char *)pdregion + | 400 | d->free_regions_size -= tounmap; |
599 | cnt * malloc_pagesize); | 401 | offset = getrbyte(); |
600 | cnt++; | 402 | for (i = 0; tounmap > 0 && i < malloc_cache; i++) { |
601 | memset(pd, 0, malloc_pagesize); | 403 | r = &d->free_regions[(i + offset) & (malloc_cache - 1)]; |
602 | pi = (struct pdinfo *) ((caddr_t) pd + pdi_off); | 404 | if (r->p != NULL) { |
603 | pi->base = pd; | 405 | if (r->size <= tounmap) { |
604 | pi->prev = spi; | 406 | rsz = r->size << MALLOC_PAGESHIFT; |
605 | pi->next = spi->next; | 407 | if (munmap(r->p, rsz)) |
606 | pi->dirnum = idx * (malloc_pagesize / | 408 | wrterror("munmap"); |
607 | sizeof(struct pginfo *)); | 409 | tounmap -= r->size; |
608 | 410 | r->p = NULL; | |
609 | if (spi->next != NULL) | 411 | r->size = 0; |
610 | spi->next->prev = pi; | 412 | malloc_used -= rsz; |
611 | spi->next = pi; | ||
612 | } | ||
613 | if (idx > pidx && idx < lidx) { | ||
614 | pi->dirnum += pdi_mod; | ||
615 | } else if (idx == pidx) { | ||
616 | if (pidx == lidx) { | ||
617 | pi->dirnum += (u_long)(tail - result) >> | ||
618 | malloc_pageshift; | ||
619 | } else { | 413 | } else { |
620 | pi->dirnum += pdi_mod - PI_OFF(index); | 414 | rsz = tounmap << MALLOC_PAGESHIFT; |
415 | if (munmap((char *)r->p + ((r->size - tounmap) | ||
416 | << MALLOC_PAGESHIFT), rsz)) | ||
417 | wrterror("munmap"); | ||
418 | r->size -= tounmap ; | ||
419 | tounmap = 0; | ||
420 | malloc_used -= rsz; | ||
621 | } | 421 | } |
622 | } else { | ||
623 | pi->dirnum += PI_OFF(ptr2index(tail - 1)) + 1; | ||
624 | } | 422 | } |
625 | #ifdef MALLOC_EXTRA_SANITY | 423 | } |
626 | if (PD_OFF(pi->dirnum) > pdi_mod || PD_IDX(pi->dirnum) > idx) { | 424 | if (tounmap > 0) |
627 | wrterror("(ES): pages directory overflow"); | 425 | wrtwarning("malloc cache underflow"); |
628 | errno = EFAULT; | 426 | for (i = 0; i < malloc_cache; i++) { |
629 | return (NULL); | 427 | r = &d->free_regions[i]; |
428 | if (r->p == NULL) { | ||
429 | if (malloc_hint) | ||
430 | madvise(p, sz, MADV_FREE); | ||
431 | if (malloc_freeprot) | ||
432 | mprotect(p, sz, PROT_NONE); | ||
433 | r->p = p; | ||
434 | r->size = psz; | ||
435 | d->free_regions_size += psz; | ||
436 | break; | ||
630 | } | 437 | } |
631 | #endif /* MALLOC_EXTRA_SANITY */ | 438 | } |
632 | if (idx == pidx && pi != last_dir) { | 439 | if (i == malloc_cache) |
633 | prev_dir = last_dir; | 440 | wrtwarning("malloc free slot lost"); |
634 | last_dir = pi; | 441 | if (d->free_regions_size > malloc_cache) |
442 | wrtwarning("malloc cache overflow"); | ||
443 | } | ||
444 | |||
445 | static void * | ||
446 | map(struct dir_info *d, size_t sz) | ||
447 | { | ||
448 | size_t psz = PAGEROUND(sz) >> MALLOC_PAGESHIFT; | ||
449 | struct region_info *r, *big = NULL; | ||
450 | u_int i, offset; | ||
451 | void *p; | ||
452 | |||
453 | if (psz > d->free_regions_size) { | ||
454 | p = MMAP(sz); | ||
455 | if (p != MAP_FAILED) | ||
456 | malloc_used += sz; | ||
457 | return p; | ||
458 | } | ||
459 | offset = getrbyte(); | ||
460 | for (i = 0; i < malloc_cache; i++) { | ||
461 | r = &d->free_regions[(i + offset) & (malloc_cache - 1)]; | ||
462 | if (r->p != NULL) { | ||
463 | if (r->size == psz) { | ||
464 | p = r->p; | ||
465 | if (malloc_freeprot) | ||
466 | mprotect(p, sz, PROT_READ | PROT_WRITE); | ||
467 | if (malloc_hint) | ||
468 | madvise(p, sz, MADV_NORMAL); | ||
469 | r->p = NULL; | ||
470 | r->size = 0; | ||
471 | d->free_regions_size -= psz; | ||
472 | return p; | ||
473 | } else if (r->size > psz) | ||
474 | big = r; | ||
635 | } | 475 | } |
636 | spi = pi; | ||
637 | pi = spi->next; | ||
638 | } | 476 | } |
639 | #ifdef MALLOC_EXTRA_SANITY | 477 | if (big != NULL) { |
640 | if (cnt > dirs) | 478 | r = big; |
641 | wrtwarning("(ES): cnt > dirs"); | 479 | p = (char *)r->p + ((r->size - psz) << MALLOC_PAGESHIFT); |
642 | #endif /* MALLOC_EXTRA_SANITY */ | 480 | if (malloc_freeprot) |
643 | if (cnt < dirs) | 481 | mprotect(p, sz, PROT_READ | PROT_WRITE); |
644 | munmap((char *)pdregion + cnt * malloc_pagesize, | 482 | if (malloc_hint) |
645 | (dirs - cnt) * malloc_pagesize); | 483 | madvise(p, sz, MADV_NORMAL); |
646 | 484 | r->size -= psz; | |
647 | return (result); | 485 | d->free_regions_size -= psz; |
486 | return p; | ||
487 | } | ||
488 | p = MMAP(sz); | ||
489 | if (p != MAP_FAILED) | ||
490 | malloc_used += sz; | ||
491 | if (d->free_regions_size > malloc_cache) | ||
492 | wrtwarning("malloc cache"); | ||
493 | return p; | ||
494 | } | ||
495 | |||
496 | static void | ||
497 | rbytes_init(void) | ||
498 | { | ||
499 | arc4random_buf(rbytes, sizeof(rbytes)); | ||
500 | rbytesused = 0; | ||
501 | } | ||
502 | |||
503 | static u_char | ||
504 | getrbyte(void) | ||
505 | { | ||
506 | if (rbytesused >= sizeof(rbytes)) | ||
507 | rbytes_init(); | ||
508 | return rbytes[rbytesused++]; | ||
648 | } | 509 | } |
649 | 510 | ||
650 | /* | 511 | /* |
651 | * Initialize the world | 512 | * Initialize a dir_info, which should have been cleared by caller |
652 | */ | 513 | */ |
653 | static void | 514 | static int |
654 | malloc_init(void) | 515 | omalloc_init(struct dir_info *d) |
655 | { | 516 | { |
656 | char *p, b[64]; | 517 | char *p, b[64]; |
657 | int i, j, save_errno = errno; | 518 | int i, j, save_errno = errno; |
519 | size_t regioninfo_size; | ||
658 | 520 | ||
659 | #ifdef MALLOC_EXTRA_SANITY | 521 | rbytes_init(); |
660 | malloc_junk = 1; | ||
661 | #endif /* MALLOC_EXTRA_SANITY */ | ||
662 | 522 | ||
663 | for (i = 0; i < 3; i++) { | 523 | for (i = 0; i < 3; i++) { |
664 | switch (i) { | 524 | switch (i) { |
@@ -686,6 +546,8 @@ malloc_init(void) | |||
686 | switch (*p) { | 546 | switch (*p) { |
687 | case '>': | 547 | case '>': |
688 | malloc_cache <<= 1; | 548 | malloc_cache <<= 1; |
549 | if (malloc_cache > MALLOC_MAXCACHE) | ||
550 | malloc_cache = MALLOC_MAXCACHE; | ||
689 | break; | 551 | break; |
690 | case '<': | 552 | case '<': |
691 | malloc_cache >>= 1; | 553 | malloc_cache >>= 1; |
@@ -714,7 +576,7 @@ malloc_init(void) | |||
714 | malloc_guard = 0; | 576 | malloc_guard = 0; |
715 | break; | 577 | break; |
716 | case 'G': | 578 | case 'G': |
717 | malloc_guard = malloc_pagesize; | 579 | malloc_guard = MALLOC_PAGESIZE; |
718 | break; | 580 | break; |
719 | case 'h': | 581 | case 'h': |
720 | malloc_hint = 0; | 582 | malloc_hint = 0; |
@@ -735,10 +597,10 @@ malloc_init(void) | |||
735 | malloc_silent = 1; | 597 | malloc_silent = 1; |
736 | break; | 598 | break; |
737 | case 'p': | 599 | case 'p': |
738 | malloc_ptrguard = 0; | 600 | malloc_move = 0; |
739 | break; | 601 | break; |
740 | case 'P': | 602 | case 'P': |
741 | malloc_ptrguard = 1; | 603 | malloc_move = 1; |
742 | break; | 604 | break; |
743 | case 'r': | 605 | case 'r': |
744 | malloc_realloc = 0; | 606 | malloc_realloc = 0; |
@@ -746,14 +608,6 @@ malloc_init(void) | |||
746 | case 'R': | 608 | case 'R': |
747 | malloc_realloc = 1; | 609 | malloc_realloc = 1; |
748 | break; | 610 | break; |
749 | #ifdef __FreeBSD__ | ||
750 | case 'u': | ||
751 | malloc_utrace = 0; | ||
752 | break; | ||
753 | case 'U': | ||
754 | malloc_utrace = 1; | ||
755 | break; | ||
756 | #endif /* __FreeBSD__ */ | ||
757 | case 'x': | 611 | case 'x': |
758 | malloc_xmalloc = 0; | 612 | malloc_xmalloc = 0; |
759 | break; | 613 | break; |
@@ -776,8 +630,6 @@ malloc_init(void) | |||
776 | } | 630 | } |
777 | } | 631 | } |
778 | 632 | ||
779 | UTRACE(0, 0, 0); | ||
780 | |||
781 | /* | 633 | /* |
782 | * We want junk in the entire allocation, and zero only in the part | 634 | * We want junk in the entire allocation, and zero only in the part |
783 | * the user asked for. | 635 | * the user asked for. |
@@ -791,271 +643,228 @@ malloc_init(void) | |||
791 | " Will not be able to dump malloc stats on exit"); | 643 | " Will not be able to dump malloc stats on exit"); |
792 | #endif /* MALLOC_STATS */ | 644 | #endif /* MALLOC_STATS */ |
793 | 645 | ||
794 | /* Allocate one page for the page directory. */ | 646 | errno = save_errno; |
795 | page_dir = (struct pginfo **)MMAP(malloc_pagesize); | ||
796 | 647 | ||
797 | if (page_dir == MAP_FAILED) { | 648 | d->regions_bits = 9; |
798 | wrterror("mmap(2) failed, check limits"); | 649 | d->regions_free = d->regions_total = 1 << d->regions_bits; |
799 | errno = ENOMEM; | 650 | regioninfo_size = d->regions_total * sizeof(struct region_info); |
800 | return; | 651 | d->r = MMAP(regioninfo_size); |
652 | if (d->r == MAP_FAILED) { | ||
653 | wrterror("malloc init mmap failed"); | ||
654 | d->regions_total = 0; | ||
655 | return 1; | ||
801 | } | 656 | } |
802 | pdi_off = (malloc_pagesize - sizeof(struct pdinfo)) & ~(malloc_minsize - 1); | 657 | malloc_used += regioninfo_size; |
803 | pdi_mod = pdi_off / sizeof(struct pginfo *); | 658 | memset(d->r, 0, regioninfo_size); |
804 | 659 | d->canary1 = arc4random(); | |
805 | last_dir = (struct pdinfo *) ((caddr_t) page_dir + pdi_off); | 660 | d->canary2 = ~d->canary1; |
806 | last_dir->base = page_dir; | 661 | return 0; |
807 | last_dir->prev = last_dir->next = NULL; | ||
808 | last_dir->dirnum = malloc_pageshift; | ||
809 | |||
810 | /* Been here, done that. */ | ||
811 | malloc_started++; | ||
812 | |||
813 | /* Recalculate the cache size in bytes, and make sure it's nonzero. */ | ||
814 | if (!malloc_cache) | ||
815 | malloc_cache++; | ||
816 | malloc_cache <<= malloc_pageshift; | ||
817 | errno = save_errno; | ||
818 | } | 662 | } |
819 | 663 | ||
820 | /* | 664 | static int |
821 | * Allocate a number of complete pages | 665 | omalloc_grow(struct dir_info *d) |
822 | */ | ||
823 | static void * | ||
824 | malloc_pages(size_t size) | ||
825 | { | 666 | { |
826 | void *p, *tp; | 667 | size_t newbits; |
827 | int i; | 668 | size_t newtotal; |
828 | struct pginfo **pd; | 669 | size_t newsize; |
829 | struct pdinfo *pi; | 670 | size_t mask; |
830 | u_long pidx, index; | 671 | size_t i; |
831 | struct pgfree *pf, *delay_free = NULL; | 672 | struct region_info *p; |
832 | 673 | ||
833 | size = pageround(size) + malloc_guard; | 674 | if (d->regions_total > SIZE_MAX / sizeof(struct region_info) / 2 ) |
834 | 675 | return 1; | |
835 | p = NULL; | 676 | |
836 | /* Look for free pages before asking for more */ | 677 | newbits = d->regions_bits + 1; |
837 | for (pf = free_list.next; pf; pf = pf->next) { | 678 | newtotal = d->regions_total * 2; |
838 | 679 | newsize = newtotal * sizeof(struct region_info); | |
839 | #ifdef MALLOC_EXTRA_SANITY | 680 | mask = newtotal - 1; |
840 | if (pf->size & malloc_pagemask) { | 681 | |
841 | wrterror("(ES): junk length entry on free_list"); | 682 | p = MMAP(newsize); |
842 | errno = EFAULT; | 683 | if (p == MAP_FAILED) |
843 | return (NULL); | 684 | return 1; |
844 | } | 685 | |
845 | if (!pf->size) { | 686 | malloc_used += newsize; |
846 | wrterror("(ES): zero length entry on free_list"); | 687 | memset(p, 0, newsize); |
847 | errno = EFAULT; | 688 | STATS_ZERO(d->inserts); |
848 | return (NULL); | 689 | STATS_ZERO(d->insert_collisions); |
849 | } | 690 | for (i = 0; i < d->regions_total; i++) { |
850 | if (pf->page > (pf->page + pf->size)) { | 691 | void *q = d->r[i].p; |
851 | wrterror("(ES): sick entry on free_list"); | 692 | if (q != NULL) { |
852 | errno = EFAULT; | 693 | size_t index = hash(q) & mask; |
853 | return (NULL); | 694 | STATS_INC(d->inserts); |
854 | } | 695 | while (p[index].p != NULL) { |
855 | if ((pi = pf->pdir) == NULL) { | 696 | index = (index - 1) & mask; |
856 | wrterror("(ES): invalid page directory on free-list"); | 697 | STATS_INC(d->insert_collisions); |
857 | errno = EFAULT; | 698 | } |
858 | return (NULL); | 699 | p[index] = d->r[i]; |
859 | } | ||
860 | if ((pidx = PI_IDX(ptr2index(pf->page))) != PD_IDX(pi->dirnum)) { | ||
861 | wrterror("(ES): directory index mismatch on free-list"); | ||
862 | errno = EFAULT; | ||
863 | return (NULL); | ||
864 | } | ||
865 | pd = pi->base; | ||
866 | if (pd[PI_OFF(ptr2index(pf->page))] != MALLOC_FREE) { | ||
867 | wrterror("(ES): non-free first page on free-list"); | ||
868 | errno = EFAULT; | ||
869 | return (NULL); | ||
870 | } | ||
871 | pidx = PI_IDX(ptr2index((pf->page) + (pf->size)) - 1); | ||
872 | for (pi = pf->pdir; pi != NULL && PD_IDX(pi->dirnum) < pidx; | ||
873 | pi = pi->next) | ||
874 | ; | ||
875 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | ||
876 | wrterror("(ES): last page not referenced in page directory"); | ||
877 | errno = EFAULT; | ||
878 | return (NULL); | ||
879 | } | ||
880 | pd = pi->base; | ||
881 | if (pd[PI_OFF(ptr2index((pf->page) + (pf->size)) - 1)] != MALLOC_FREE) { | ||
882 | wrterror("(ES): non-free last page on free-list"); | ||
883 | errno = EFAULT; | ||
884 | return (NULL); | ||
885 | } | ||
886 | #endif /* MALLOC_EXTRA_SANITY */ | ||
887 | |||
888 | if (pf->size < size) | ||
889 | continue; | ||
890 | |||
891 | if (pf->size == size) { | ||
892 | p = pf->page; | ||
893 | pi = pf->pdir; | ||
894 | if (pf->next != NULL) | ||
895 | pf->next->prev = pf->prev; | ||
896 | pf->prev->next = pf->next; | ||
897 | delay_free = pf; | ||
898 | break; | ||
899 | } | 700 | } |
900 | p = pf->page; | 701 | } |
901 | pf->page = (char *) pf->page + size; | 702 | /* avoid pages containing meta info to end up in cache */ |
902 | pf->size -= size; | 703 | if (munmap(d->r, d->regions_total * sizeof(struct region_info))) |
903 | pidx = PI_IDX(ptr2index(pf->page)); | 704 | wrterror("omalloc_grow munmap"); |
904 | for (pi = pf->pdir; pi != NULL && PD_IDX(pi->dirnum) < pidx; | 705 | else |
905 | pi = pi->next) | 706 | malloc_used -= d->regions_total * sizeof(struct region_info); |
906 | ; | 707 | d->regions_free = d->regions_free + d->regions_total; |
907 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | 708 | d->regions_total = newtotal; |
908 | wrterror("(ES): hole in directories"); | 709 | d->regions_bits = newbits; |
909 | errno = EFAULT; | 710 | d->r = p; |
910 | return (NULL); | 711 | return 0; |
712 | } | ||
713 | |||
714 | static struct chunk_info * | ||
715 | alloc_chunk_info(struct dir_info *d) | ||
716 | { | ||
717 | struct chunk_info *p; | ||
718 | int i; | ||
719 | |||
720 | if (d->chunk_info_list == NULL) { | ||
721 | p = MMAP(MALLOC_PAGESIZE); | ||
722 | if (p == MAP_FAILED) | ||
723 | return NULL; | ||
724 | malloc_used += MALLOC_PAGESIZE; | ||
725 | for (i = 0; i < MALLOC_PAGESIZE / sizeof(*p); i++) { | ||
726 | p[i].next = d->chunk_info_list; | ||
727 | d->chunk_info_list = &p[i]; | ||
911 | } | 728 | } |
912 | tp = pf->pdir; | ||
913 | pf->pdir = pi; | ||
914 | pi = tp; | ||
915 | break; | ||
916 | } | 729 | } |
730 | p = d->chunk_info_list; | ||
731 | d->chunk_info_list = p->next; | ||
732 | memset(p, 0, sizeof *p); | ||
733 | p->canary = d->canary1; | ||
734 | return p; | ||
735 | } | ||
736 | |||
917 | 737 | ||
918 | size -= malloc_guard; | 738 | static void |
739 | put_chunk_info(struct dir_info *d, struct chunk_info *p) | ||
740 | { | ||
741 | p->next = d->chunk_info_list; | ||
742 | d->chunk_info_list = p; | ||
743 | } | ||
919 | 744 | ||
920 | #ifdef MALLOC_EXTRA_SANITY | 745 | static int |
921 | if (p != NULL && pi != NULL) { | 746 | insert(struct dir_info *d, void *p, size_t sz) |
922 | pidx = PD_IDX(pi->dirnum); | 747 | { |
923 | pd = pi->base; | 748 | size_t index; |
749 | size_t mask; | ||
750 | void *q; | ||
751 | |||
752 | if (d->regions_free * 4 < d->regions_total) { | ||
753 | if (omalloc_grow(d)) | ||
754 | return 1; | ||
924 | } | 755 | } |
925 | if (p != NULL && pd[PI_OFF(ptr2index(p))] != MALLOC_FREE) { | 756 | mask = d->regions_total - 1; |
926 | wrterror("(ES): allocated non-free page on free-list"); | 757 | index = hash(p) & mask; |
927 | errno = EFAULT; | 758 | q = d->r[index].p; |
928 | return (NULL); | 759 | STATS_INC(d->inserts); |
760 | while (q != NULL) { | ||
761 | index = (index - 1) & mask; | ||
762 | q = d->r[index].p; | ||
763 | STATS_INC(d->insert_collisions); | ||
929 | } | 764 | } |
930 | #endif /* MALLOC_EXTRA_SANITY */ | 765 | d->r[index].p = p; |
931 | 766 | d->r[index].size = sz; | |
932 | if (p != NULL && (malloc_guard || malloc_freeprot)) | 767 | d->regions_free--; |
933 | mprotect(p, size, PROT_READ | PROT_WRITE); | 768 | return 0; |
769 | } | ||
934 | 770 | ||
935 | size >>= malloc_pageshift; | 771 | static struct region_info * |
772 | find(struct dir_info *d, void *p) | ||
773 | { | ||
774 | size_t index; | ||
775 | size_t mask = d->regions_total - 1; | ||
776 | void *q, *r; | ||
777 | |||
778 | if (d->canary1 != ~d->canary2) | ||
779 | wrterror("internal struct corrupt"); | ||
780 | p = MASK_POINTER(p); | ||
781 | index = hash(p) & mask; | ||
782 | r = d->r[index].p; | ||
783 | q = MASK_POINTER(r); | ||
784 | STATS_INC(d->finds); | ||
785 | while (q != p && r != NULL) { | ||
786 | index = (index - 1) & mask; | ||
787 | r = d->r[index].p; | ||
788 | q = MASK_POINTER(r); | ||
789 | STATS_INC(d->find_collisions); | ||
790 | } | ||
791 | return q == p ? &d->r[index] : NULL; | ||
792 | } | ||
936 | 793 | ||
937 | /* Map new pages */ | 794 | static void |
938 | if (p == NULL) | 795 | delete(struct dir_info *d, struct region_info *ri) |
939 | p = map_pages(size); | 796 | { |
940 | 797 | /* algorithm R, Knuth Vol III section 6.4 */ | |
941 | if (p != NULL) { | 798 | size_t mask = d->regions_total - 1; |
942 | index = ptr2index(p); | 799 | size_t i, j, r; |
943 | pidx = PI_IDX(index); | 800 | |
944 | pdir_lookup(index, &pi); | 801 | if (d->regions_total & (d->regions_total - 1)) |
945 | #ifdef MALLOC_EXTRA_SANITY | 802 | wrterror("regions_total not 2^x"); |
946 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | 803 | d->regions_free++; |
947 | wrterror("(ES): mapped pages not found in directory"); | 804 | STATS_INC(g_pool.deletes); |
948 | errno = EFAULT; | 805 | |
949 | return (NULL); | 806 | i = ri - d->r; |
950 | } | 807 | for (;;) { |
951 | #endif /* MALLOC_EXTRA_SANITY */ | 808 | d->r[i].p = NULL; |
952 | if (pi != last_dir) { | 809 | d->r[i].size = 0; |
953 | prev_dir = last_dir; | 810 | j = i; |
954 | last_dir = pi; | 811 | for (;;) { |
955 | } | 812 | i = (i - 1) & mask; |
956 | pd = pi->base; | 813 | if (d->r[i].p == NULL) |
957 | pd[PI_OFF(index)] = MALLOC_FIRST; | 814 | return; |
958 | for (i = 1; i < size; i++) { | 815 | r = hash(d->r[i].p) & mask; |
959 | if (!PI_OFF(index + i)) { | 816 | if ((i <= r && r < j) || (r < j && j < i) || |
960 | pidx++; | 817 | (j < i && i <= r)) |
961 | pi = pi->next; | 818 | continue; |
962 | #ifdef MALLOC_EXTRA_SANITY | 819 | d->r[j] = d->r[i]; |
963 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | 820 | STATS_INC(g_pool.delete_moves); |
964 | wrterror("(ES): hole in mapped pages directory"); | 821 | break; |
965 | errno = EFAULT; | ||
966 | return (NULL); | ||
967 | } | ||
968 | #endif /* MALLOC_EXTRA_SANITY */ | ||
969 | pd = pi->base; | ||
970 | } | ||
971 | pd[PI_OFF(index + i)] = MALLOC_FOLLOW; | ||
972 | } | ||
973 | if (malloc_guard) { | ||
974 | if (!PI_OFF(index + i)) { | ||
975 | pidx++; | ||
976 | pi = pi->next; | ||
977 | #ifdef MALLOC_EXTRA_SANITY | ||
978 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | ||
979 | wrterror("(ES): hole in mapped pages directory"); | ||
980 | errno = EFAULT; | ||
981 | return (NULL); | ||
982 | } | ||
983 | #endif /* MALLOC_EXTRA_SANITY */ | ||
984 | pd = pi->base; | ||
985 | } | ||
986 | pd[PI_OFF(index + i)] = MALLOC_FIRST; | ||
987 | } | 822 | } |
988 | malloc_used += size << malloc_pageshift; | ||
989 | malloc_guarded += malloc_guard; | ||
990 | 823 | ||
991 | if (malloc_junk) | ||
992 | memset(p, SOME_JUNK, size << malloc_pageshift); | ||
993 | } | 824 | } |
994 | if (delay_free) { | ||
995 | if (px == NULL) | ||
996 | px = delay_free; | ||
997 | else | ||
998 | put_pgfree(delay_free); | ||
999 | } | ||
1000 | return (p); | ||
1001 | } | 825 | } |
1002 | 826 | ||
1003 | /* | 827 | /* |
1004 | * Allocate a page of fragments | 828 | * Allocate a page of chunks |
1005 | */ | 829 | */ |
1006 | 830 | static struct chunk_info * | |
1007 | static int | 831 | omalloc_make_chunks(struct dir_info *d, int bits) |
1008 | malloc_make_chunks(int bits) | ||
1009 | { | 832 | { |
1010 | struct pginfo *bp, **pd; | 833 | struct chunk_info *bp; |
1011 | struct pdinfo *pi; | ||
1012 | #ifdef MALLOC_EXTRA_SANITY | ||
1013 | u_long pidx; | ||
1014 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1015 | void *pp; | 834 | void *pp; |
1016 | long i, k; | 835 | long i, k; |
1017 | 836 | ||
1018 | /* Allocate a new bucket */ | 837 | /* Allocate a new bucket */ |
1019 | pp = malloc_pages((size_t)malloc_pagesize); | 838 | pp = map(d, MALLOC_PAGESIZE); |
1020 | if (pp == NULL) | 839 | if (pp == MAP_FAILED) |
1021 | return (0); | 840 | return NULL; |
1022 | 841 | ||
1023 | /* Find length of admin structure */ | 842 | bp = alloc_chunk_info(d); |
1024 | |||
1025 | /* Don't waste more than two chunks on this */ | ||
1026 | |||
1027 | /* | ||
1028 | * If we are to allocate a memory protected page for the malloc(0) | ||
1029 | * case (when bits=0), it must be from a different page than the | ||
1030 | * pginfo page. | ||
1031 | * --> Treat it like the big chunk alloc, get a second data page. | ||
1032 | */ | ||
1033 | bp = alloc_pginfo(); | ||
1034 | if (bp == NULL) { | 843 | if (bp == NULL) { |
1035 | ifree(pp); | 844 | unmap(d, pp, MALLOC_PAGESIZE); |
1036 | return (0); | 845 | return NULL; |
1037 | } | 846 | } |
1038 | 847 | ||
1039 | /* memory protect the page allocated in the malloc(0) case */ | 848 | /* memory protect the page allocated in the malloc(0) case */ |
1040 | if (bits == 0) { | 849 | if (bits == 0) { |
1041 | bp->size = 0; | 850 | bp->size = 0; |
1042 | bp->shift = 1; | 851 | bp->shift = 1; |
1043 | i = malloc_minsize - 1; | 852 | i = MALLOC_MINSIZE - 1; |
1044 | while (i >>= 1) | 853 | while (i >>= 1) |
1045 | bp->shift++; | 854 | bp->shift++; |
1046 | bp->total = bp->free = malloc_pagesize >> bp->shift; | 855 | bp->total = bp->free = MALLOC_PAGESIZE >> bp->shift; |
1047 | bp->page = pp; | 856 | bp->page = pp; |
1048 | 857 | ||
1049 | k = mprotect(pp, malloc_pagesize, PROT_NONE); | 858 | k = mprotect(pp, MALLOC_PAGESIZE, PROT_NONE); |
1050 | if (k < 0) { | 859 | if (k < 0) { |
1051 | ifree(pp); | 860 | unmap(d, pp, MALLOC_PAGESIZE); |
1052 | put_pginfo(bp); | 861 | put_chunk_info(d, bp); |
1053 | return (0); | 862 | return NULL; |
1054 | } | 863 | } |
1055 | } else { | 864 | } else { |
1056 | bp->size = (1UL << bits); | 865 | bp->size = (1UL << bits); |
1057 | bp->shift = bits; | 866 | bp->shift = bits; |
1058 | bp->total = bp->free = malloc_pagesize >> bits; | 867 | bp->total = bp->free = MALLOC_PAGESIZE >> bits; |
1059 | bp->page = pp; | 868 | bp->page = pp; |
1060 | } | 869 | } |
1061 | 870 | ||
@@ -1070,63 +879,54 @@ malloc_make_chunks(int bits) | |||
1070 | for (; i < k; i++) | 879 | for (; i < k; i++) |
1071 | bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); | 880 | bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); |
1072 | 881 | ||
1073 | pdir_lookup(ptr2index(pp), &pi); | 882 | bp->next = d->chunk_dir[bits]; |
1074 | #ifdef MALLOC_EXTRA_SANITY | 883 | d->chunk_dir[bits] = bp; |
1075 | pidx = PI_IDX(ptr2index(pp)); | ||
1076 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | ||
1077 | wrterror("(ES): mapped pages not found in directory"); | ||
1078 | errno = EFAULT; | ||
1079 | return (0); | ||
1080 | } | ||
1081 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1082 | if (pi != last_dir) { | ||
1083 | prev_dir = last_dir; | ||
1084 | last_dir = pi; | ||
1085 | } | ||
1086 | pd = pi->base; | ||
1087 | pd[PI_OFF(ptr2index(pp))] = bp; | ||
1088 | 884 | ||
1089 | bp->next = page_dir[bits]; | 885 | bits++; |
1090 | page_dir[bits] = bp; | 886 | if ((uintptr_t)pp & bits) |
887 | wrterror("pp & bits"); | ||
1091 | 888 | ||
1092 | /* MALLOC_UNLOCK */ | 889 | insert(d, (void *)((uintptr_t)pp | bits), (uintptr_t)bp); |
1093 | return (1); | 890 | return bp; |
1094 | } | 891 | } |
1095 | 892 | ||
893 | |||
1096 | /* | 894 | /* |
1097 | * Allocate a fragment | 895 | * Allocate a chunk |
1098 | */ | 896 | */ |
1099 | static void * | 897 | static void * |
1100 | malloc_bytes(size_t size) | 898 | malloc_bytes(struct dir_info *d, size_t size) |
1101 | { | 899 | { |
1102 | int i, j; | 900 | int i, j; |
1103 | size_t k; | 901 | size_t k; |
1104 | u_long u, *lp; | 902 | u_long u, *lp; |
1105 | struct pginfo *bp; | 903 | struct chunk_info *bp; |
1106 | 904 | ||
1107 | /* Don't bother with anything less than this */ | 905 | /* Don't bother with anything less than this */ |
1108 | /* unless we have a malloc(0) requests */ | 906 | /* unless we have a malloc(0) requests */ |
1109 | if (size != 0 && size < malloc_minsize) | 907 | if (size != 0 && size < MALLOC_MINSIZE) |
1110 | size = malloc_minsize; | 908 | size = MALLOC_MINSIZE; |
1111 | 909 | ||
1112 | /* Find the right bucket */ | 910 | /* Find the right bucket */ |
1113 | if (size == 0) | 911 | if (size == 0) |
1114 | j = 0; | 912 | j = 0; |
1115 | else { | 913 | else { |
1116 | j = 1; | 914 | j = MALLOC_MINSHIFT; |
1117 | i = size - 1; | 915 | i = (size - 1) >> (MALLOC_MINSHIFT - 1); |
1118 | while (i >>= 1) | 916 | while (i >>= 1) |
1119 | j++; | 917 | j++; |
1120 | } | 918 | } |
1121 | 919 | ||
1122 | /* If it's empty, make a page more of that size chunks */ | 920 | /* If it's empty, make a page more of that size chunks */ |
1123 | if (page_dir[j] == NULL && !malloc_make_chunks(j)) | 921 | bp = d->chunk_dir[j]; |
1124 | return (NULL); | 922 | if (bp == NULL && (bp = omalloc_make_chunks(d, j)) == NULL) |
1125 | 923 | return NULL; | |
1126 | bp = page_dir[j]; | ||
1127 | 924 | ||
925 | if (bp->canary != d->canary1) | ||
926 | wrterror("chunk info corrupted"); | ||
1128 | /* Find first word of bitmap which isn't empty */ | 927 | /* Find first word of bitmap which isn't empty */ |
1129 | for (lp = bp->bits; !*lp; lp++); | 928 | for (lp = bp->bits; !*lp; lp++) |
929 | /* EMPTY */; | ||
1130 | 930 | ||
1131 | /* Find that bit, and tweak it */ | 931 | /* Find that bit, and tweak it */ |
1132 | u = 1; | 932 | u = 1; |
@@ -1136,578 +936,59 @@ malloc_bytes(size_t size) | |||
1136 | k++; | 936 | k++; |
1137 | } | 937 | } |
1138 | 938 | ||
1139 | if (malloc_guard) { | 939 | /* advance a random # of positions */ |
1140 | /* Walk to a random position. */ | 940 | i = (getrbyte() & (MALLOC_DELAYED_CHUNKS - 1)) % bp->free; |
1141 | i = arc4random_uniform(bp->free); | 941 | while (i > 0) { |
1142 | while (i > 0) { | 942 | u += u; |
1143 | u += u; | 943 | k++; |
1144 | k++; | 944 | if (k >= MALLOC_BITS) { |
1145 | if (k >= MALLOC_BITS) { | 945 | lp++; |
1146 | lp++; | 946 | u = 1; |
1147 | u = 1; | 947 | k = 0; |
1148 | k = 0; | 948 | } |
1149 | } | 949 | if (lp - bp->bits > (bp->total - 1) / MALLOC_BITS) { |
1150 | #ifdef MALLOC_EXTRA_SANITY | 950 | wrterror("chunk overflow"); |
1151 | if (lp - bp->bits > (bp->total - 1) / MALLOC_BITS) { | 951 | errno = EFAULT; |
1152 | wrterror("chunk overflow"); | 952 | return (NULL); |
1153 | errno = EFAULT; | ||
1154 | return (NULL); | ||
1155 | } | ||
1156 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1157 | if (*lp & u) | ||
1158 | i--; | ||
1159 | } | 953 | } |
954 | if (*lp & u) | ||
955 | i--; | ||
1160 | } | 956 | } |
957 | |||
1161 | *lp ^= u; | 958 | *lp ^= u; |
1162 | 959 | ||
1163 | /* If there are no more free, remove from free-list */ | 960 | /* If there are no more free, remove from free-list */ |
1164 | if (!--bp->free) { | 961 | if (!--bp->free) { |
1165 | page_dir[j] = bp->next; | 962 | d->chunk_dir[j] = bp->next; |
1166 | bp->next = NULL; | 963 | bp->next = NULL; |
1167 | } | 964 | } |
1168 | /* Adjust to the real offset of that chunk */ | 965 | /* Adjust to the real offset of that chunk */ |
1169 | k += (lp - bp->bits) * MALLOC_BITS; | 966 | k += (lp - bp->bits) * MALLOC_BITS; |
1170 | k <<= bp->shift; | 967 | k <<= bp->shift; |
1171 | 968 | ||
1172 | if (malloc_junk && bp->size != 0) | 969 | if (malloc_junk && bp->size > 0) |
1173 | memset((char *)bp->page + k, SOME_JUNK, (size_t)bp->size); | 970 | memset((char *)bp->page + k, SOME_JUNK, bp->size); |
1174 | 971 | return ((char *)bp->page + k); | |
1175 | return ((u_char *) bp->page + k); | ||
1176 | } | 972 | } |
1177 | 973 | ||
1178 | /* | ||
1179 | * Magic so that malloc(sizeof(ptr)) is near the end of the page. | ||
1180 | */ | ||
1181 | #define PTR_GAP (malloc_pagesize - sizeof(void *)) | ||
1182 | #define PTR_SIZE (sizeof(void *)) | ||
1183 | #define PTR_ALIGNED(p) (((unsigned long)p & malloc_pagemask) == PTR_GAP) | ||
1184 | |||
1185 | /* | ||
1186 | * Allocate a piece of memory | ||
1187 | */ | ||
1188 | static void * | ||
1189 | imalloc(size_t size) | ||
1190 | { | ||
1191 | void *result; | ||
1192 | int ptralloc = 0; | ||
1193 | |||
1194 | if (!malloc_started) | ||
1195 | malloc_init(); | ||
1196 | |||
1197 | if (suicide) | ||
1198 | abort(); | ||
1199 | |||
1200 | /* does not matter if alloc_pgfree() fails */ | ||
1201 | if (px == NULL) | ||
1202 | px = alloc_pgfree(); | ||
1203 | |||
1204 | if (malloc_ptrguard && size == PTR_SIZE) { | ||
1205 | ptralloc = 1; | ||
1206 | size = malloc_pagesize; | ||
1207 | } | ||
1208 | if ((size + malloc_pagesize) < size) { /* Check for overflow */ | ||
1209 | result = NULL; | ||
1210 | errno = ENOMEM; | ||
1211 | } else if (size <= malloc_maxsize) | ||
1212 | result = malloc_bytes(size); | ||
1213 | else | ||
1214 | result = malloc_pages(size); | ||
1215 | |||
1216 | if (malloc_abort == 1 && result == NULL) | ||
1217 | wrterror("allocation failed"); | ||
1218 | |||
1219 | if (malloc_zero && result != NULL) | ||
1220 | memset(result, 0, size); | ||
1221 | |||
1222 | if (result && ptralloc) | ||
1223 | return ((char *) result + PTR_GAP); | ||
1224 | return (result); | ||
1225 | } | ||
1226 | |||
1227 | /* | ||
1228 | * Change the size of an allocation. | ||
1229 | */ | ||
1230 | static void * | ||
1231 | irealloc(void *ptr, size_t size) | ||
1232 | { | ||
1233 | void *p; | ||
1234 | size_t osize; | ||
1235 | u_long index, i; | ||
1236 | struct pginfo **mp; | ||
1237 | struct pginfo **pd; | ||
1238 | struct pdinfo *pi; | ||
1239 | #ifdef MALLOC_EXTRA_SANITY | ||
1240 | u_long pidx; | ||
1241 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1242 | |||
1243 | if (suicide) | ||
1244 | abort(); | ||
1245 | |||
1246 | if (!malloc_started) { | ||
1247 | wrtwarning("malloc() has never been called"); | ||
1248 | return (NULL); | ||
1249 | } | ||
1250 | if (malloc_ptrguard && PTR_ALIGNED(ptr)) { | ||
1251 | if (size <= PTR_SIZE) | ||
1252 | return (ptr); | ||
1253 | |||
1254 | p = imalloc(size); | ||
1255 | if (p) | ||
1256 | memcpy(p, ptr, PTR_SIZE); | ||
1257 | ifree(ptr); | ||
1258 | return (p); | ||
1259 | } | ||
1260 | index = ptr2index(ptr); | ||
1261 | |||
1262 | if (index < malloc_pageshift) { | ||
1263 | wrtwarning("junk pointer, too low to make sense"); | ||
1264 | return (NULL); | ||
1265 | } | ||
1266 | if (index > last_index) { | ||
1267 | wrtwarning("junk pointer, too high to make sense"); | ||
1268 | return (NULL); | ||
1269 | } | ||
1270 | pdir_lookup(index, &pi); | ||
1271 | #ifdef MALLOC_EXTRA_SANITY | ||
1272 | pidx = PI_IDX(index); | ||
1273 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | ||
1274 | wrterror("(ES): mapped pages not found in directory"); | ||
1275 | errno = EFAULT; | ||
1276 | return (NULL); | ||
1277 | } | ||
1278 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1279 | if (pi != last_dir) { | ||
1280 | prev_dir = last_dir; | ||
1281 | last_dir = pi; | ||
1282 | } | ||
1283 | pd = pi->base; | ||
1284 | mp = &pd[PI_OFF(index)]; | ||
1285 | |||
1286 | if (*mp == MALLOC_FIRST) { /* Page allocation */ | ||
1287 | |||
1288 | /* Check the pointer */ | ||
1289 | if ((u_long) ptr & malloc_pagemask) { | ||
1290 | wrtwarning("modified (page-) pointer"); | ||
1291 | return (NULL); | ||
1292 | } | ||
1293 | /* Find the size in bytes */ | ||
1294 | i = index; | ||
1295 | if (!PI_OFF(++i)) { | ||
1296 | pi = pi->next; | ||
1297 | if (pi != NULL && PD_IDX(pi->dirnum) != PI_IDX(i)) | ||
1298 | pi = NULL; | ||
1299 | if (pi != NULL) | ||
1300 | pd = pi->base; | ||
1301 | } | ||
1302 | for (osize = malloc_pagesize; | ||
1303 | pi != NULL && pd[PI_OFF(i)] == MALLOC_FOLLOW;) { | ||
1304 | osize += malloc_pagesize; | ||
1305 | if (!PI_OFF(++i)) { | ||
1306 | pi = pi->next; | ||
1307 | if (pi != NULL && PD_IDX(pi->dirnum) != PI_IDX(i)) | ||
1308 | pi = NULL; | ||
1309 | if (pi != NULL) | ||
1310 | pd = pi->base; | ||
1311 | } | ||
1312 | } | ||
1313 | |||
1314 | if (!malloc_realloc && size <= osize && | ||
1315 | size > osize - malloc_pagesize) { | ||
1316 | if (malloc_junk) | ||
1317 | memset((char *)ptr + size, SOME_JUNK, osize - size); | ||
1318 | return (ptr); /* ..don't do anything else. */ | ||
1319 | } | ||
1320 | } else if (*mp >= MALLOC_MAGIC) { /* Chunk allocation */ | ||
1321 | |||
1322 | /* Check the pointer for sane values */ | ||
1323 | if ((u_long) ptr & ((1UL << ((*mp)->shift)) - 1)) { | ||
1324 | wrtwarning("modified (chunk-) pointer"); | ||
1325 | return (NULL); | ||
1326 | } | ||
1327 | /* Find the chunk index in the page */ | ||
1328 | i = ((u_long) ptr & malloc_pagemask) >> (*mp)->shift; | ||
1329 | |||
1330 | /* Verify that it isn't a free chunk already */ | ||
1331 | if ((*mp)->bits[i / MALLOC_BITS] & (1UL << (i % MALLOC_BITS))) { | ||
1332 | wrtwarning("chunk is already free"); | ||
1333 | return (NULL); | ||
1334 | } | ||
1335 | osize = (*mp)->size; | ||
1336 | |||
1337 | if (!malloc_realloc && size <= osize && | ||
1338 | (size > osize / 2 || osize == malloc_minsize)) { | ||
1339 | if (malloc_junk) | ||
1340 | memset((char *) ptr + size, SOME_JUNK, osize - size); | ||
1341 | return (ptr); /* ..don't do anything else. */ | ||
1342 | } | ||
1343 | } else { | ||
1344 | wrtwarning("irealloc: pointer to wrong page"); | ||
1345 | return (NULL); | ||
1346 | } | ||
1347 | |||
1348 | p = imalloc(size); | ||
1349 | |||
1350 | if (p != NULL) { | ||
1351 | /* copy the lesser of the two sizes, and free the old one */ | ||
1352 | /* Don't move from/to 0 sized region !!! */ | ||
1353 | if (osize != 0 && size != 0) { | ||
1354 | if (osize < size) | ||
1355 | memcpy(p, ptr, osize); | ||
1356 | else | ||
1357 | memcpy(p, ptr, size); | ||
1358 | } | ||
1359 | ifree(ptr); | ||
1360 | } | ||
1361 | return (p); | ||
1362 | } | ||
1363 | |||
1364 | /* | ||
1365 | * Free a sequence of pages | ||
1366 | */ | ||
1367 | static void | ||
1368 | free_pages(void *ptr, u_long index, struct pginfo * info) | ||
1369 | { | ||
1370 | u_long i, pidx, lidx; | ||
1371 | size_t l, cachesize = 0; | ||
1372 | struct pginfo **pd; | ||
1373 | struct pdinfo *pi, *spi; | ||
1374 | struct pgfree *pf, *pt = NULL; | ||
1375 | caddr_t tail; | ||
1376 | |||
1377 | if (info == MALLOC_FREE) { | ||
1378 | wrtwarning("page is already free"); | ||
1379 | return; | ||
1380 | } | ||
1381 | if (info != MALLOC_FIRST) { | ||
1382 | wrtwarning("free_pages: pointer to wrong page"); | ||
1383 | return; | ||
1384 | } | ||
1385 | if ((u_long) ptr & malloc_pagemask) { | ||
1386 | wrtwarning("modified (page-) pointer"); | ||
1387 | return; | ||
1388 | } | ||
1389 | /* Count how many pages and mark them free at the same time */ | ||
1390 | pidx = PI_IDX(index); | ||
1391 | pdir_lookup(index, &pi); | ||
1392 | #ifdef MALLOC_EXTRA_SANITY | ||
1393 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | ||
1394 | wrterror("(ES): mapped pages not found in directory"); | ||
1395 | errno = EFAULT; | ||
1396 | return; | ||
1397 | } | ||
1398 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1399 | |||
1400 | spi = pi; /* Save page index for start of region. */ | ||
1401 | |||
1402 | pd = pi->base; | ||
1403 | pd[PI_OFF(index)] = MALLOC_FREE; | ||
1404 | i = 1; | ||
1405 | if (!PI_OFF(index + i)) { | ||
1406 | pi = pi->next; | ||
1407 | if (pi == NULL || PD_IDX(pi->dirnum) != PI_IDX(index + i)) | ||
1408 | pi = NULL; | ||
1409 | else | ||
1410 | pd = pi->base; | ||
1411 | } | ||
1412 | while (pi != NULL && pd[PI_OFF(index + i)] == MALLOC_FOLLOW) { | ||
1413 | pd[PI_OFF(index + i)] = MALLOC_FREE; | ||
1414 | i++; | ||
1415 | if (!PI_OFF(index + i)) { | ||
1416 | if ((pi = pi->next) == NULL || | ||
1417 | PD_IDX(pi->dirnum) != PI_IDX(index + i)) | ||
1418 | pi = NULL; | ||
1419 | else | ||
1420 | pd = pi->base; | ||
1421 | } | ||
1422 | } | ||
1423 | |||
1424 | l = i << malloc_pageshift; | ||
1425 | |||
1426 | if (malloc_junk) | ||
1427 | memset(ptr, SOME_JUNK, l); | ||
1428 | |||
1429 | malloc_used -= l; | ||
1430 | malloc_guarded -= malloc_guard; | ||
1431 | if (malloc_guard) { | ||
1432 | #ifdef MALLOC_EXTRA_SANITY | ||
1433 | if (pi == NULL || PD_IDX(pi->dirnum) != PI_IDX(index + i)) { | ||
1434 | wrterror("(ES): hole in mapped pages directory"); | ||
1435 | errno = EFAULT; | ||
1436 | return; | ||
1437 | } | ||
1438 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1439 | pd[PI_OFF(index + i)] = MALLOC_FREE; | ||
1440 | l += malloc_guard; | ||
1441 | } | ||
1442 | tail = (caddr_t)ptr + l; | ||
1443 | |||
1444 | if (malloc_hint) | ||
1445 | madvise(ptr, l, MADV_FREE); | ||
1446 | |||
1447 | if (malloc_freeprot) | ||
1448 | mprotect(ptr, l, PROT_NONE); | ||
1449 | |||
1450 | /* Add to free-list. */ | ||
1451 | if (px == NULL && (px = alloc_pgfree()) == NULL) | ||
1452 | goto not_return; | ||
1453 | px->page = ptr; | ||
1454 | px->pdir = spi; | ||
1455 | px->size = l; | ||
1456 | |||
1457 | if (free_list.next == NULL) { | ||
1458 | /* Nothing on free list, put this at head. */ | ||
1459 | px->next = NULL; | ||
1460 | px->prev = &free_list; | ||
1461 | free_list.next = px; | ||
1462 | pf = px; | ||
1463 | px = NULL; | ||
1464 | } else { | ||
1465 | /* | ||
1466 | * Find the right spot, leave pf pointing to the modified | ||
1467 | * entry. | ||
1468 | */ | ||
1469 | |||
1470 | /* Race ahead here, while calculating cache size. */ | ||
1471 | for (pf = free_list.next; | ||
1472 | (caddr_t)ptr > ((caddr_t)pf->page + pf->size) | ||
1473 | && pf->next != NULL; | ||
1474 | pf = pf->next) | ||
1475 | cachesize += pf->size; | ||
1476 | |||
1477 | /* Finish cache size calculation. */ | ||
1478 | pt = pf; | ||
1479 | while (pt) { | ||
1480 | cachesize += pt->size; | ||
1481 | pt = pt->next; | ||
1482 | } | ||
1483 | |||
1484 | if ((caddr_t)pf->page > tail) { | ||
1485 | /* Insert before entry */ | ||
1486 | px->next = pf; | ||
1487 | px->prev = pf->prev; | ||
1488 | pf->prev = px; | ||
1489 | px->prev->next = px; | ||
1490 | pf = px; | ||
1491 | px = NULL; | ||
1492 | } else if (((caddr_t)pf->page + pf->size) == ptr) { | ||
1493 | /* Append to the previous entry. */ | ||
1494 | cachesize -= pf->size; | ||
1495 | pf->size += l; | ||
1496 | if (pf->next != NULL && | ||
1497 | pf->next->page == ((caddr_t)pf->page + pf->size)) { | ||
1498 | /* And collapse the next too. */ | ||
1499 | pt = pf->next; | ||
1500 | pf->size += pt->size; | ||
1501 | pf->next = pt->next; | ||
1502 | if (pf->next != NULL) | ||
1503 | pf->next->prev = pf; | ||
1504 | } | ||
1505 | } else if (pf->page == tail) { | ||
1506 | /* Prepend to entry. */ | ||
1507 | cachesize -= pf->size; | ||
1508 | pf->size += l; | ||
1509 | pf->page = ptr; | ||
1510 | pf->pdir = spi; | ||
1511 | } else if (pf->next == NULL) { | ||
1512 | /* Append at tail of chain. */ | ||
1513 | px->next = NULL; | ||
1514 | px->prev = pf; | ||
1515 | pf->next = px; | ||
1516 | pf = px; | ||
1517 | px = NULL; | ||
1518 | } else { | ||
1519 | wrterror("freelist is destroyed"); | ||
1520 | errno = EFAULT; | ||
1521 | return; | ||
1522 | } | ||
1523 | } | ||
1524 | |||
1525 | if (pf->pdir != last_dir) { | ||
1526 | prev_dir = last_dir; | ||
1527 | last_dir = pf->pdir; | ||
1528 | } | ||
1529 | |||
1530 | /* Return something to OS ? */ | ||
1531 | if (pf->size > (malloc_cache - cachesize)) { | ||
1532 | |||
1533 | /* | ||
1534 | * Keep the cache intact. Notice that the '>' above guarantees that | ||
1535 | * the pf will always have at least one page afterwards. | ||
1536 | */ | ||
1537 | if (munmap((char *) pf->page + (malloc_cache - cachesize), | ||
1538 | pf->size - (malloc_cache - cachesize)) != 0) | ||
1539 | goto not_return; | ||
1540 | tail = (caddr_t)pf->page + pf->size; | ||
1541 | lidx = ptr2index(tail) - 1; | ||
1542 | pf->size = malloc_cache - cachesize; | ||
1543 | |||
1544 | index = ptr2index((caddr_t)pf->page + pf->size); | ||
1545 | |||
1546 | pidx = PI_IDX(index); | ||
1547 | if (prev_dir != NULL && PD_IDX(prev_dir->dirnum) >= pidx) | ||
1548 | prev_dir = NULL; /* Will be wiped out below ! */ | ||
1549 | |||
1550 | for (pi = pf->pdir; pi != NULL && PD_IDX(pi->dirnum) < pidx; | ||
1551 | pi = pi->next) | ||
1552 | ; | ||
1553 | |||
1554 | spi = pi; | ||
1555 | if (pi != NULL && PD_IDX(pi->dirnum) == pidx) { | ||
1556 | pd = pi->base; | ||
1557 | |||
1558 | for (i = index; i <= lidx;) { | ||
1559 | if (pd[PI_OFF(i)] != MALLOC_NOT_MINE) { | ||
1560 | pd[PI_OFF(i)] = MALLOC_NOT_MINE; | ||
1561 | #ifdef MALLOC_EXTRA_SANITY | ||
1562 | if (!PD_OFF(pi->dirnum)) { | ||
1563 | wrterror("(ES): pages directory underflow"); | ||
1564 | errno = EFAULT; | ||
1565 | return; | ||
1566 | } | ||
1567 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1568 | pi->dirnum--; | ||
1569 | } | ||
1570 | #ifdef MALLOC_EXTRA_SANITY | ||
1571 | else | ||
1572 | wrtwarning("(ES): page already unmapped"); | ||
1573 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1574 | i++; | ||
1575 | if (!PI_OFF(i)) { | ||
1576 | /* | ||
1577 | * If no page in that dir, free | ||
1578 | * directory page. | ||
1579 | */ | ||
1580 | if (!PD_OFF(pi->dirnum)) { | ||
1581 | /* Remove from list. */ | ||
1582 | if (spi == pi) | ||
1583 | spi = pi->prev; | ||
1584 | if (pi->prev != NULL) | ||
1585 | pi->prev->next = pi->next; | ||
1586 | if (pi->next != NULL) | ||
1587 | pi->next->prev = pi->prev; | ||
1588 | pi = pi->next; | ||
1589 | munmap(pd, malloc_pagesize); | ||
1590 | } else | ||
1591 | pi = pi->next; | ||
1592 | if (pi == NULL || | ||
1593 | PD_IDX(pi->dirnum) != PI_IDX(i)) | ||
1594 | break; | ||
1595 | pd = pi->base; | ||
1596 | } | ||
1597 | } | ||
1598 | if (pi && !PD_OFF(pi->dirnum)) { | ||
1599 | /* Resulting page dir is now empty. */ | ||
1600 | /* Remove from list. */ | ||
1601 | if (spi == pi) /* Update spi only if first. */ | ||
1602 | spi = pi->prev; | ||
1603 | if (pi->prev != NULL) | ||
1604 | pi->prev->next = pi->next; | ||
1605 | if (pi->next != NULL) | ||
1606 | pi->next->prev = pi->prev; | ||
1607 | pi = pi->next; | ||
1608 | munmap(pd, malloc_pagesize); | ||
1609 | } | ||
1610 | } | ||
1611 | if (pi == NULL && malloc_brk == tail) { | ||
1612 | /* Resize down the malloc upper boundary. */ | ||
1613 | last_index = index - 1; | ||
1614 | malloc_brk = index2ptr(index); | ||
1615 | } | ||
1616 | |||
1617 | /* XXX: We could realloc/shrink the pagedir here I guess. */ | ||
1618 | if (pf->size == 0) { /* Remove from free-list as well. */ | ||
1619 | if (px) | ||
1620 | put_pgfree(px); | ||
1621 | if ((px = pf->prev) != &free_list) { | ||
1622 | if (pi == NULL && last_index == (index - 1)) { | ||
1623 | if (spi == NULL) { | ||
1624 | malloc_brk = NULL; | ||
1625 | i = 11; | ||
1626 | } else { | ||
1627 | pd = spi->base; | ||
1628 | if (PD_IDX(spi->dirnum) < pidx) | ||
1629 | index = | ||
1630 | ((PD_IDX(spi->dirnum) + 1) * | ||
1631 | pdi_mod) - 1; | ||
1632 | for (pi = spi, i = index; | ||
1633 | pd[PI_OFF(i)] == MALLOC_NOT_MINE; | ||
1634 | i--) { | ||
1635 | #ifdef MALLOC_EXTRA_SANITY | ||
1636 | if (!PI_OFF(i)) { | ||
1637 | pi = pi->prev; | ||
1638 | if (pi == NULL || i == 0) | ||
1639 | break; | ||
1640 | pd = pi->base; | ||
1641 | i = (PD_IDX(pi->dirnum) + 1) * pdi_mod; | ||
1642 | } | ||
1643 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1644 | } | ||
1645 | malloc_brk = index2ptr(i + 1); | ||
1646 | } | ||
1647 | last_index = i; | ||
1648 | } | ||
1649 | if ((px->next = pf->next) != NULL) | ||
1650 | px->next->prev = px; | ||
1651 | } else { | ||
1652 | if ((free_list.next = pf->next) != NULL) | ||
1653 | free_list.next->prev = &free_list; | ||
1654 | } | ||
1655 | px = pf; | ||
1656 | last_dir = prev_dir; | ||
1657 | prev_dir = NULL; | ||
1658 | } | ||
1659 | } | ||
1660 | not_return: | ||
1661 | if (pt != NULL) | ||
1662 | put_pgfree(pt); | ||
1663 | } | ||
1664 | 974 | ||
1665 | /* | 975 | /* |
1666 | * Free a chunk, and possibly the page it's on, if the page becomes empty. | 976 | * Free a chunk, and possibly the page it's on, if the page becomes empty. |
1667 | */ | 977 | */ |
1668 | |||
1669 | /* ARGSUSED */ | ||
1670 | static void | 978 | static void |
1671 | free_bytes(void *ptr) | 979 | free_bytes(struct dir_info *d, struct region_info *r, void *ptr) |
1672 | { | 980 | { |
1673 | u_int8_t __arc4_getbyte(void); | 981 | struct chunk_info *info, **mp; |
1674 | struct pginfo **mp, **pd, *info; | 982 | long i; |
1675 | struct pdinfo *pi; | ||
1676 | #ifdef MALLOC_EXTRA_SANITY | ||
1677 | u_long pidx; | ||
1678 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1679 | u_long index; | ||
1680 | void *vp; | ||
1681 | long i; | ||
1682 | void *tmpptr; | ||
1683 | unsigned int tmpidx; | ||
1684 | /* pointers that we will want to free at some future time */ | ||
1685 | static void *chunk_buffer[16]; | ||
1686 | |||
1687 | |||
1688 | /* delay return, returning a random something from before instead */ | ||
1689 | tmpidx = __arc4_getbyte() % 16; | ||
1690 | tmpptr = chunk_buffer[tmpidx]; | ||
1691 | chunk_buffer[tmpidx] = ptr; | ||
1692 | ptr = tmpptr; | ||
1693 | if (!ptr) | ||
1694 | return; | ||
1695 | |||
1696 | index = ptr2index(ptr); | ||
1697 | |||
1698 | pdir_lookup(index, &pi); | ||
1699 | if (pi != last_dir) { | ||
1700 | prev_dir = last_dir; | ||
1701 | last_dir = pi; | ||
1702 | } | ||
1703 | pd = pi->base; | ||
1704 | info = pd[PI_OFF(index)]; | ||
1705 | 983 | ||
984 | info = (struct chunk_info *)r->size; | ||
985 | if (info->canary != d->canary1) | ||
986 | wrterror("chunk info corrupted"); | ||
1706 | 987 | ||
1707 | /* Find the chunk number on the page */ | 988 | /* Find the chunk number on the page */ |
1708 | i = ((u_long) ptr & malloc_pagemask) >> info->shift; | 989 | i = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift; |
1709 | 990 | ||
1710 | if ((u_long) ptr & ((1UL << (info->shift)) - 1)) { | 991 | if ((uintptr_t)ptr & ((1UL << (info->shift)) - 1)) { |
1711 | wrtwarning("modified (chunk-) pointer"); | 992 | wrtwarning("modified (chunk-) pointer"); |
1712 | return; | 993 | return; |
1713 | } | 994 | } |
@@ -1715,16 +996,14 @@ free_bytes(void *ptr) | |||
1715 | wrtwarning("chunk is already free"); | 996 | wrtwarning("chunk is already free"); |
1716 | return; | 997 | return; |
1717 | } | 998 | } |
1718 | if (malloc_junk && info->size != 0) | ||
1719 | memset(ptr, SOME_JUNK, (size_t)info->size); | ||
1720 | 999 | ||
1721 | info->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); | 1000 | info->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); |
1722 | info->free++; | 1001 | info->free++; |
1723 | 1002 | ||
1724 | if (info->size != 0) | 1003 | if (info->size != 0) |
1725 | mp = page_dir + info->shift; | 1004 | mp = d->chunk_dir + info->shift; |
1726 | else | 1005 | else |
1727 | mp = page_dir; | 1006 | mp = d->chunk_dir; |
1728 | 1007 | ||
1729 | if (info->free == 1) { | 1008 | if (info->free == 1) { |
1730 | /* Page became non-full */ | 1009 | /* Page became non-full */ |
@@ -1743,102 +1022,71 @@ free_bytes(void *ptr) | |||
1743 | /* Find & remove this page in the queue */ | 1022 | /* Find & remove this page in the queue */ |
1744 | while (*mp != info) { | 1023 | while (*mp != info) { |
1745 | mp = &((*mp)->next); | 1024 | mp = &((*mp)->next); |
1746 | #ifdef MALLOC_EXTRA_SANITY | ||
1747 | if (!*mp) { | 1025 | if (!*mp) { |
1748 | wrterror("(ES): Not on queue"); | 1026 | wrterror("not on queue"); |
1749 | errno = EFAULT; | 1027 | errno = EFAULT; |
1750 | return; | 1028 | return; |
1751 | } | 1029 | } |
1752 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1753 | } | 1030 | } |
1754 | *mp = info->next; | 1031 | *mp = info->next; |
1755 | 1032 | ||
1756 | /* Free the page & the info structure if need be */ | 1033 | if (info->size == 0 && !malloc_freeprot) |
1757 | pdir_lookup(ptr2index(info->page), &pi); | 1034 | mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); |
1758 | #ifdef MALLOC_EXTRA_SANITY | 1035 | unmap(d, info->page, MALLOC_PAGESIZE); |
1759 | pidx = PI_IDX(ptr2index(info->page)); | ||
1760 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | ||
1761 | wrterror("(ES): mapped pages not found in directory"); | ||
1762 | errno = EFAULT; | ||
1763 | return; | ||
1764 | } | ||
1765 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1766 | if (pi != last_dir) { | ||
1767 | prev_dir = last_dir; | ||
1768 | last_dir = pi; | ||
1769 | } | ||
1770 | pd = pi->base; | ||
1771 | pd[PI_OFF(ptr2index(info->page))] = MALLOC_FIRST; | ||
1772 | |||
1773 | /* If the page was mprotected, unprotect it before releasing it */ | ||
1774 | if (info->size == 0) | ||
1775 | mprotect(info->page, malloc_pagesize, PROT_READ | PROT_WRITE); | ||
1776 | 1036 | ||
1777 | vp = info->page; | 1037 | delete(d, r); |
1778 | put_pginfo(info); | 1038 | put_chunk_info(d, info); |
1779 | ifree(vp); | ||
1780 | } | 1039 | } |
1781 | 1040 | ||
1782 | static void | ||
1783 | ifree(void *ptr) | ||
1784 | { | ||
1785 | struct pginfo *info, **pd; | ||
1786 | u_long index; | ||
1787 | #ifdef MALLOC_EXTRA_SANITY | ||
1788 | u_long pidx; | ||
1789 | #endif /* MALLOC_EXTRA_SANITY */ | ||
1790 | struct pdinfo *pi; | ||
1791 | |||
1792 | if (!malloc_started) { | ||
1793 | wrtwarning("malloc() has never been called"); | ||
1794 | return; | ||
1795 | } | ||
1796 | /* If we're already sinking, don't make matters any worse. */ | ||
1797 | if (suicide) | ||
1798 | return; | ||
1799 | |||
1800 | if (malloc_ptrguard && PTR_ALIGNED(ptr)) | ||
1801 | ptr = (char *)ptr - PTR_GAP; | ||
1802 | 1041 | ||
1803 | index = ptr2index(ptr); | ||
1804 | 1042 | ||
1805 | if (index < malloc_pageshift) { | 1043 | static void * |
1806 | warnx("(%p)", ptr); | 1044 | omalloc(size_t sz, int zero_fill) |
1807 | wrtwarning("ifree: junk pointer, too low to make sense"); | 1045 | { |
1808 | return; | 1046 | void *p; |
1809 | } | 1047 | size_t psz; |
1810 | if (index > last_index) { | ||
1811 | warnx("(%p)", ptr); | ||
1812 | wrtwarning("ifree: junk pointer, too high to make sense"); | ||
1813 | return; | ||
1814 | } | ||
1815 | 1048 | ||
1816 | pdir_lookup(index, &pi); | 1049 | if (sz > MALLOC_MAXCHUNK) { |
1817 | #ifdef MALLOC_EXTRA_SANITY | 1050 | if (sz >= SIZE_MAX - malloc_guard - MALLOC_PAGESIZE) { |
1818 | pidx = PI_IDX(index); | 1051 | errno = ENOMEM; |
1819 | if (pi == NULL || PD_IDX(pi->dirnum) != pidx) { | 1052 | return NULL; |
1820 | wrterror("(ES): mapped pages not found in directory"); | 1053 | } |
1821 | errno = EFAULT; | 1054 | sz += malloc_guard; |
1822 | return; | 1055 | psz = PAGEROUND(sz); |
1823 | } | 1056 | p = map(&g_pool, psz); |
1824 | #endif /* MALLOC_EXTRA_SANITY */ | 1057 | if (p == MAP_FAILED) { |
1825 | if (pi != last_dir) { | 1058 | errno = ENOMEM; |
1826 | prev_dir = last_dir; | 1059 | return NULL; |
1827 | last_dir = pi; | 1060 | } |
1061 | if (insert(&g_pool, p, sz)) { | ||
1062 | unmap(&g_pool, p, sz); | ||
1063 | errno = ENOMEM; | ||
1064 | return NULL; | ||
1065 | } | ||
1066 | if (malloc_guard) { | ||
1067 | if (mprotect((char *)p + psz - malloc_guard, | ||
1068 | malloc_guard, PROT_NONE)) | ||
1069 | wrterror("mprotect"); | ||
1070 | malloc_guarded += malloc_guard; | ||
1071 | } | ||
1072 | if (malloc_junk) | ||
1073 | memset(p, SOME_JUNK, psz - malloc_guard); | ||
1074 | |||
1075 | /* shift towards the end */ | ||
1076 | if (malloc_move && | ||
1077 | sz - malloc_guard < MALLOC_PAGESIZE - MALLOC_MINSIZE) | ||
1078 | p = ((char *)p) + ((MALLOC_PAGESIZE - MALLOC_MINSIZE - | ||
1079 | (sz - malloc_guard)) & ~(MALLOC_MINSIZE-1)); | ||
1080 | if (zero_fill) | ||
1081 | memset(p, 0, sz - malloc_guard); | ||
1082 | } else { | ||
1083 | /* takes care of SOME_JUNK */ | ||
1084 | p = malloc_bytes(&g_pool, sz); | ||
1085 | if (zero_fill && p != NULL && sz > 0) | ||
1086 | memset(p, 0, sz); | ||
1828 | } | 1087 | } |
1829 | pd = pi->base; | ||
1830 | info = pd[PI_OFF(index)]; | ||
1831 | |||
1832 | if (info < MALLOC_MAGIC) | ||
1833 | free_pages(ptr, index, info); | ||
1834 | else | ||
1835 | free_bytes(ptr); | ||
1836 | |||
1837 | /* does not matter if alloc_pgfree fails */ | ||
1838 | if (px == NULL) | ||
1839 | px = alloc_pgfree(); | ||
1840 | 1088 | ||
1841 | return; | 1089 | return p; |
1842 | } | 1090 | } |
1843 | 1091 | ||
1844 | /* | 1092 | /* |
@@ -1846,10 +1094,10 @@ ifree(void *ptr) | |||
1846 | * print the error message once, to avoid making the problem | 1094 | * print the error message once, to avoid making the problem |
1847 | * potentially worse. | 1095 | * potentially worse. |
1848 | */ | 1096 | */ |
1849 | static void | 1097 | static void |
1850 | malloc_recurse(void) | 1098 | malloc_recurse(void) |
1851 | { | 1099 | { |
1852 | static int noprint; | 1100 | static int noprint; |
1853 | 1101 | ||
1854 | if (noprint == 0) { | 1102 | if (noprint == 0) { |
1855 | noprint = 1; | 1103 | noprint = 1; |
@@ -1860,74 +1108,218 @@ malloc_recurse(void) | |||
1860 | errno = EDEADLK; | 1108 | errno = EDEADLK; |
1861 | } | 1109 | } |
1862 | 1110 | ||
1863 | /* | ||
1864 | * These are the public exported interface routines. | ||
1865 | */ | ||
1866 | void * | 1111 | void * |
1867 | malloc(size_t size) | 1112 | malloc(size_t size) |
1868 | { | 1113 | { |
1869 | void *r; | 1114 | void *r; |
1870 | 1115 | ||
1871 | _MALLOC_LOCK(); | 1116 | _MALLOC_LOCK(); |
1872 | malloc_func = " in malloc():"; | 1117 | malloc_func = " in malloc():"; |
1118 | if (!g_pool.regions_total) { | ||
1119 | if (omalloc_init(&g_pool)) { | ||
1120 | _MALLOC_UNLOCK(); | ||
1121 | if (malloc_xmalloc) | ||
1122 | wrterror("out of memory"); | ||
1123 | errno = ENOMEM; | ||
1124 | return NULL; | ||
1125 | } | ||
1126 | } | ||
1873 | if (malloc_active++) { | 1127 | if (malloc_active++) { |
1874 | malloc_recurse(); | 1128 | malloc_recurse(); |
1875 | return (NULL); | 1129 | return NULL; |
1876 | } | 1130 | } |
1877 | r = imalloc(size); | 1131 | r = omalloc(size, malloc_zero); |
1878 | UTRACE(0, size, r); | ||
1879 | malloc_active--; | 1132 | malloc_active--; |
1880 | _MALLOC_UNLOCK(); | 1133 | _MALLOC_UNLOCK(); |
1881 | if (malloc_xmalloc && r == NULL) { | 1134 | if (r == NULL && malloc_xmalloc) { |
1882 | wrterror("out of memory"); | 1135 | wrterror("out of memory"); |
1883 | errno = ENOMEM; | 1136 | errno = ENOMEM; |
1884 | } | 1137 | } |
1885 | return (r); | 1138 | return r; |
1139 | } | ||
1140 | |||
1141 | static void | ||
1142 | ofree(void *p) | ||
1143 | { | ||
1144 | struct region_info *r; | ||
1145 | size_t sz; | ||
1146 | |||
1147 | r = find(&g_pool, p); | ||
1148 | if (r == NULL) { | ||
1149 | wrtwarning("bogus pointer (double free?)"); | ||
1150 | return; | ||
1151 | } | ||
1152 | REALSIZE(sz, r); | ||
1153 | if (sz > MALLOC_MAXCHUNK) { | ||
1154 | if (sz - malloc_guard >= MALLOC_PAGESIZE - MALLOC_MINSIZE) { | ||
1155 | if (r->p != p) | ||
1156 | wrtwarning("bogus pointer"); | ||
1157 | } else { | ||
1158 | #if notyetbecause_of_realloc | ||
1159 | /* shifted towards the end */ | ||
1160 | if (p != ((char *)r->p) + ((MALLOC_PAGESIZE - | ||
1161 | MALLOC_MINSIZE - sz - malloc_guard) & | ||
1162 | ~(MALLOC_MINSIZE-1))) { | ||
1163 | } | ||
1164 | #endif | ||
1165 | p = r->p; | ||
1166 | } | ||
1167 | if (malloc_guard) { | ||
1168 | if (sz < malloc_guard) | ||
1169 | wrtwarning("guard size"); | ||
1170 | if (!malloc_freeprot) { | ||
1171 | if (mprotect((char *)p + PAGEROUND(sz) - | ||
1172 | malloc_guard, malloc_guard, | ||
1173 | PROT_READ | PROT_WRITE)) | ||
1174 | wrterror("mprotect"); | ||
1175 | } | ||
1176 | malloc_guarded -= malloc_guard; | ||
1177 | } | ||
1178 | if (malloc_junk) | ||
1179 | memset(p, SOME_FREEJUNK, PAGEROUND(sz) - malloc_guard); | ||
1180 | unmap(&g_pool, p, sz); | ||
1181 | delete(&g_pool, r); | ||
1182 | } else { | ||
1183 | void *tmp; | ||
1184 | int i; | ||
1185 | |||
1186 | if (malloc_junk && sz > 0) | ||
1187 | memset(p, SOME_FREEJUNK, sz); | ||
1188 | i = getrbyte() & (MALLOC_DELAYED_CHUNKS - 1); | ||
1189 | tmp = p; | ||
1190 | p = g_pool.delayed_chunks[i]; | ||
1191 | g_pool.delayed_chunks[i] = tmp; | ||
1192 | if (p != NULL) { | ||
1193 | r = find(&g_pool, p); | ||
1194 | if (r == NULL) { | ||
1195 | wrtwarning("bogus pointer (double free?)"); | ||
1196 | return; | ||
1197 | } | ||
1198 | free_bytes(&g_pool, r, p); | ||
1199 | } | ||
1200 | } | ||
1886 | } | 1201 | } |
1887 | 1202 | ||
1888 | void | 1203 | void |
1889 | free(void *ptr) | 1204 | free(void *ptr) |
1890 | { | 1205 | { |
1891 | /* This is legal. XXX quick path */ | 1206 | /* This is legal. */ |
1892 | if (ptr == NULL) | 1207 | if (ptr == NULL) |
1893 | return; | 1208 | return; |
1894 | 1209 | ||
1895 | _MALLOC_LOCK(); | 1210 | _MALLOC_LOCK(); |
1896 | malloc_func = " in free():"; | 1211 | malloc_func = " in free():"; |
1897 | if (malloc_active++) { | 1212 | if (malloc_active++) { |
1898 | malloc_recurse(); | 1213 | malloc_recurse(); |
1899 | return; | 1214 | return; |
1900 | } | 1215 | } |
1901 | ifree(ptr); | 1216 | ofree(ptr); |
1902 | UTRACE(ptr, 0, 0); | ||
1903 | malloc_active--; | 1217 | malloc_active--; |
1904 | _MALLOC_UNLOCK(); | 1218 | _MALLOC_UNLOCK(); |
1905 | return; | 1219 | } |
1220 | |||
1221 | |||
1222 | static void * | ||
1223 | orealloc(void *p, size_t newsz) | ||
1224 | { | ||
1225 | struct region_info *r; | ||
1226 | size_t oldsz, goldsz, gnewsz; | ||
1227 | void *q; | ||
1228 | |||
1229 | if (p == NULL) | ||
1230 | return omalloc(newsz, 0); | ||
1231 | |||
1232 | r = find(&g_pool, p); | ||
1233 | if (r == NULL) { | ||
1234 | wrtwarning("bogus pointer (double free?)"); | ||
1235 | return NULL; | ||
1236 | } | ||
1237 | if (newsz >= SIZE_MAX - malloc_guard - MALLOC_PAGESIZE) { | ||
1238 | errno = ENOMEM; | ||
1239 | return NULL; | ||
1240 | } | ||
1241 | |||
1242 | REALSIZE(oldsz, r); | ||
1243 | goldsz = oldsz; | ||
1244 | if (oldsz > MALLOC_MAXCHUNK) { | ||
1245 | if (oldsz < malloc_guard) | ||
1246 | wrtwarning("guard size"); | ||
1247 | oldsz -= malloc_guard; | ||
1248 | } | ||
1249 | |||
1250 | gnewsz = newsz; | ||
1251 | if (gnewsz > MALLOC_MAXCHUNK) | ||
1252 | gnewsz += malloc_guard; | ||
1253 | |||
1254 | if (newsz > MALLOC_MAXCHUNK && oldsz > MALLOC_MAXCHUNK && p == r->p && | ||
1255 | !malloc_realloc) { | ||
1256 | size_t roldsz = PAGEROUND(goldsz); | ||
1257 | size_t rnewsz = PAGEROUND(gnewsz); | ||
1258 | |||
1259 | if (rnewsz < roldsz) { | ||
1260 | if (malloc_guard) { | ||
1261 | if (mprotect((char *)p + roldsz - malloc_guard, | ||
1262 | malloc_guard, PROT_READ | PROT_WRITE)) | ||
1263 | wrterror("mprotect"); | ||
1264 | if (mprotect((char *)p + rnewsz - malloc_guard, | ||
1265 | malloc_guard, PROT_NONE)) | ||
1266 | wrterror("mprotect"); | ||
1267 | } | ||
1268 | unmap(&g_pool, (char *)p + rnewsz, roldsz - rnewsz); | ||
1269 | r->size = gnewsz; | ||
1270 | return p; | ||
1271 | } else if (rnewsz == roldsz) { | ||
1272 | if (newsz > oldsz && malloc_junk) | ||
1273 | memset((char *)p + newsz, SOME_JUNK, | ||
1274 | rnewsz - malloc_guard - newsz); | ||
1275 | r->size = gnewsz; | ||
1276 | return p; | ||
1277 | } | ||
1278 | } | ||
1279 | if (newsz <= oldsz && newsz > oldsz / 2 && !malloc_realloc) { | ||
1280 | if (malloc_junk && newsz > 0) | ||
1281 | memset((char *)p + newsz, SOME_JUNK, oldsz - newsz); | ||
1282 | return p; | ||
1283 | } else if (newsz != oldsz || malloc_realloc) { | ||
1284 | q = omalloc(newsz, 0); | ||
1285 | if (q == NULL) | ||
1286 | return NULL; | ||
1287 | if (newsz != 0 && oldsz != 0) | ||
1288 | memcpy(q, p, oldsz < newsz ? oldsz : newsz); | ||
1289 | ofree(p); | ||
1290 | return q; | ||
1291 | } else | ||
1292 | return p; | ||
1906 | } | 1293 | } |
1907 | 1294 | ||
1908 | void * | 1295 | void * |
1909 | realloc(void *ptr, size_t size) | 1296 | realloc(void *ptr, size_t size) |
1910 | { | 1297 | { |
1911 | void *r; | 1298 | void *r; |
1912 | 1299 | ||
1913 | _MALLOC_LOCK(); | 1300 | _MALLOC_LOCK(); |
1914 | malloc_func = " in realloc():"; | 1301 | malloc_func = " in realloc():"; |
1302 | if (!g_pool.regions_total) { | ||
1303 | if (omalloc_init(&g_pool)) { | ||
1304 | _MALLOC_UNLOCK(); | ||
1305 | if (malloc_xmalloc) | ||
1306 | wrterror("out of memory"); | ||
1307 | errno = ENOMEM; | ||
1308 | return NULL; | ||
1309 | } | ||
1310 | } | ||
1915 | if (malloc_active++) { | 1311 | if (malloc_active++) { |
1916 | malloc_recurse(); | 1312 | malloc_recurse(); |
1917 | return (NULL); | 1313 | return NULL; |
1918 | } | 1314 | } |
1919 | 1315 | ||
1920 | if (ptr == NULL) | 1316 | r = orealloc(ptr, size); |
1921 | r = imalloc(size); | 1317 | |
1922 | else | ||
1923 | r = irealloc(ptr, size); | ||
1924 | |||
1925 | UTRACE(ptr, size, r); | ||
1926 | malloc_active--; | 1318 | malloc_active--; |
1927 | _MALLOC_UNLOCK(); | 1319 | _MALLOC_UNLOCK(); |
1928 | if (malloc_xmalloc && r == NULL) { | 1320 | if (r == NULL && malloc_xmalloc) { |
1929 | wrterror("out of memory"); | 1321 | wrterror("out of memory"); |
1930 | errno = ENOMEM; | 1322 | errno = ENOMEM; |
1931 | } | 1323 | } |
1932 | return (r); | 1324 | return r; |
1933 | } | 1325 | } |