On Sun, Apr 13, 2014 at 06:34:17PM -0400, Ted Unangst wrote:
> I took another look at the way junk works in malloc, and there's a few
> improvements I'd like to make.
>
> 1. Remove the Z option. In general, I think malloc options should make
> programs crash more, not less. This option is a bandaid, but looks
> like something that makes things better. (Anecdotally, I've seen
> people turn it on because it somehow sounded even better than J.
> Incorrect.)
>
> 2. Default to something halfway to J. When we free a chunk, always
> overwrite it. When we free a larger allocation, just overwrite the
> front of it. This has the desirable property of wiping small freed
> secrets and data structures, but won't have the performance impact of
> wiping large buffers (or faulting in pages that weren't even touched).
> J turns it up to junking on malloc() as before, and j turns it off.
>
> I have't been able to build perl reliably with this diff, but haven't
> ruled out the 5.18 update either.
Idea looks good...
Some remarks:
- always junk half a page? Maybe that's a tad over done, athough it
might be relatively cheap since your writing the page anyway.
- Chunks are now always completely junked on free, maybe a bit
overdone for malloc_junk = 1
- if we are freeing a large region, it does not end up in the cache,
so junking (part of) it is a waste of effort. (That already is the
case for current, you are not introducing that).
- What about 'j'? Do we want it to cancel a J or always disable
junking?
This need some timing/profiling on various archs to get some idea on
the costs.
-Otto
>
> Index: malloc.3
> ===================================================================
> RCS file: /cvs/src/lib/libc/stdlib/malloc.3,v
> retrieving revision 1.73
> diff -u -p -r1.73 malloc.3
> --- malloc.3 18 Jul 2013 10:14:49 -0000 1.73
> +++ malloc.3 13 Apr 2014 21:49:47 -0000
> @@ -298,11 +298,6 @@ malloc_options = "X";
> .Pp
> Note that this will cause code that is supposed to handle
> out-of-memory conditions gracefully to abort instead.
> -.It Cm Z
> -.Dq Zero .
> -Fill some junk into the area allocated (see
> -.Cm J ) ,
> -except for the exact length the user asked for, which is zeroed.
> .It Cm <
> .Dq Half the cache size .
> Decrease the size of the free page cache by a factor of two.
> Index: malloc.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/stdlib/malloc.c,v
> retrieving revision 1.152
> diff -u -p -r1.152 malloc.c
> --- malloc.c 3 Apr 2014 16:18:11 -0000 1.152
> +++ malloc.c 13 Apr 2014 21:05:11 -0000
> @@ -167,7 +167,6 @@ struct malloc_readonly {
> int malloc_move; /* move allocations to end of page? */
> int malloc_realloc; /* always realloc? */
> int malloc_xmalloc; /* xmalloc behaviour? */
> - int malloc_zero; /* zero fill? */
> size_t malloc_guard; /* use guard pages after allocations? */
> u_int malloc_cache; /* free pages we cache */
> #ifdef MALLOC_STATS
> @@ -412,7 +411,7 @@ map(struct dir_info *d, size_t sz, int z
> d->free_regions_size -= psz;
> if (zero_fill)
> memset(p, 0, sz);
> - else if (mopts.malloc_junk &&
> + else if (mopts.malloc_junk == 2 &&
> mopts.malloc_freeunmap)
> memset(p, SOME_FREEJUNK, sz);
> return p;
> @@ -431,7 +430,7 @@ map(struct dir_info *d, size_t sz, int z
> d->free_regions_size -= psz;
> if (zero_fill)
> memset(p, 0, sz);
> - else if (mopts.malloc_junk && mopts.malloc_freeunmap)
> + else if (mopts.malloc_junk == 2 && mopts.malloc_freeunmap)
> memset(p, SOME_FREEJUNK, sz);
> return p;
> }
> @@ -461,6 +460,7 @@ omalloc_init(struct dir_info **dp)
> * Default options
> */
> mopts.malloc_abort = 1;
> + mopts.malloc_junk = 1;
> mopts.malloc_move = 1;
> mopts.malloc_cache = MALLOC_DEFAULT_CACHE;
>
> @@ -534,7 +534,7 @@ omalloc_init(struct dir_info **dp)
> mopts.malloc_junk = 0;
> break;
> case 'J':
> - mopts.malloc_junk = 1;
> + mopts.malloc_junk = 2;
> break;
> case 'n':
> case 'N':
> @@ -557,7 +557,8 @@ omalloc_init(struct dir_info **dp)
> mopts.malloc_cache = MALLOC_DEFAULT_CACHE;
> break;
> case 'S':
> - mopts.malloc_freeunmap = mopts.malloc_junk = 1;
> + mopts.malloc_freeunmap = 1;
> + mopts.malloc_junk = 2;
> mopts.malloc_guard = MALLOC_PAGESIZE;
> mopts.malloc_cache = 0;
> break;
> @@ -573,12 +574,6 @@ omalloc_init(struct dir_info **dp)
> case 'X':
> mopts.malloc_xmalloc = 1;
> break;
> - case 'z':
> - mopts.malloc_zero = 0;
> - break;
> - case 'Z':
> - mopts.malloc_zero = 1;
> - break;
> default: {
> static const char q[] = "malloc() warning: "
> "unknown char in MALLOC_OPTIONS\n";
> @@ -589,13 +584,6 @@ omalloc_init(struct dir_info **dp)
> }
> }
>
> - /*
> - * We want junk in the entire allocation, and zero only in the part
> - * the user asked for.
> - */
> - if (mopts.malloc_zero)
> - mopts.malloc_junk = 1;
> -
> #ifdef MALLOC_STATS
> if (mopts.malloc_stats && (atexit(malloc_exit) == -1)) {
> static const char q[] = "malloc() warning: atexit(2) failed."
> @@ -969,7 +957,7 @@ malloc_bytes(struct dir_info *d, size_t
> k += (lp - bp->bits) * MALLOC_BITS;
> k <<= bp->shift;
>
> - if (mopts.malloc_junk && bp->size > 0)
> + if (mopts.malloc_junk == 2 && bp->size > 0)
> memset((char *)bp->page + k, SOME_JUNK, bp->size);
> return ((char *)bp->page + k);
> }
> @@ -1067,16 +1055,16 @@ omalloc(size_t sz, int zero_fill, void *
> sz - mopts.malloc_guard < MALLOC_PAGESIZE -
> MALLOC_LEEWAY) {
> /* fill whole allocation */
> - if (mopts.malloc_junk)
> + if (mopts.malloc_junk == 2)
> memset(p, SOME_JUNK, psz - mopts.malloc_guard);
> /* shift towards the end */
> p = ((char *)p) + ((MALLOC_PAGESIZE - MALLOC_LEEWAY -
> (sz - mopts.malloc_guard)) & ~(MALLOC_MINSIZE-1));
> /* fill zeros if needed and overwritten above */
> - if (zero_fill && mopts.malloc_junk)
> + if (zero_fill && mopts.malloc_junk == 2)
> memset(p, 0, sz - mopts.malloc_guard);
> } else {
> - if (mopts.malloc_junk) {
> + if (mopts.malloc_junk == 2) {
> if (zero_fill)
> memset((char *)p + sz -
> mopts.malloc_guard,
> SOME_JUNK, psz - sz);
> @@ -1144,7 +1132,7 @@ malloc(size_t size)
> malloc_recurse();
> return NULL;
> }
> - r = omalloc(size, mopts.malloc_zero, CALLER);
> + r = omalloc(size, 0, CALLER);
> malloc_active--;
> _MALLOC_UNLOCK();
> if (r == NULL && mopts.malloc_xmalloc) {
> @@ -1196,9 +1184,11 @@ ofree(void *p)
> }
> malloc_guarded -= mopts.malloc_guard;
> }
> - if (mopts.malloc_junk && !mopts.malloc_freeunmap)
> - memset(p, SOME_FREEJUNK,
> - PAGEROUND(sz) - mopts.malloc_guard);
> + if (mopts.malloc_junk && !mopts.malloc_freeunmap) {
> + size_t amt = mopts.malloc_junk == 1 ? MALLOC_MAXCHUNK :
> + PAGEROUND(sz) - mopts.malloc_guard;
> + memset(p, SOME_FREEJUNK, amt);
> + }
> unmap(g_pool, p, PAGEROUND(sz));
> delete(g_pool, r);
> } else {
> @@ -1302,7 +1292,7 @@ orealloc(void *p, size_t newsz, void *f)
> q = MAP_FAILED;
> if (q == hint) {
> malloc_used += needed;
> - if (mopts.malloc_junk)
> + if (mopts.malloc_junk == 2)
> memset(q, SOME_JUNK, needed);
> r->size = newsz;
> STATS_SETF(r, f);
> @@ -1329,7 +1319,7 @@ orealloc(void *p, size_t newsz, void *f)
> STATS_SETF(r, f);
> return p;
> } else {
> - if (newsz > oldsz && mopts.malloc_junk)
> + if (newsz > oldsz && mopts.malloc_junk == 2)
> memset((char *)p + newsz, SOME_JUNK,
> rnewsz - mopts.malloc_guard - newsz);
> r->size = gnewsz;
> @@ -1338,7 +1328,7 @@ orealloc(void *p, size_t newsz, void *f)
> }
> }
> if (newsz <= oldsz && newsz > oldsz / 2 && !mopts.malloc_realloc) {
> - if (mopts.malloc_junk && newsz > 0)
> + if (mopts.malloc_junk == 2 && newsz > 0)
> memset((char *)p + newsz, SOME_JUNK, oldsz - newsz);
> STATS_SETF(r, f);
> return p;
> @@ -1509,7 +1499,7 @@ omemalign(size_t alignment, size_t sz, i
> malloc_guarded += mopts.malloc_guard;
> }
>
> - if (mopts.malloc_junk) {
> + if (mopts.malloc_junk == 2) {
> if (zero_fill)
> memset((char *)p + sz - mopts.malloc_guard,
> SOME_JUNK, psz - sz);
> @@ -1540,7 +1530,7 @@ posix_memalign(void **memptr, size_t ali
> malloc_recurse();
> goto err;
> }
> - r = omemalign(alignment, size, mopts.malloc_zero, CALLER);
> + r = omemalign(alignment, size, 0, CALLER);
> malloc_active--;
> _MALLOC_UNLOCK();
> if (r == NULL) {