Instead of always using a fixed byte pattern, I think malloc should use a random pattern. Now, this sometimes means it's harder to identify exactly what's used after free, so we should provide a means to get the old 0xdf pattern back.
Since we already have two junk modes, I thought I'd carry on along those lines. The default junk behavior, for free chunks only, is more of a security measure. I think this means we want random junk. The second level 'J' junk is more of a debugging tool, so that retains 0xdf. There's some overlap here with canaries, but nothing wrong with that. :) Index: malloc.c =================================================================== RCS file: /cvs/src/lib/libc/stdlib/malloc.c,v retrieving revision 1.195 diff -u -p -r1.195 malloc.c --- malloc.c 1 Sep 2016 10:41:02 -0000 1.195 +++ malloc.c 7 Sep 2016 22:21:37 -0000 @@ -186,6 +186,7 @@ struct malloc_readonly { #endif u_int32_t malloc_canary; /* Matched against ones in malloc_pool */ uintptr_t malloc_chunk_canary; + u_char malloc_junkbytes[256]; }; /* This object is mapped PROT_READ after initialisation to prevent tampering */ @@ -597,6 +598,8 @@ omalloc_init(void) mopts.malloc_move = 1; mopts.malloc_cache = MALLOC_DEFAULT_CACHE; + arc4random_buf(mopts.malloc_junkbytes, sizeof(mopts.malloc_junkbytes)); + for (i = 0; i < 3; i++) { switch (i) { case 0: @@ -1260,7 +1263,29 @@ malloc(size_t size) /*DEF_STRONG(malloc);*/ static void -validate_junk(struct dir_info *pool, void *p) { +random_junk(void *p, size_t amt) +{ + u_long offset = ((u_long)p) & (sizeof(mopts.malloc_junkbytes) - 1); + + if (amt < sizeof(mopts.malloc_junkbytes) - offset) { + memcpy(p, mopts.malloc_junkbytes + offset, amt); + } else { + memcpy(p, mopts.malloc_junkbytes + offset, + sizeof(mopts.malloc_junkbytes) - offset); + amt -= sizeof(mopts.malloc_junkbytes) - offset; + while (amt > 0) { + size_t x = amt > sizeof(mopts.malloc_junkbytes) ? + sizeof(mopts.malloc_junkbytes) : amt; + memcpy(p, mopts.malloc_junkbytes, x); + amt -= x; + } + } +} + + +static void +validate_junk(struct dir_info *pool, void *p) +{ struct region_info *r; size_t byte, sz; @@ -1274,9 +1299,15 @@ validate_junk(struct dir_info *pool, voi sz -= mopts.malloc_canaries; if (sz > 32) sz = 32; - for (byte = 0; byte < sz; byte++) { - if (((unsigned char *)p)[byte] != SOME_FREEJUNK) + if (mopts.malloc_junk == 1) { + u_long offset = ((u_long)p) & (sizeof(mopts.malloc_junkbytes) - 1); + if (memcmp(p, mopts.malloc_junkbytes + offset, sz) != 0) wrterror(pool, "use after free", p); + } else { + for (byte = 0; byte < sz; byte++) { + if (((unsigned char *)p)[byte] != SOME_FREEJUNK) + wrterror(pool, "use after free", p); + } } } @@ -1336,10 +1367,11 @@ ofree(struct dir_info *argpool, void *p) } STATS_SUB(pool->malloc_guarded, 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); + if (mopts.malloc_junk == 2 && !mopts.malloc_freeunmap) { + memset(p, SOME_FREEJUNK, + PAGEROUND(sz) - mopts.malloc_guard); + } else if (mopts.malloc_junk == 1 && !mopts.malloc_freeunmap) { + random_junk(p, MALLOC_MAXCHUNK); } unmap(pool, p, PAGEROUND(sz)); delete(pool, r); @@ -1347,8 +1379,10 @@ ofree(struct dir_info *argpool, void *p) void *tmp; int i; - if (mopts.malloc_junk && sz > 0) + if (mopts.malloc_junk == 2 && sz > 0) memset(p, SOME_FREEJUNK, sz - mopts.malloc_canaries); + else if (mopts.malloc_junk == 1 && sz > 0) + random_junk(p, sz); if (!mopts.malloc_freenow) { if (find_chunknum(pool, r, p) == -1) goto done;