diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index 7e7364f4..2a05d806 100644 --- a/src/lib/libc/stdlib/malloc.c +++ b/src/lib/libc/stdlib/malloc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: malloc.c,v 1.202 2016/10/15 18:24:40 guenther Exp $ */ +/* $OpenBSD: malloc.c,v 1.203 2016/10/20 05:38:41 otto Exp $ */ /* * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek * Copyright (c) 2012 Matthew Dempsky @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -199,6 +200,8 @@ static union { char *malloc_options; /* compile-time options */ static u_char getrbyte(struct dir_info *d); +static __dead void wrterror(struct dir_info *d, char *msg, ...) + __attribute__((__format__ (printf, 2, 3))); #ifdef MALLOC_STATS void malloc_dump(int, struct dir_info *); @@ -261,40 +264,26 @@ struct dir_info *getpool(void) } static __dead void -wrterror(struct dir_info *d, char *msg, void *p) +wrterror(struct dir_info *d, char *msg, ...) { - char *q = " error: "; - struct iovec iov[7]; - char pidbuf[20]; - char buf[20]; - int saved_errno = errno, i; - - iov[0].iov_base = __progname; - iov[0].iov_len = strlen(__progname); - iov[1].iov_base = pidbuf; - snprintf(pidbuf, sizeof(pidbuf), "(%d) in ", getpid()); - iov[1].iov_len = strlen(pidbuf); - if (d != NULL) { - iov[2].iov_base = d->func; - iov[2].iov_len = strlen(d->func); - } else { - iov[2].iov_base = "unknown"; - iov[2].iov_len = 7; - } - iov[3].iov_base = q; - iov[3].iov_len = strlen(q); - iov[4].iov_base = msg; - iov[4].iov_len = strlen(msg); - iov[5].iov_base = buf; - if (p == NULL) - iov[5].iov_len = 0; - else { - snprintf(buf, sizeof(buf), " %010p", p); - iov[5].iov_len = strlen(buf); - } - iov[6].iov_base = "\n"; - iov[6].iov_len = 1; - writev(STDERR_FILENO, iov, 7); + struct iovec iov[3]; + char pidbuf[80]; + char buf[80]; + int saved_errno = errno, ret; + va_list ap; + + iov[0].iov_base = pidbuf; + ret = snprintf(pidbuf, sizeof(pidbuf), "%.50s(%d) in %s(): ", + __progname, getpid(), d->func ? d->func : "unknown"); + iov[0].iov_len = ret > 0 ? strlen(pidbuf) : 0; + iov[1].iov_base = buf; + va_start(ap, msg); + ret = vsnprintf(buf, sizeof(buf), msg, ap); + va_end(ap); + iov[1].iov_len = ret > 0 ? strlen(buf) : 0; + iov[2].iov_base = "\n"; + iov[2].iov_len = 1; + writev(STDERR_FILENO, iov, 3); #ifdef MALLOC_STATS if (mopts.malloc_stats) @@ -342,12 +331,12 @@ unmap(struct dir_info *d, void *p, size_t sz) u_int i, offset; if (sz != PAGEROUND(sz)) - wrterror(d, "munmap round", NULL); + wrterror(d, "munmap round"); if (psz > mopts.malloc_cache) { i = munmap(p, sz); if (i) - wrterror(d, "munmap", p); + wrterror(d, "munmap %p", p); STATS_SUB(d->malloc_used, sz); return; } @@ -361,7 +350,7 @@ unmap(struct dir_info *d, void *p, size_t sz) if (r->p != NULL) { rsz = r->size << MALLOC_PAGESHIFT; if (munmap(r->p, rsz)) - wrterror(d, "munmap", r->p); + wrterror(d, "munmap %p", r->p); r->p = NULL; if (tounmap > r->size) tounmap -= r->size; @@ -373,7 +362,7 @@ unmap(struct dir_info *d, void *p, size_t sz) } } if (tounmap > 0) - wrterror(d, "malloc cache underflow", NULL); + 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) { @@ -393,9 +382,9 @@ unmap(struct dir_info *d, void *p, size_t sz) } } if (i == mopts.malloc_cache) - wrterror(d, "malloc free slot lost", NULL); + wrterror(d, "malloc free slot lost"); if (d->free_regions_size > mopts.malloc_cache) - wrterror(d, "malloc cache overflow", NULL); + wrterror(d, "malloc cache overflow"); } static void @@ -410,7 +399,7 @@ zapcacheregion(struct dir_info *d, void *p, size_t len) if (r->p >= p && r->p <= (void *)((char *)p + len)) { rsz = r->size << MALLOC_PAGESHIFT; if (munmap(r->p, rsz)) - wrterror(d, "munmap", r->p); + wrterror(d, "munmap %p", r->p); r->p = NULL; d->free_regions_size -= r->size; r->size = 0; @@ -429,9 +418,9 @@ map(struct dir_info *d, void *hint, size_t sz, int zero_fill) if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || d->canary1 != ~d->canary2) - wrterror(d, "internal struct corrupt", NULL); + wrterror(d, "internal struct corrupt"); if (sz != PAGEROUND(sz)) - wrterror(d, "map round", NULL); + wrterror(d, "map round"); if (!hint && psz > d->free_regions_size) { _MALLOC_LEAVE(d); @@ -486,7 +475,7 @@ map(struct dir_info *d, void *hint, size_t sz, int zero_fill) if (hint) return MAP_FAILED; if (d->free_regions_size > mopts.malloc_cache) - wrterror(d, "malloc cache", NULL); + wrterror(d, "malloc cache"); _MALLOC_LEAVE(d); p = MMAP(sz); _MALLOC_ENTER(d); @@ -673,7 +662,7 @@ omalloc_poolinit(struct dir_info **dp) * lies (subject to alignment by 1 << MALLOC_MINSHIFT) */ if ((p = MMAP(DIR_INFO_RSZ + (MALLOC_PAGESIZE * 2))) == MAP_FAILED) - wrterror(NULL, "malloc init mmap failed", NULL); + wrterror(NULL, "malloc init mmap failed"); mprotect(p, MALLOC_PAGESIZE, PROT_NONE); mprotect(p + MALLOC_PAGESIZE + DIR_INFO_RSZ, MALLOC_PAGESIZE, PROT_NONE); @@ -687,7 +676,7 @@ omalloc_poolinit(struct dir_info **dp) d->r = MMAP(regioninfo_size); if (d->r == MAP_FAILED) { d->regions_total = 0; - wrterror(NULL, "malloc init mmap failed", NULL); + wrterror(NULL, "malloc init mmap failed"); } for (i = 0; i <= MALLOC_MAXSHIFT; i++) { LIST_INIT(&d->chunk_info_list[i]); @@ -738,7 +727,7 @@ omalloc_grow(struct dir_info *d) } /* avoid pages containing meta info to end up in cache */ if (munmap(d->r, d->regions_total * sizeof(struct region_info))) - wrterror(d, "munmap", d->r); + wrterror(d, "munmap %p", d->r); else STATS_SUB(d->malloc_used, d->regions_total * sizeof(struct region_info)); @@ -828,7 +817,7 @@ find(struct dir_info *d, void *p) if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || d->canary1 != ~d->canary2) - wrterror(d, "internal struct corrupt", NULL); + wrterror(d, "internal struct corrupt"); p = MASK_POINTER(p); index = hash(p) & mask; r = d->r[index].p; @@ -851,7 +840,7 @@ delete(struct dir_info *d, struct region_info *ri) size_t i, j, r; if (d->regions_total & (d->regions_total - 1)) - wrterror(d, "regions_total not 2^x", NULL); + wrterror(d, "regions_total not 2^x"); d->regions_free++; STATS_INC(d->deletes); @@ -936,7 +925,7 @@ omalloc_make_chunks(struct dir_info *d, int bits, int listnum) bits++; if ((uintptr_t)pp & bits) - wrterror(d, "pp & bits", pp); + wrterror(d, "pp & bits %p", pp); insert(d, (void *)((uintptr_t)pp | bits), (uintptr_t)bp, NULL); return bp; @@ -956,7 +945,7 @@ malloc_bytes(struct dir_info *d, size_t argsize, void *f) if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || d->canary1 != ~d->canary2) - wrterror(d, "internal struct corrupt", NULL); + wrterror(d, "internal struct corrupt"); size = argsize; @@ -984,7 +973,7 @@ malloc_bytes(struct dir_info *d, size_t argsize, void *f) } if (bp->canary != d->canary1) - wrterror(d, "chunk info corrupted", NULL); + wrterror(d, "chunk info corrupted"); i = d->chunk_start; if (bp->free > 1) @@ -1045,6 +1034,25 @@ malloc_bytes(struct dir_info *d, size_t argsize, void *f) return ((char *)bp->page + k); } +static void +validate_canary(struct dir_info *d, u_char *ptr, size_t sz, size_t allocated) +{ + size_t check_sz = allocated - sz; + u_char *p, *q; + + if (check_sz > CHUNK_CHECK_LENGTH) + check_sz = CHUNK_CHECK_LENGTH; + p = (u_char *)ptr + sz; + q = p + check_sz; + + while (p < q) { + if (*p++ != SOME_JUNK) { + wrterror(d, "chunk canary corrupted %p %#tx@%#zx", + ptr, p - ptr - 1, sz); + } + } +} + static uint32_t find_chunknum(struct dir_info *d, struct region_info *r, void *ptr, int check) { @@ -1053,32 +1061,20 @@ find_chunknum(struct dir_info *d, struct region_info *r, void *ptr, int check) info = (struct chunk_info *)r->size; if (info->canary != d->canary1) - wrterror(d, "chunk info corrupted", NULL); + wrterror(d, "chunk info corrupted"); /* Find the chunk number on the page */ chunknum = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift; if (check && mopts.chunk_canaries && info->size > 0) { - size_t sz = info->bits[info->offset + chunknum]; - size_t check_sz = info->size - sz; - u_char *p, *q; - - if (check_sz > CHUNK_CHECK_LENGTH) - check_sz = CHUNK_CHECK_LENGTH; - p = (u_char *)ptr + sz; - q = p + check_sz; - - while (p < q) - if (*p++ != SOME_JUNK) { - q = (void *)(sz << 16 | p - (u_char *)ptr - 1); - wrterror(d, "chunk canary corrupted: ", q); - } + validate_canary(d, ptr, info->bits[info->offset + chunknum], + info->size); } if ((uintptr_t)ptr & ((1U << (info->shift)) - 1)) - wrterror(d, "modified chunk-pointer", ptr); + wrterror(d, "modified chunk-pointer %p", ptr); if (info->bits[chunknum / MALLOC_BITS] & (1U << (chunknum % MALLOC_BITS))) - wrterror(d, "chunk is already free", ptr); + wrterror(d, "chunk is already free %p", ptr); return chunknum; } @@ -1156,7 +1152,7 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) if (mopts.malloc_guard) { if (mprotect((char *)p + psz - mopts.malloc_guard, mopts.malloc_guard, PROT_NONE)) - wrterror(pool, "mprotect", NULL); + wrterror(pool, "mprotect"); STATS_ADD(pool->malloc_guarded, mopts.malloc_guard); } @@ -1180,6 +1176,12 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) else memset(p, SOME_JUNK, psz - mopts.malloc_guard); + } else if (mopts.chunk_canaries) { + size_t csz = psz - mopts.malloc_guard - sz; + + if (csz > CHUNK_CHECK_LENGTH) + csz = CHUNK_CHECK_LENGTH; + memset(p + sz, SOME_JUNK, csz); } } @@ -1205,7 +1207,7 @@ malloc_recurse(struct dir_info *d) if (noprint == 0) { noprint = 1; - wrterror(d, "recursive call", NULL); + wrterror(d, "recursive call"); } d->active--; _MALLOC_UNLOCK(d->mutex); @@ -1264,7 +1266,7 @@ malloc(size_t size) d = getpool(); } _MALLOC_LOCK(d->mutex); - d->func = "malloc():"; + d->func = "malloc"; if (d->active++) { malloc_recurse(d); @@ -1274,7 +1276,7 @@ malloc(size_t size) d->active--; _MALLOC_UNLOCK(d->mutex); if (r == NULL && mopts.malloc_xmalloc) - wrterror(d, "out of memory", NULL); + wrterror(d, "out of memory"); if (r != NULL) errno = saved_errno; return r; @@ -1291,13 +1293,13 @@ validate_junk(struct dir_info *pool, void *p) return; r = find(pool, p); if (r == NULL) - wrterror(pool, "bogus pointer in validate_junk", p); + wrterror(pool, "bogus pointer in validate_junk %p", p); REALSIZE(sz, r); if (sz > CHUNK_CHECK_LENGTH) sz = CHUNK_CHECK_LENGTH; for (byte = 0; byte < sz; byte++) { if (((unsigned char *)p)[byte] != SOME_FREEJUNK) - wrterror(pool, "use after free", p); + wrterror(pool, "use after free %p", p); } } @@ -1327,7 +1329,7 @@ ofree(struct dir_info *argpool, void *p) } } if (r == NULL) - wrterror(pool, "bogus pointer (double free?)", p); + wrterror(pool, "bogus pointer (double free?) %p", p); } REALSIZE(sz, r); @@ -1335,7 +1337,11 @@ ofree(struct dir_info *argpool, void *p) if (sz - mopts.malloc_guard >= MALLOC_PAGESIZE - MALLOC_LEEWAY) { if (r->p != p) - wrterror(pool, "bogus pointer", p); + wrterror(pool, "bogus pointer %p", p); + if (mopts.chunk_canaries) + validate_canary(pool, p, + sz - mopts.malloc_guard, + PAGEROUND(sz - mopts.malloc_guard)); } else { #if notyetbecause_of_realloc /* shifted towards the end */ @@ -1348,12 +1354,12 @@ ofree(struct dir_info *argpool, void *p) } if (mopts.malloc_guard) { if (sz < mopts.malloc_guard) - wrterror(pool, "guard size", NULL); + wrterror(pool, "guard size"); if (!mopts.malloc_freeunmap) { if (mprotect((char *)p + PAGEROUND(sz) - mopts.malloc_guard, mopts.malloc_guard, PROT_READ | PROT_WRITE)) - wrterror(pool, "mprotect", NULL); + wrterror(pool, "mprotect"); } STATS_SUB(pool->malloc_guarded, mopts.malloc_guard); } @@ -1371,7 +1377,7 @@ ofree(struct dir_info *argpool, void *p) tmp = p; p = pool->delayed_chunks[i]; if (tmp == p) - wrterror(pool, "double free", p); + wrterror(pool, "double free %p", tmp); if (mopts.malloc_junk) validate_junk(pool, p); pool->delayed_chunks[i] = tmp; @@ -1382,11 +1388,12 @@ ofree(struct dir_info *argpool, void *p) if (p != NULL) { r = find(pool, p); if (r == NULL) - wrterror(pool, "bogus pointer (double free?)", p); + wrterror(pool, + "bogus pointer (double free?) %p", p); free_bytes(pool, r, p); } } -done: + if (argpool != pool) { pool->active--; _MALLOC_UNLOCK(pool->mutex); @@ -1407,9 +1414,9 @@ free(void *ptr) d = getpool(); if (d == NULL) - wrterror(d, "free() called before allocation", NULL); + wrterror(d, "free() called before allocation"); _MALLOC_LOCK(d->mutex); - d->func = "free():"; + d->func = "free"; if (d->active++) { malloc_recurse(d); return; @@ -1453,7 +1460,7 @@ orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) } } if (r == NULL) - wrterror(pool, "bogus pointer (double free?)", p); + wrterror(pool, "bogus pointer (double free?) %p", p); } if (newsz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { errno = ENOMEM; @@ -1465,7 +1472,7 @@ orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) goldsz = oldsz; if (oldsz > MALLOC_MAXCHUNK) { if (oldsz < mopts.malloc_guard) - wrterror(pool, "guard size", NULL); + wrterror(pool, "guard size"); oldsz -= mopts.malloc_guard; } @@ -1474,7 +1481,7 @@ orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) gnewsz += mopts.malloc_guard; if (newsz > MALLOC_MAXCHUNK && oldsz > MALLOC_MAXCHUNK && p == r->p && - !mopts.malloc_realloc) { + !mopts.chunk_canaries && !mopts.malloc_realloc) { size_t roldsz = PAGEROUND(goldsz); size_t rnewsz = PAGEROUND(gnewsz); @@ -1505,7 +1512,7 @@ gotit: goto done; } else if (q != MAP_FAILED) { if (munmap(q, needed)) - wrterror(pool, "munmap", q); + wrterror(pool, "munmap %p", q); } } } else if (rnewsz < roldsz) { @@ -1513,11 +1520,11 @@ gotit: if (mprotect((char *)p + roldsz - mopts.malloc_guard, mopts.malloc_guard, PROT_READ | PROT_WRITE)) - wrterror(pool, "mprotect", NULL); + wrterror(pool, "mprotect"); if (mprotect((char *)p + rnewsz - mopts.malloc_guard, mopts.malloc_guard, PROT_NONE)) - wrterror(pool, "mprotect", NULL); + wrterror(pool, "mprotect"); } unmap(pool, (char *)p + rnewsz, roldsz - rnewsz); r->size = gnewsz; @@ -1578,7 +1585,7 @@ realloc(void *ptr, size_t size) d = getpool(); } _MALLOC_LOCK(d->mutex); - d->func = "realloc():"; + d->func = "realloc"; if (d->active++) { malloc_recurse(d); return NULL; @@ -1588,7 +1595,7 @@ realloc(void *ptr, size_t size) d->active--; _MALLOC_UNLOCK(d->mutex); if (r == NULL && mopts.malloc_xmalloc) - wrterror(d, "out of memory", NULL); + wrterror(d, "out of memory"); if (r != NULL) errno = saved_errno; return r; @@ -1615,12 +1622,12 @@ calloc(size_t nmemb, size_t size) d = getpool(); } _MALLOC_LOCK(d->mutex); - d->func = "calloc():"; + d->func = "calloc"; if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { _MALLOC_UNLOCK(d->mutex); if (mopts.malloc_xmalloc) - wrterror(d, "out of memory", NULL); + wrterror(d, "out of memory"); errno = ENOMEM; return NULL; } @@ -1636,7 +1643,7 @@ calloc(size_t nmemb, size_t size) d->active--; _MALLOC_UNLOCK(d->mutex); if (r == NULL && mopts.malloc_xmalloc) - wrterror(d, "out of memory", NULL); + wrterror(d, "out of memory"); if (r != NULL) errno = saved_errno; return r; @@ -1649,9 +1656,9 @@ mapalign(struct dir_info *d, size_t alignment, size_t sz, int zero_fill) char *p, *q; if (alignment < MALLOC_PAGESIZE || ((alignment - 1) & alignment) != 0) - wrterror(d, "mapalign bad alignment", NULL); + wrterror(d, "mapalign bad alignment"); if (sz != PAGEROUND(sz)) - wrterror(d, "mapalign round", NULL); + wrterror(d, "mapalign round"); /* Allocate sz + alignment bytes of memory, which must include a * subrange of size bytes that is properly aligned. Unmap the @@ -1668,10 +1675,10 @@ mapalign(struct dir_info *d, size_t alignment, size_t sz, int zero_fill) q = (char *)(((uintptr_t)p + alignment - 1) & ~(alignment - 1)); if (q != p) { if (munmap(p, q - p)) - wrterror(d, "munmap", p); + wrterror(d, "munmap %p", p); } if (munmap(q + sz, alignment - (q - p))) - wrterror(d, "munmap", q + sz); + wrterror(d, "munmap %p", q + sz); STATS_SUB(d->malloc_used, alignment); return q; @@ -1716,7 +1723,7 @@ omemalign(struct dir_info *pool, size_t alignment, size_t sz, int zero_fill, voi if (mopts.malloc_guard) { if (mprotect((char *)p + psz - mopts.malloc_guard, mopts.malloc_guard, PROT_NONE)) - wrterror(pool, "mprotect", NULL); + wrterror(pool, "mprotect"); STATS_ADD(pool->malloc_guarded, mopts.malloc_guard); } @@ -1748,7 +1755,7 @@ posix_memalign(void **memptr, size_t alignment, size_t size) d = getpool(); } _MALLOC_LOCK(d->mutex); - d->func = "posix_memalign():"; + d->func = "posix_memalign"; if (d->active++) { malloc_recurse(d); goto err; @@ -1758,7 +1765,7 @@ posix_memalign(void **memptr, size_t alignment, size_t size) _MALLOC_UNLOCK(d->mutex); if (r == NULL) { if (mopts.malloc_xmalloc) - wrterror(d, "out of memory", NULL); + wrterror(d, "out of memory"); goto err; } errno = saved_errno; @@ -2007,7 +2014,7 @@ malloc_dump(int fd, struct dir_info *pool) continue; r = find(pool, p); if (r == NULL) - wrterror(pool, "bogus pointer in malloc_dump", p); + wrterror(pool, "bogus pointer in malloc_dump %p", p); free_bytes(pool, r, p); pool->delayed_chunks[i] = NULL; }