Hi, malloc(3) has the nice feature to move (subject to alignment constraints) allocations that are between the max chunk size (half a page) and a page size towards the end of the allocated page, to catch more buffer overflows. Due to the allocation being higher up within a page, buffer overflows will end up beyond the page in more cases.
Until now, realloc(3) did not handle this in a smart way. When it encountered an existing moved allocation it always did a malloc-copy-free dance. This diff fixes that, at the same time doing more cases without requesting new pages from the kernel. Please review and test, BTW, we cannot use this feature for allocations larger than or equal to a page, since these should return page-aligned pointers. -Otto Index: malloc.c =================================================================== RCS file: /cvs/src/lib/libc/stdlib/malloc.c,v retrieving revision 1.212 diff -u -p -r1.212 malloc.c --- malloc.c 21 Jan 2017 07:47:42 -0000 1.212 +++ malloc.c 24 Jan 2017 06:08:13 -0000 @@ -1328,13 +1328,9 @@ ofree(struct dir_info *argpool, void *p) sz - mopts.malloc_guard, PAGEROUND(sz - mopts.malloc_guard)); } else { -#if notyetbecause_of_realloc /* shifted towards the end */ - if (p != ((char *)r->p) + ((MALLOC_PAGESIZE - - MALLOC_MINSIZE - sz - mopts.malloc_guard) & - ~(MALLOC_MINSIZE-1))) { - } -#endif + if (p != MALLOC_MOVE(r->p, sz)) + wrterror(pool, "bogus moved pointer %p", p); p = r->p; } if (mopts.malloc_guard) { @@ -1474,7 +1470,7 @@ orealloc(struct dir_info *argpool, void if (gnewsz > MALLOC_MAXCHUNK) gnewsz += mopts.malloc_guard; - if (newsz > MALLOC_MAXCHUNK && oldsz > MALLOC_MAXCHUNK && p == r->p && + if (newsz > MALLOC_MAXCHUNK && oldsz > MALLOC_MAXCHUNK && !mopts.malloc_realloc) { /* First case: from n pages sized allocation to m pages sized allocation, no malloc_move in effect */ @@ -1484,7 +1480,7 @@ orealloc(struct dir_info *argpool, void if (rnewsz > roldsz) { /* try to extend existing region */ if (!mopts.malloc_guard) { - void *hint = (char *)p + roldsz; + void *hint = (char *)r->p + roldsz; size_t needed = rnewsz - roldsz; STATS_INC(pool->cheap_realloc_tries); @@ -1502,9 +1498,15 @@ gotit: STATS_ADD(pool->malloc_used, needed); if (mopts.malloc_junk == 2) memset(q, SOME_JUNK, needed); - r->size = newsz; + r->size = gnewsz; + if (r->p != p) { + /* old pointer is moved */ + memmove(r->p, p, oldsz); + p = r->p; + } if (mopts.chunk_canaries) - fill_canary(p, newsz, PAGEROUND(newsz)); + fill_canary(p, newsz, + PAGEROUND(newsz)); STATS_SETF(r, f); STATS_INC(pool->cheap_reallocs); ret = p; @@ -1517,30 +1519,45 @@ gotit: } else if (rnewsz < roldsz) { /* shrink number of pages */ if (mopts.malloc_guard) { - if (mprotect((char *)p + roldsz - + if (mprotect((char *)r->p + roldsz - mopts.malloc_guard, mopts.malloc_guard, PROT_READ | PROT_WRITE)) wrterror(pool, "mprotect"); - if (mprotect((char *)p + rnewsz - + if (mprotect((char *)r->p + rnewsz - mopts.malloc_guard, mopts.malloc_guard, PROT_NONE)) wrterror(pool, "mprotect"); } - unmap(pool, (char *)p + rnewsz, roldsz - rnewsz); + unmap(pool, (char *)r->p + rnewsz, roldsz - rnewsz); r->size = gnewsz; - if (mopts.chunk_canaries) + if (MALLOC_MOVE_COND(gnewsz)) { + void *pp = MALLOC_MOVE(r->p, gnewsz); + memmove(pp, p, newsz); + p = pp; + } else if (mopts.chunk_canaries) fill_canary(p, newsz, PAGEROUND(newsz)); STATS_SETF(r, f); ret = p; goto done; } else { /* number of pages remains the same */ - if (newsz > oldsz && mopts.malloc_junk == 2) - memset((char *)p + newsz, SOME_JUNK, - rnewsz - mopts.malloc_guard - newsz); + void *pp = r->p; + r->size = gnewsz; - if (mopts.chunk_canaries) - fill_canary(p, newsz, PAGEROUND(newsz)); + if (MALLOC_MOVE_COND(gnewsz)) + pp = MALLOC_MOVE(r->p, gnewsz); + if (p != pp) { + memmove(pp, p, oldsz < newsz ? oldsz : newsz); + p = pp; + } + if (p == r->p) { + if (newsz > oldsz && mopts.malloc_junk == 2) + memset((char *)p + newsz, SOME_JUNK, + rnewsz - mopts.malloc_guard - + newsz); + if (mopts.chunk_canaries) + fill_canary(p, newsz, PAGEROUND(newsz)); + } STATS_SETF(r, f); ret = p; goto done;