|
@ -1,4 +1,4 @@ |
|
|
/* $OpenBSD: malloc.c,v 1.237 2017/12/27 10:05:23 otto Exp $ */ |
|
|
|
|
|
|
|
|
/* $OpenBSD: malloc.c,v 1.238 2018/01/01 12:41:48 otto Exp $ */ |
|
|
/* |
|
|
/* |
|
|
* Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net> |
|
|
* Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net> |
|
|
* Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> |
|
|
* Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> |
|
@ -203,10 +203,8 @@ static union { |
|
|
|
|
|
|
|
|
char *malloc_options; /* compile-time options */ |
|
|
char *malloc_options; /* compile-time options */ |
|
|
|
|
|
|
|
|
static u_char getrbyte(struct dir_info *d); |
|
|
|
|
|
static __dead void wrterror(struct dir_info *d, char *msg, ...) |
|
|
static __dead void wrterror(struct dir_info *d, char *msg, ...) |
|
|
__attribute__((__format__ (printf, 2, 3))); |
|
|
__attribute__((__format__ (printf, 2, 3))); |
|
|
static void fill_canary(char *ptr, size_t sz, size_t allocated); |
|
|
|
|
|
|
|
|
|
|
|
#ifdef MALLOC_STATS |
|
|
#ifdef MALLOC_STATS |
|
|
void malloc_dump(int, int, struct dir_info *); |
|
|
void malloc_dump(int, int, struct dir_info *); |
|
@ -312,178 +310,6 @@ getrbyte(struct dir_info *d) |
|
|
return x; |
|
|
return x; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* Cache maintenance. We keep at most malloc_cache pages cached. |
|
|
|
|
|
* If the cache is becoming full, unmap pages in the cache for real, |
|
|
|
|
|
* and then add the region to the cache |
|
|
|
|
|
* Opposed to the regular region data structure, the sizes in the |
|
|
|
|
|
* cache are in MALLOC_PAGESIZE units. |
|
|
|
|
|
*/ |
|
|
|
|
|
static void |
|
|
|
|
|
unmap(struct dir_info *d, void *p, size_t sz, int clear) |
|
|
|
|
|
{ |
|
|
|
|
|
size_t psz = sz >> MALLOC_PAGESHIFT; |
|
|
|
|
|
size_t rsz, tounmap; |
|
|
|
|
|
struct region_info *r; |
|
|
|
|
|
u_int i, offset; |
|
|
|
|
|
|
|
|
|
|
|
if (sz != PAGEROUND(sz)) |
|
|
|
|
|
wrterror(d, "munmap round"); |
|
|
|
|
|
|
|
|
|
|
|
rsz = mopts.malloc_cache - d->free_regions_size; |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* normally the cache holds recently freed regions, but if the region |
|
|
|
|
|
* to unmap is larger than the cache size or we're clearing and the |
|
|
|
|
|
* cache is full, just munmap |
|
|
|
|
|
*/ |
|
|
|
|
|
if (psz > mopts.malloc_cache || (clear && rsz == 0)) { |
|
|
|
|
|
i = munmap(p, sz); |
|
|
|
|
|
if (i) |
|
|
|
|
|
wrterror(d, "munmap %p", p); |
|
|
|
|
|
STATS_SUB(d->malloc_used, sz); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
tounmap = 0; |
|
|
|
|
|
if (psz > rsz) |
|
|
|
|
|
tounmap = psz - rsz; |
|
|
|
|
|
offset = getrbyte(d); |
|
|
|
|
|
for (i = 0; tounmap > 0 && i < mopts.malloc_cache; i++) { |
|
|
|
|
|
r = &d->free_regions[(i + offset) & (mopts.malloc_cache - 1)]; |
|
|
|
|
|
if (r->p != NULL) { |
|
|
|
|
|
rsz = r->size << MALLOC_PAGESHIFT; |
|
|
|
|
|
if (munmap(r->p, rsz)) |
|
|
|
|
|
wrterror(d, "munmap %p", r->p); |
|
|
|
|
|
r->p = NULL; |
|
|
|
|
|
if (tounmap > r->size) |
|
|
|
|
|
tounmap -= r->size; |
|
|
|
|
|
else |
|
|
|
|
|
tounmap = 0; |
|
|
|
|
|
d->free_regions_size -= r->size; |
|
|
|
|
|
r->size = 0; |
|
|
|
|
|
STATS_SUB(d->malloc_used, rsz); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (tounmap > 0) |
|
|
|
|
|
wrterror(d, "malloc cache underflow"); |
|
|
|
|
|
for (i = 0; i < mopts.malloc_cache; i++) { |
|
|
|
|
|
r = &d->free_regions[(i + offset) & (mopts.malloc_cache - 1)]; |
|
|
|
|
|
if (r->p == NULL) { |
|
|
|
|
|
if (clear) |
|
|
|
|
|
memset(p, 0, sz - mopts.malloc_guard); |
|
|
|
|
|
if (mopts.malloc_junk && !mopts.malloc_freeunmap) { |
|
|
|
|
|
size_t amt = mopts.malloc_junk == 1 ? |
|
|
|
|
|
MALLOC_MAXCHUNK : sz; |
|
|
|
|
|
memset(p, SOME_FREEJUNK, amt); |
|
|
|
|
|
} |
|
|
|
|
|
if (mopts.malloc_freeunmap) |
|
|
|
|
|
mprotect(p, sz, PROT_NONE); |
|
|
|
|
|
r->p = p; |
|
|
|
|
|
r->size = psz; |
|
|
|
|
|
d->free_regions_size += psz; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (i == mopts.malloc_cache) |
|
|
|
|
|
wrterror(d, "malloc free slot lost"); |
|
|
|
|
|
if (d->free_regions_size > mopts.malloc_cache) |
|
|
|
|
|
wrterror(d, "malloc cache overflow"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
|
|
|
zapcacheregion(struct dir_info *d, void *p, size_t len) |
|
|
|
|
|
{ |
|
|
|
|
|
u_int i; |
|
|
|
|
|
struct region_info *r; |
|
|
|
|
|
size_t rsz; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < mopts.malloc_cache; i++) { |
|
|
|
|
|
r = &d->free_regions[i]; |
|
|
|
|
|
if (r->p >= p && r->p <= (void *)((char *)p + len)) { |
|
|
|
|
|
rsz = r->size << MALLOC_PAGESHIFT; |
|
|
|
|
|
if (munmap(r->p, rsz)) |
|
|
|
|
|
wrterror(d, "munmap %p", r->p); |
|
|
|
|
|
r->p = NULL; |
|
|
|
|
|
d->free_regions_size -= r->size; |
|
|
|
|
|
r->size = 0; |
|
|
|
|
|
STATS_SUB(d->malloc_used, rsz); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void * |
|
|
|
|
|
map(struct dir_info *d, void *hint, size_t sz, int zero_fill) |
|
|
|
|
|
{ |
|
|
|
|
|
size_t psz = sz >> MALLOC_PAGESHIFT; |
|
|
|
|
|
struct region_info *r, *big = NULL; |
|
|
|
|
|
u_int i; |
|
|
|
|
|
void *p; |
|
|
|
|
|
|
|
|
|
|
|
if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || |
|
|
|
|
|
d->canary1 != ~d->canary2) |
|
|
|
|
|
wrterror(d, "internal struct corrupt"); |
|
|
|
|
|
if (sz != PAGEROUND(sz)) |
|
|
|
|
|
wrterror(d, "map round"); |
|
|
|
|
|
|
|
|
|
|
|
if (hint == NULL && psz > d->free_regions_size) { |
|
|
|
|
|
_MALLOC_LEAVE(d); |
|
|
|
|
|
p = MMAP(sz); |
|
|
|
|
|
_MALLOC_ENTER(d); |
|
|
|
|
|
if (p != MAP_FAILED) |
|
|
|
|
|
STATS_ADD(d->malloc_used, sz); |
|
|
|
|
|
/* zero fill not needed */ |
|
|
|
|
|
return p; |
|
|
|
|
|
} |
|
|
|
|
|
for (i = 0; i < mopts.malloc_cache; i++) { |
|
|
|
|
|
r = &d->free_regions[(i + d->rotor) & (mopts.malloc_cache - 1)]; |
|
|
|
|
|
if (r->p != NULL) { |
|
|
|
|
|
if (hint != NULL && r->p != hint) |
|
|
|
|
|
continue; |
|
|
|
|
|
if (r->size == psz) { |
|
|
|
|
|
p = r->p; |
|
|
|
|
|
r->p = NULL; |
|
|
|
|
|
r->size = 0; |
|
|
|
|
|
d->free_regions_size -= psz; |
|
|
|
|
|
if (mopts.malloc_freeunmap) |
|
|
|
|
|
mprotect(p, sz, PROT_READ | PROT_WRITE); |
|
|
|
|
|
if (zero_fill) |
|
|
|
|
|
memset(p, 0, sz); |
|
|
|
|
|
else if (mopts.malloc_junk == 2 && |
|
|
|
|
|
mopts.malloc_freeunmap) |
|
|
|
|
|
memset(p, SOME_FREEJUNK, sz); |
|
|
|
|
|
d->rotor += i + 1; |
|
|
|
|
|
return p; |
|
|
|
|
|
} else if (r->size > psz) |
|
|
|
|
|
big = r; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (big != NULL) { |
|
|
|
|
|
r = big; |
|
|
|
|
|
p = r->p; |
|
|
|
|
|
r->p = (char *)r->p + (psz << MALLOC_PAGESHIFT); |
|
|
|
|
|
if (mopts.malloc_freeunmap) |
|
|
|
|
|
mprotect(p, sz, PROT_READ | PROT_WRITE); |
|
|
|
|
|
r->size -= psz; |
|
|
|
|
|
d->free_regions_size -= psz; |
|
|
|
|
|
if (zero_fill) |
|
|
|
|
|
memset(p, 0, sz); |
|
|
|
|
|
else if (mopts.malloc_junk == 2 && mopts.malloc_freeunmap) |
|
|
|
|
|
memset(p, SOME_FREEJUNK, sz); |
|
|
|
|
|
return p; |
|
|
|
|
|
} |
|
|
|
|
|
if (hint != NULL) |
|
|
|
|
|
return MAP_FAILED; |
|
|
|
|
|
if (d->free_regions_size > mopts.malloc_cache) |
|
|
|
|
|
wrterror(d, "malloc cache"); |
|
|
|
|
|
_MALLOC_LEAVE(d); |
|
|
|
|
|
p = MMAP(sz); |
|
|
|
|
|
_MALLOC_ENTER(d); |
|
|
|
|
|
if (p != MAP_FAILED) |
|
|
|
|
|
STATS_ADD(d->malloc_used, sz); |
|
|
|
|
|
/* zero fill not needed */ |
|
|
|
|
|
return p; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
static void |
|
|
omalloc_parseopt(char opt) |
|
|
omalloc_parseopt(char opt) |
|
|
{ |
|
|
{ |
|
@ -714,43 +540,6 @@ omalloc_grow(struct dir_info *d) |
|
|
return 0; |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static struct chunk_info * |
|
|
|
|
|
alloc_chunk_info(struct dir_info *d, int bits) |
|
|
|
|
|
{ |
|
|
|
|
|
struct chunk_info *p; |
|
|
|
|
|
|
|
|
|
|
|
if (LIST_EMPTY(&d->chunk_info_list[bits])) { |
|
|
|
|
|
size_t size, count, i; |
|
|
|
|
|
char *q; |
|
|
|
|
|
|
|
|
|
|
|
if (bits == 0) |
|
|
|
|
|
count = MALLOC_PAGESIZE / MALLOC_MINSIZE; |
|
|
|
|
|
else |
|
|
|
|
|
count = MALLOC_PAGESIZE >> bits; |
|
|
|
|
|
|
|
|
|
|
|
size = howmany(count, MALLOC_BITS); |
|
|
|
|
|
size = sizeof(struct chunk_info) + (size - 1) * sizeof(u_short); |
|
|
|
|
|
if (mopts.chunk_canaries) |
|
|
|
|
|
size += count * sizeof(u_short); |
|
|
|
|
|
size = ALIGN(size); |
|
|
|
|
|
|
|
|
|
|
|
q = MMAP(MALLOC_PAGESIZE); |
|
|
|
|
|
if (q == MAP_FAILED) |
|
|
|
|
|
return NULL; |
|
|
|
|
|
STATS_ADD(d->malloc_used, MALLOC_PAGESIZE); |
|
|
|
|
|
count = MALLOC_PAGESIZE / size; |
|
|
|
|
|
for (i = 0; i < count; i++, q += size) { |
|
|
|
|
|
p = (struct chunk_info *)q; |
|
|
|
|
|
p->canary = (u_short)d->canary1; |
|
|
|
|
|
LIST_INSERT_HEAD(&d->chunk_info_list[bits], p, entries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
p = LIST_FIRST(&d->chunk_info_list[bits]); |
|
|
|
|
|
LIST_REMOVE(p, entries); |
|
|
|
|
|
return p; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
/* |
|
|
* The hashtable uses the assumption that p is never NULL. This holds since |
|
|
* The hashtable uses the assumption that p is never NULL. This holds since |
|
|
* non-MAP_FIXED mappings with hint 0 start at BRKSIZ. |
|
|
* non-MAP_FIXED mappings with hint 0 start at BRKSIZ. |
|
@ -842,72 +631,277 @@ delete(struct dir_info *d, struct region_info *ri) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
|
/* |
|
|
* Allocate a page of chunks |
|
|
|
|
|
|
|
|
* Cache maintenance. We keep at most malloc_cache pages cached. |
|
|
|
|
|
* If the cache is becoming full, unmap pages in the cache for real, |
|
|
|
|
|
* and then add the region to the cache |
|
|
|
|
|
* Opposed to the regular region data structure, the sizes in the |
|
|
|
|
|
* cache are in MALLOC_PAGESIZE units. |
|
|
*/ |
|
|
*/ |
|
|
static struct chunk_info * |
|
|
|
|
|
omalloc_make_chunks(struct dir_info *d, int bits, int listnum) |
|
|
|
|
|
|
|
|
static void |
|
|
|
|
|
unmap(struct dir_info *d, void *p, size_t sz, int clear) |
|
|
{ |
|
|
{ |
|
|
struct chunk_info *bp; |
|
|
|
|
|
void *pp; |
|
|
|
|
|
int i, k; |
|
|
|
|
|
|
|
|
size_t psz = sz >> MALLOC_PAGESHIFT; |
|
|
|
|
|
size_t rsz, tounmap; |
|
|
|
|
|
struct region_info *r; |
|
|
|
|
|
u_int i, offset; |
|
|
|
|
|
|
|
|
/* Allocate a new bucket */ |
|
|
|
|
|
pp = map(d, NULL, MALLOC_PAGESIZE, 0); |
|
|
|
|
|
if (pp == MAP_FAILED) |
|
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
if (sz != PAGEROUND(sz)) |
|
|
|
|
|
wrterror(d, "munmap round"); |
|
|
|
|
|
|
|
|
bp = alloc_chunk_info(d, bits); |
|
|
|
|
|
if (bp == NULL) { |
|
|
|
|
|
unmap(d, pp, MALLOC_PAGESIZE, 0); |
|
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
rsz = mopts.malloc_cache - d->free_regions_size; |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* normally the cache holds recently freed regions, but if the region |
|
|
|
|
|
* to unmap is larger than the cache size or we're clearing and the |
|
|
|
|
|
* cache is full, just munmap |
|
|
|
|
|
*/ |
|
|
|
|
|
if (psz > mopts.malloc_cache || (clear && rsz == 0)) { |
|
|
|
|
|
i = munmap(p, sz); |
|
|
|
|
|
if (i) |
|
|
|
|
|
wrterror(d, "munmap %p", p); |
|
|
|
|
|
STATS_SUB(d->malloc_used, sz); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
tounmap = 0; |
|
|
|
|
|
if (psz > rsz) |
|
|
|
|
|
tounmap = psz - rsz; |
|
|
|
|
|
offset = getrbyte(d); |
|
|
|
|
|
for (i = 0; tounmap > 0 && i < mopts.malloc_cache; i++) { |
|
|
|
|
|
r = &d->free_regions[(i + offset) & (mopts.malloc_cache - 1)]; |
|
|
|
|
|
if (r->p != NULL) { |
|
|
|
|
|
rsz = r->size << MALLOC_PAGESHIFT; |
|
|
|
|
|
if (munmap(r->p, rsz)) |
|
|
|
|
|
wrterror(d, "munmap %p", r->p); |
|
|
|
|
|
r->p = NULL; |
|
|
|
|
|
if (tounmap > r->size) |
|
|
|
|
|
tounmap -= r->size; |
|
|
|
|
|
else |
|
|
|
|
|
tounmap = 0; |
|
|
|
|
|
d->free_regions_size -= r->size; |
|
|
|
|
|
r->size = 0; |
|
|
|
|
|
STATS_SUB(d->malloc_used, rsz); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (tounmap > 0) |
|
|
|
|
|
wrterror(d, "malloc cache underflow"); |
|
|
|
|
|
for (i = 0; i < mopts.malloc_cache; i++) { |
|
|
|
|
|
r = &d->free_regions[(i + offset) & (mopts.malloc_cache - 1)]; |
|
|
|
|
|
if (r->p == NULL) { |
|
|
|
|
|
if (clear) |
|
|
|
|
|
memset(p, 0, sz - mopts.malloc_guard); |
|
|
|
|
|
if (mopts.malloc_junk && !mopts.malloc_freeunmap) { |
|
|
|
|
|
size_t amt = mopts.malloc_junk == 1 ? |
|
|
|
|
|
MALLOC_MAXCHUNK : sz; |
|
|
|
|
|
memset(p, SOME_FREEJUNK, amt); |
|
|
|
|
|
} |
|
|
|
|
|
if (mopts.malloc_freeunmap) |
|
|
|
|
|
mprotect(p, sz, PROT_NONE); |
|
|
|
|
|
r->p = p; |
|
|
|
|
|
r->size = psz; |
|
|
|
|
|
d->free_regions_size += psz; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (i == mopts.malloc_cache) |
|
|
|
|
|
wrterror(d, "malloc free slot lost"); |
|
|
|
|
|
if (d->free_regions_size > mopts.malloc_cache) |
|
|
|
|
|
wrterror(d, "malloc cache overflow"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
|
|
|
zapcacheregion(struct dir_info *d, void *p, size_t len) |
|
|
|
|
|
{ |
|
|
|
|
|
u_int i; |
|
|
|
|
|
struct region_info *r; |
|
|
|
|
|
size_t rsz; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < mopts.malloc_cache; i++) { |
|
|
|
|
|
r = &d->free_regions[i]; |
|
|
|
|
|
if (r->p >= p && r->p <= (void *)((char *)p + len)) { |
|
|
|
|
|
rsz = r->size << MALLOC_PAGESHIFT; |
|
|
|
|
|
if (munmap(r->p, rsz)) |
|
|
|
|
|
wrterror(d, "munmap %p", r->p); |
|
|
|
|
|
r->p = NULL; |
|
|
|
|
|
d->free_regions_size -= r->size; |
|
|
|
|
|
r->size = 0; |
|
|
|
|
|
STATS_SUB(d->malloc_used, rsz); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void * |
|
|
|
|
|
map(struct dir_info *d, void *hint, size_t sz, int zero_fill) |
|
|
|
|
|
{ |
|
|
|
|
|
size_t psz = sz >> MALLOC_PAGESHIFT; |
|
|
|
|
|
struct region_info *r, *big = NULL; |
|
|
|
|
|
u_int i; |
|
|
|
|
|
void *p; |
|
|
|
|
|
|
|
|
|
|
|
if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || |
|
|
|
|
|
d->canary1 != ~d->canary2) |
|
|
|
|
|
wrterror(d, "internal struct corrupt"); |
|
|
|
|
|
if (sz != PAGEROUND(sz)) |
|
|
|
|
|
wrterror(d, "map round"); |
|
|
|
|
|
|
|
|
|
|
|
if (hint == NULL && psz > d->free_regions_size) { |
|
|
|
|
|
_MALLOC_LEAVE(d); |
|
|
|
|
|
p = MMAP(sz); |
|
|
|
|
|
_MALLOC_ENTER(d); |
|
|
|
|
|
if (p != MAP_FAILED) |
|
|
|
|
|
STATS_ADD(d->malloc_used, sz); |
|
|
|
|
|
/* zero fill not needed */ |
|
|
|
|
|
return p; |
|
|
|
|
|
} |
|
|
|
|
|
for (i = 0; i < mopts.malloc_cache; i++) { |
|
|
|
|
|
r = &d->free_regions[(i + d->rotor) & (mopts.malloc_cache - 1)]; |
|
|
|
|
|
if (r->p != NULL) { |
|
|
|
|
|
if (hint != NULL && r->p != hint) |
|
|
|
|
|
continue; |
|
|
|
|
|
if (r->size == psz) { |
|
|
|
|
|
p = r->p; |
|
|
|
|
|
r->p = NULL; |
|
|
|
|
|
r->size = 0; |
|
|
|
|
|
d->free_regions_size -= psz; |
|
|
|
|
|
if (mopts.malloc_freeunmap) |
|
|
|
|
|
mprotect(p, sz, PROT_READ | PROT_WRITE); |
|
|
|
|
|
if (zero_fill) |
|
|
|
|
|
memset(p, 0, sz); |
|
|
|
|
|
else if (mopts.malloc_junk == 2 && |
|
|
|
|
|
mopts.malloc_freeunmap) |
|
|
|
|
|
memset(p, SOME_FREEJUNK, sz); |
|
|
|
|
|
d->rotor += i + 1; |
|
|
|
|
|
return p; |
|
|
|
|
|
} else if (r->size > psz) |
|
|
|
|
|
big = r; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (big != NULL) { |
|
|
|
|
|
r = big; |
|
|
|
|
|
p = r->p; |
|
|
|
|
|
r->p = (char *)r->p + (psz << MALLOC_PAGESHIFT); |
|
|
|
|
|
if (mopts.malloc_freeunmap) |
|
|
|
|
|
mprotect(p, sz, PROT_READ | PROT_WRITE); |
|
|
|
|
|
r->size -= psz; |
|
|
|
|
|
d->free_regions_size -= psz; |
|
|
|
|
|
if (zero_fill) |
|
|
|
|
|
memset(p, 0, sz); |
|
|
|
|
|
else if (mopts.malloc_junk == 2 && mopts.malloc_freeunmap) |
|
|
|
|
|
memset(p, SOME_FREEJUNK, sz); |
|
|
|
|
|
return p; |
|
|
|
|
|
} |
|
|
|
|
|
if (hint != NULL) |
|
|
|
|
|
return MAP_FAILED; |
|
|
|
|
|
if (d->free_regions_size > mopts.malloc_cache) |
|
|
|
|
|
wrterror(d, "malloc cache"); |
|
|
|
|
|
_MALLOC_LEAVE(d); |
|
|
|
|
|
p = MMAP(sz); |
|
|
|
|
|
_MALLOC_ENTER(d); |
|
|
|
|
|
if (p != MAP_FAILED) |
|
|
|
|
|
STATS_ADD(d->malloc_used, sz); |
|
|
|
|
|
/* zero fill not needed */ |
|
|
|
|
|
return p; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
|
|
|
init_chunk_info(struct dir_info *d, struct chunk_info *p, int bits) |
|
|
|
|
|
{ |
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
/* memory protect the page allocated in the malloc(0) case */ |
|
|
|
|
|
if (bits == 0) { |
|
|
if (bits == 0) { |
|
|
bp->size = 0; |
|
|
|
|
|
bp->shift = 1; |
|
|
|
|
|
|
|
|
p->shift = 1; |
|
|
i = MALLOC_MINSIZE - 1; |
|
|
i = MALLOC_MINSIZE - 1; |
|
|
while (i >>= 1) |
|
|
while (i >>= 1) |
|
|
bp->shift++; |
|
|
|
|
|
bp->total = bp->free = MALLOC_PAGESIZE >> bp->shift; |
|
|
|
|
|
bp->offset = 0xdead; |
|
|
|
|
|
bp->page = pp; |
|
|
|
|
|
|
|
|
|
|
|
k = mprotect(pp, MALLOC_PAGESIZE, PROT_NONE); |
|
|
|
|
|
if (k < 0) { |
|
|
|
|
|
unmap(d, pp, MALLOC_PAGESIZE, 0); |
|
|
|
|
|
LIST_INSERT_HEAD(&d->chunk_info_list[0], bp, entries); |
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
p->shift++; |
|
|
|
|
|
p->total = p->free = MALLOC_PAGESIZE >> p->shift; |
|
|
|
|
|
p->size = 0; |
|
|
|
|
|
p->offset = 0xdead; |
|
|
} else { |
|
|
} else { |
|
|
bp->size = 1U << bits; |
|
|
|
|
|
bp->shift = bits; |
|
|
|
|
|
bp->total = bp->free = MALLOC_PAGESIZE >> bits; |
|
|
|
|
|
bp->offset = howmany(bp->total, MALLOC_BITS); |
|
|
|
|
|
bp->page = pp; |
|
|
|
|
|
|
|
|
p->shift = bits; |
|
|
|
|
|
p->total = p->free = MALLOC_PAGESIZE >> p->shift; |
|
|
|
|
|
p->size = 1U << bits; |
|
|
|
|
|
p->offset = howmany(p->total, MALLOC_BITS); |
|
|
} |
|
|
} |
|
|
|
|
|
p->canary = (u_short)d->canary1; |
|
|
|
|
|
|
|
|
/* set all valid bits in the bitmap */ |
|
|
/* set all valid bits in the bitmap */ |
|
|
k = bp->total; |
|
|
|
|
|
i = 0; |
|
|
|
|
|
|
|
|
for (i = 0; p->total - i >= MALLOC_BITS; i += MALLOC_BITS) |
|
|
|
|
|
p->bits[i / MALLOC_BITS] = (u_short)~0U; |
|
|
|
|
|
|
|
|
/* Do a bunch at a time */ |
|
|
|
|
|
for (; (k - i) >= MALLOC_BITS; i += MALLOC_BITS) |
|
|
|
|
|
bp->bits[i / MALLOC_BITS] = (u_short)~0U; |
|
|
|
|
|
|
|
|
if (i < p->total) |
|
|
|
|
|
p->bits[i / MALLOC_BITS] = 0; |
|
|
|
|
|
for (; i < p->total; i++) |
|
|
|
|
|
p->bits[i / MALLOC_BITS] |= (u_short)1U << (i % MALLOC_BITS); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (i < k) |
|
|
|
|
|
bp->bits[i / MALLOC_BITS] = 0; |
|
|
|
|
|
for (; i < k; i++) |
|
|
|
|
|
bp->bits[i / MALLOC_BITS] |= (u_short)1U << (i % MALLOC_BITS); |
|
|
|
|
|
|
|
|
static struct chunk_info * |
|
|
|
|
|
alloc_chunk_info(struct dir_info *d, int bits) |
|
|
|
|
|
{ |
|
|
|
|
|
struct chunk_info *p; |
|
|
|
|
|
|
|
|
LIST_INSERT_HEAD(&d->chunk_dir[bits][listnum], bp, entries); |
|
|
|
|
|
|
|
|
if (LIST_EMPTY(&d->chunk_info_list[bits])) { |
|
|
|
|
|
size_t size, count, i; |
|
|
|
|
|
char *q; |
|
|
|
|
|
|
|
|
|
|
|
if (bits == 0) |
|
|
|
|
|
count = MALLOC_PAGESIZE / MALLOC_MINSIZE; |
|
|
|
|
|
else |
|
|
|
|
|
count = MALLOC_PAGESIZE >> bits; |
|
|
|
|
|
|
|
|
bits++; |
|
|
|
|
|
if ((uintptr_t)pp & bits) |
|
|
|
|
|
wrterror(d, "pp & bits %p", pp); |
|
|
|
|
|
|
|
|
size = howmany(count, MALLOC_BITS); |
|
|
|
|
|
size = sizeof(struct chunk_info) + (size - 1) * sizeof(u_short); |
|
|
|
|
|
if (mopts.chunk_canaries) |
|
|
|
|
|
size += count * sizeof(u_short); |
|
|
|
|
|
size = ALIGN(size); |
|
|
|
|
|
|
|
|
insert(d, (void *)((uintptr_t)pp | bits), (uintptr_t)bp, NULL); |
|
|
|
|
|
|
|
|
q = MMAP(MALLOC_PAGESIZE); |
|
|
|
|
|
if (q == MAP_FAILED) |
|
|
|
|
|
return NULL; |
|
|
|
|
|
STATS_ADD(d->malloc_used, MALLOC_PAGESIZE); |
|
|
|
|
|
count = MALLOC_PAGESIZE / size; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++, q += size) { |
|
|
|
|
|
p = (struct chunk_info *)q; |
|
|
|
|
|
LIST_INSERT_HEAD(&d->chunk_info_list[bits], p, entries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
p = LIST_FIRST(&d->chunk_info_list[bits]); |
|
|
|
|
|
LIST_REMOVE(p, entries); |
|
|
|
|
|
if (p->shift == 0) |
|
|
|
|
|
init_chunk_info(d, p, bits); |
|
|
|
|
|
return p; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* Allocate a page of chunks |
|
|
|
|
|
*/ |
|
|
|
|
|
static struct chunk_info * |
|
|
|
|
|
omalloc_make_chunks(struct dir_info *d, int bits, int listnum) |
|
|
|
|
|
{ |
|
|
|
|
|
struct chunk_info *bp; |
|
|
|
|
|
void *pp; |
|
|
|
|
|
|
|
|
|
|
|
/* Allocate a new bucket */ |
|
|
|
|
|
pp = map(d, NULL, MALLOC_PAGESIZE, 0); |
|
|
|
|
|
if (pp == MAP_FAILED) |
|
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
|
|
|
/* memory protect the page allocated in the malloc(0) case */ |
|
|
|
|
|
if (bits == 0 && mprotect(pp, MALLOC_PAGESIZE, PROT_NONE) < 0) |
|
|
|
|
|
goto err; |
|
|
|
|
|
|
|
|
|
|
|
bp = alloc_chunk_info(d, bits); |
|
|
|
|
|
if (bp == NULL) |
|
|
|
|
|
goto err; |
|
|
|
|
|
bp->page = pp; |
|
|
|
|
|
|
|
|
|
|
|
if (insert(d, (void *)((uintptr_t)pp | bits + 1), (uintptr_t)bp, NULL)) |
|
|
|
|
|
goto err; |
|
|
|
|
|
LIST_INSERT_HEAD(&d->chunk_dir[bits][listnum], bp, entries); |
|
|
return bp; |
|
|
return bp; |
|
|
|
|
|
|
|
|
|
|
|
err: |
|
|
|
|
|
unmap(d, pp, MALLOC_PAGESIZE, 0); |
|
|
|
|
|
return NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static int |
|
|
static int |
|
@ -932,16 +926,26 @@ find_chunksize(size_t size) |
|
|
return j; |
|
|
return j; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
|
|
|
fill_canary(char *ptr, size_t sz, size_t allocated) |
|
|
|
|
|
{ |
|
|
|
|
|
size_t check_sz = allocated - sz; |
|
|
|
|
|
|
|
|
|
|
|
if (check_sz > CHUNK_CHECK_LENGTH) |
|
|
|
|
|
check_sz = CHUNK_CHECK_LENGTH; |
|
|
|
|
|
memset(ptr + sz, SOME_JUNK, check_sz); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
|
/* |
|
|
* Allocate a chunk |
|
|
* Allocate a chunk |
|
|
*/ |
|
|
*/ |
|
|
static void * |
|
|
static void * |
|
|
malloc_bytes(struct dir_info *d, size_t size, void *f) |
|
|
malloc_bytes(struct dir_info *d, size_t size, void *f) |
|
|
{ |
|
|
{ |
|
|
u_int i, r; |
|
|
|
|
|
int j, listnum; |
|
|
|
|
|
size_t k; |
|
|
|
|
|
u_short u, b, *lp; |
|
|
|
|
|
|
|
|
u_int i, r; |
|
|
|
|
|
int j, listnum; |
|
|
|
|
|
size_t k; |
|
|
|
|
|
u_short u, b, *lp; |
|
|
struct chunk_info *bp; |
|
|
struct chunk_info *bp; |
|
|
void *p; |
|
|
void *p; |
|
|
|
|
|
|
|
@ -1030,16 +1034,6 @@ found: |
|
|
return p; |
|
|
return p; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static void |
|
|
|
|
|
fill_canary(char *ptr, size_t sz, size_t allocated) |
|
|
|
|
|
{ |
|
|
|
|
|
size_t check_sz = allocated - sz; |
|
|
|
|
|
|
|
|
|
|
|
if (check_sz > CHUNK_CHECK_LENGTH) |
|
|
|
|
|
check_sz = CHUNK_CHECK_LENGTH; |
|
|
|
|
|
memset(ptr + sz, SOME_JUNK, check_sz); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
static void |
|
|
validate_canary(struct dir_info *d, u_char *ptr, size_t sz, size_t allocated) |
|
|
validate_canary(struct dir_info *d, u_char *ptr, size_t sz, size_t allocated) |
|
|
{ |
|
|
{ |
|
|