diff options
| -rw-r--r-- | src/lib/libc/stdlib/malloc.c | 84 |
1 files changed, 76 insertions, 8 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index dd4c487241..36d2d717db 100644 --- a/src/lib/libc/stdlib/malloc.c +++ b/src/lib/libc/stdlib/malloc.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: malloc.c,v 1.176 2015/09/13 20:29:23 guenther Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.177 2015/12/09 02:45:23 tedu Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2008, 2010, 2011 Otto Moerbeek <otto@drijf.net> | 3 | * Copyright (c) 2008, 2010, 2011 Otto Moerbeek <otto@drijf.net> |
| 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> | 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> |
| @@ -185,12 +185,14 @@ struct malloc_readonly { | |||
| 185 | int malloc_move; /* move allocations to end of page? */ | 185 | int malloc_move; /* move allocations to end of page? */ |
| 186 | int malloc_realloc; /* always realloc? */ | 186 | int malloc_realloc; /* always realloc? */ |
| 187 | int malloc_xmalloc; /* xmalloc behaviour? */ | 187 | int malloc_xmalloc; /* xmalloc behaviour? */ |
| 188 | size_t malloc_canaries; /* use canaries after chunks? */ | ||
| 188 | size_t malloc_guard; /* use guard pages after allocations? */ | 189 | size_t malloc_guard; /* use guard pages after allocations? */ |
| 189 | u_int malloc_cache; /* free pages we cache */ | 190 | u_int malloc_cache; /* free pages we cache */ |
| 190 | #ifdef MALLOC_STATS | 191 | #ifdef MALLOC_STATS |
| 191 | int malloc_stats; /* dump statistics at end */ | 192 | int malloc_stats; /* dump statistics at end */ |
| 192 | #endif | 193 | #endif |
| 193 | u_int32_t malloc_canary; /* Matched against ones in malloc_pool */ | 194 | u_int32_t malloc_canary; /* Matched against ones in malloc_pool */ |
| 195 | uintptr_t malloc_chunk_canary; | ||
| 194 | }; | 196 | }; |
| 195 | 197 | ||
| 196 | /* This object is mapped PROT_READ after initialisation to prevent tampering */ | 198 | /* This object is mapped PROT_READ after initialisation to prevent tampering */ |
| @@ -526,6 +528,12 @@ omalloc_init(struct dir_info **dp) | |||
| 526 | case 'A': | 528 | case 'A': |
| 527 | mopts.malloc_abort = 1; | 529 | mopts.malloc_abort = 1; |
| 528 | break; | 530 | break; |
| 531 | case 'c': | ||
| 532 | mopts.malloc_canaries = 0; | ||
| 533 | break; | ||
| 534 | case 'C': | ||
| 535 | mopts.malloc_canaries = sizeof(void *); | ||
| 536 | break; | ||
| 529 | #ifdef MALLOC_STATS | 537 | #ifdef MALLOC_STATS |
| 530 | case 'd': | 538 | case 'd': |
| 531 | mopts.malloc_stats = 0; | 539 | mopts.malloc_stats = 0; |
| @@ -619,6 +627,9 @@ omalloc_init(struct dir_info **dp) | |||
| 619 | while ((mopts.malloc_canary = arc4random()) == 0) | 627 | while ((mopts.malloc_canary = arc4random()) == 0) |
| 620 | ; | 628 | ; |
| 621 | 629 | ||
| 630 | arc4random_buf(&mopts.malloc_chunk_canary, | ||
| 631 | sizeof(mopts.malloc_chunk_canary)); | ||
| 632 | |||
| 622 | /* | 633 | /* |
| 623 | * Allocate dir_info with a guard page on either side. Also | 634 | * Allocate dir_info with a guard page on either side. Also |
| 624 | * randomise offset inside the page at which the dir_info | 635 | * randomise offset inside the page at which the dir_info |
| @@ -984,8 +995,15 @@ malloc_bytes(struct dir_info *d, size_t size, void *f) | |||
| 984 | k += (lp - bp->bits) * MALLOC_BITS; | 995 | k += (lp - bp->bits) * MALLOC_BITS; |
| 985 | k <<= bp->shift; | 996 | k <<= bp->shift; |
| 986 | 997 | ||
| 998 | if (mopts.malloc_canaries && bp->size > 0) { | ||
| 999 | char *end = (char *)bp->page + k + bp->size; | ||
| 1000 | uintptr_t *canary = (uintptr_t *)(end - mopts.malloc_canaries); | ||
| 1001 | *canary = mopts.malloc_chunk_canary ^ hash(canary); | ||
| 1002 | } | ||
| 1003 | |||
| 987 | if (mopts.malloc_junk == 2 && bp->size > 0) | 1004 | if (mopts.malloc_junk == 2 && bp->size > 0) |
| 988 | memset((char *)bp->page + k, SOME_JUNK, bp->size); | 1005 | memset((char *)bp->page + k, SOME_JUNK, |
| 1006 | bp->size - mopts.malloc_canaries); | ||
| 989 | return ((char *)bp->page + k); | 1007 | return ((char *)bp->page + k); |
| 990 | } | 1008 | } |
| 991 | 1009 | ||
| @@ -999,6 +1017,13 @@ find_chunknum(struct dir_info *d, struct region_info *r, void *ptr) | |||
| 999 | if (info->canary != d->canary1) | 1017 | if (info->canary != d->canary1) |
| 1000 | wrterror("chunk info corrupted", NULL); | 1018 | wrterror("chunk info corrupted", NULL); |
| 1001 | 1019 | ||
| 1020 | if (mopts.malloc_canaries && info->size > 0) { | ||
| 1021 | char *end = (char *)ptr + info->size; | ||
| 1022 | uintptr_t *canary = (uintptr_t *)(end - mopts.malloc_canaries); | ||
| 1023 | if (*canary != (mopts.malloc_chunk_canary ^ hash(canary))) | ||
| 1024 | wrterror("chunk canary corrupted", ptr); | ||
| 1025 | } | ||
| 1026 | |||
| 1002 | /* Find the chunk number on the page */ | 1027 | /* Find the chunk number on the page */ |
| 1003 | chunknum = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift; | 1028 | chunknum = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift; |
| 1004 | 1029 | ||
| @@ -1121,7 +1146,7 @@ omalloc(size_t sz, int zero_fill, void *f) | |||
| 1121 | /* takes care of SOME_JUNK */ | 1146 | /* takes care of SOME_JUNK */ |
| 1122 | p = malloc_bytes(pool, sz, f); | 1147 | p = malloc_bytes(pool, sz, f); |
| 1123 | if (zero_fill && p != NULL && sz > 0) | 1148 | if (zero_fill && p != NULL && sz > 0) |
| 1124 | memset(p, 0, sz); | 1149 | memset(p, 0, sz - mopts.malloc_canaries); |
| 1125 | } | 1150 | } |
| 1126 | 1151 | ||
| 1127 | return p; | 1152 | return p; |
| @@ -1176,6 +1201,8 @@ malloc(size_t size) | |||
| 1176 | malloc_recurse(); | 1201 | malloc_recurse(); |
| 1177 | return NULL; | 1202 | return NULL; |
| 1178 | } | 1203 | } |
| 1204 | if (size > 0 && size <= MALLOC_MAXCHUNK) | ||
| 1205 | size += mopts.malloc_canaries; | ||
| 1179 | r = omalloc(size, 0, CALLER); | 1206 | r = omalloc(size, 0, CALLER); |
| 1180 | malloc_active--; | 1207 | malloc_active--; |
| 1181 | _MALLOC_UNLOCK(); | 1208 | _MALLOC_UNLOCK(); |
| @@ -1190,6 +1217,30 @@ malloc(size_t size) | |||
| 1190 | /*DEF_STRONG(malloc);*/ | 1217 | /*DEF_STRONG(malloc);*/ |
| 1191 | 1218 | ||
| 1192 | static void | 1219 | static void |
| 1220 | validate_junk(void *p) { | ||
| 1221 | struct region_info *r; | ||
| 1222 | struct dir_info *pool = getpool(); | ||
| 1223 | size_t byte, sz; | ||
| 1224 | |||
| 1225 | if (p == NULL) | ||
| 1226 | return; | ||
| 1227 | r = find(pool, p); | ||
| 1228 | if (r == NULL) | ||
| 1229 | wrterror("bogus pointer in validate_junk", p); | ||
| 1230 | REALSIZE(sz, r); | ||
| 1231 | if (sz > 0 && sz <= MALLOC_MAXCHUNK) | ||
| 1232 | sz -= mopts.malloc_canaries; | ||
| 1233 | if (sz > 32) | ||
| 1234 | sz = 32; | ||
| 1235 | for (byte = 0; byte < sz; byte++) { | ||
| 1236 | if (((unsigned char *)p)[byte] != SOME_FREEJUNK) { | ||
| 1237 | wrterror("use after free", p); | ||
| 1238 | return; | ||
| 1239 | } | ||
| 1240 | } | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | static void | ||
| 1193 | ofree(void *p) | 1244 | ofree(void *p) |
| 1194 | { | 1245 | { |
| 1195 | struct dir_info *pool = getpool(); | 1246 | struct dir_info *pool = getpool(); |
| @@ -1242,7 +1293,7 @@ ofree(void *p) | |||
| 1242 | int i; | 1293 | int i; |
| 1243 | 1294 | ||
| 1244 | if (mopts.malloc_junk && sz > 0) | 1295 | if (mopts.malloc_junk && sz > 0) |
| 1245 | memset(p, SOME_FREEJUNK, sz); | 1296 | memset(p, SOME_FREEJUNK, sz - mopts.malloc_canaries); |
| 1246 | if (!mopts.malloc_freenow) { | 1297 | if (!mopts.malloc_freenow) { |
| 1247 | if (find_chunknum(pool, r, p) == -1) | 1298 | if (find_chunknum(pool, r, p) == -1) |
| 1248 | return; | 1299 | return; |
| @@ -1253,6 +1304,8 @@ ofree(void *p) | |||
| 1253 | wrterror("double free", p); | 1304 | wrterror("double free", p); |
| 1254 | return; | 1305 | return; |
| 1255 | } | 1306 | } |
| 1307 | if (mopts.malloc_junk) | ||
| 1308 | validate_junk(p); | ||
| 1256 | pool->delayed_chunks[i] = tmp; | 1309 | pool->delayed_chunks[i] = tmp; |
| 1257 | } | 1310 | } |
| 1258 | if (p != NULL) { | 1311 | if (p != NULL) { |
| @@ -1386,16 +1439,25 @@ gotit: | |||
| 1386 | } | 1439 | } |
| 1387 | } | 1440 | } |
| 1388 | if (newsz <= oldsz && newsz > oldsz / 2 && !mopts.malloc_realloc) { | 1441 | if (newsz <= oldsz && newsz > oldsz / 2 && !mopts.malloc_realloc) { |
| 1389 | if (mopts.malloc_junk == 2 && newsz > 0) | 1442 | if (mopts.malloc_junk == 2 && newsz > 0) { |
| 1390 | memset((char *)p + newsz, SOME_JUNK, oldsz - newsz); | 1443 | size_t usable_oldsz = oldsz; |
| 1444 | if (oldsz <= MALLOC_MAXCHUNK) | ||
| 1445 | usable_oldsz -= mopts.malloc_canaries; | ||
| 1446 | if (newsz < usable_oldsz) | ||
| 1447 | memset((char *)p + newsz, SOME_JUNK, usable_oldsz - newsz); | ||
| 1448 | } | ||
| 1391 | STATS_SETF(r, f); | 1449 | STATS_SETF(r, f); |
| 1392 | return p; | 1450 | return p; |
| 1393 | } else if (newsz != oldsz || mopts.malloc_realloc) { | 1451 | } else if (newsz != oldsz || mopts.malloc_realloc) { |
| 1394 | q = omalloc(newsz, 0, f); | 1452 | q = omalloc(newsz, 0, f); |
| 1395 | if (q == NULL) | 1453 | if (q == NULL) |
| 1396 | return NULL; | 1454 | return NULL; |
| 1397 | if (newsz != 0 && oldsz != 0) | 1455 | if (newsz != 0 && oldsz != 0) { |
| 1398 | memcpy(q, p, oldsz < newsz ? oldsz : newsz); | 1456 | size_t copysz = oldsz < newsz ? oldsz : newsz; |
| 1457 | if (copysz <= MALLOC_MAXCHUNK) | ||
| 1458 | copysz -= mopts.malloc_canaries; | ||
| 1459 | memcpy(q, p, copysz); | ||
| 1460 | } | ||
| 1399 | ofree(p); | 1461 | ofree(p); |
| 1400 | return q; | 1462 | return q; |
| 1401 | } else { | 1463 | } else { |
| @@ -1420,6 +1482,8 @@ realloc(void *ptr, size_t size) | |||
| 1420 | malloc_recurse(); | 1482 | malloc_recurse(); |
| 1421 | return NULL; | 1483 | return NULL; |
| 1422 | } | 1484 | } |
| 1485 | if (size > 0 && size <= MALLOC_MAXCHUNK) | ||
| 1486 | size += mopts.malloc_canaries; | ||
| 1423 | r = orealloc(ptr, size, CALLER); | 1487 | r = orealloc(ptr, size, CALLER); |
| 1424 | 1488 | ||
| 1425 | malloc_active--; | 1489 | malloc_active--; |
| @@ -1468,6 +1532,8 @@ calloc(size_t nmemb, size_t size) | |||
| 1468 | } | 1532 | } |
| 1469 | 1533 | ||
| 1470 | size *= nmemb; | 1534 | size *= nmemb; |
| 1535 | if (size > 0 && size <= MALLOC_MAXCHUNK) | ||
| 1536 | size += mopts.malloc_canaries; | ||
| 1471 | r = omalloc(size, 1, CALLER); | 1537 | r = omalloc(size, 1, CALLER); |
| 1472 | 1538 | ||
| 1473 | malloc_active--; | 1539 | malloc_active--; |
| @@ -1595,6 +1661,8 @@ posix_memalign(void **memptr, size_t alignment, size_t size) | |||
| 1595 | malloc_recurse(); | 1661 | malloc_recurse(); |
| 1596 | goto err; | 1662 | goto err; |
| 1597 | } | 1663 | } |
| 1664 | if (size > 0 && size <= MALLOC_MAXCHUNK) | ||
| 1665 | size += mopts.malloc_canaries; | ||
| 1598 | r = omemalign(alignment, size, 0, CALLER); | 1666 | r = omemalign(alignment, size, 0, CALLER); |
| 1599 | malloc_active--; | 1667 | malloc_active--; |
| 1600 | _MALLOC_UNLOCK(); | 1668 | _MALLOC_UNLOCK(); |
