diff options
| author | otto <> | 2016-10-07 05:54:35 +0000 |
|---|---|---|
| committer | otto <> | 2016-10-07 05:54:35 +0000 |
| commit | 15a3c9b626616f87c20513e7e47c983dff929aef (patch) | |
| tree | 0afbe1812d042d5d560f4006bb03ba845f6ab1fa /src/lib/libc/stdlib/malloc.c | |
| parent | a9fc1dae75393c36afdc70f29846f7bd0d73b5a7 (diff) | |
| download | openbsd-15a3c9b626616f87c20513e7e47c983dff929aef.tar.gz openbsd-15a3c9b626616f87c20513e7e47c983dff929aef.tar.bz2 openbsd-15a3c9b626616f87c20513e7e47c983dff929aef.zip | |
Beter implementation of chunk canaries: store size in chunk meta data
instead of chunk itself; does not change actual allocated size; ok tedu@
Diffstat (limited to 'src/lib/libc/stdlib/malloc.c')
| -rw-r--r-- | src/lib/libc/stdlib/malloc.c | 124 |
1 files changed, 63 insertions, 61 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index 20807a25b4..c5940ffad9 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.197 2016/09/21 04:38:56 guenther Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.198 2016/10/07 05:54:35 otto Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net> | 3 | * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net> |
| 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> | 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> |
| @@ -64,6 +64,7 @@ | |||
| 64 | #define MALLOC_INITIAL_REGIONS 512 | 64 | #define MALLOC_INITIAL_REGIONS 512 |
| 65 | #define MALLOC_DEFAULT_CACHE 64 | 65 | #define MALLOC_DEFAULT_CACHE 64 |
| 66 | #define MALLOC_CHUNK_LISTS 4 | 66 | #define MALLOC_CHUNK_LISTS 4 |
| 67 | #define CHUNK_CHECK_LENGTH 32 | ||
| 67 | 68 | ||
| 68 | /* | 69 | /* |
| 69 | * When the P option is active, we move allocations between half a page | 70 | * When the P option is active, we move allocations between half a page |
| @@ -178,14 +179,13 @@ struct malloc_readonly { | |||
| 178 | int malloc_move; /* move allocations to end of page? */ | 179 | int malloc_move; /* move allocations to end of page? */ |
| 179 | int malloc_realloc; /* always realloc? */ | 180 | int malloc_realloc; /* always realloc? */ |
| 180 | int malloc_xmalloc; /* xmalloc behaviour? */ | 181 | int malloc_xmalloc; /* xmalloc behaviour? */ |
| 181 | size_t malloc_canaries; /* use canaries after chunks? */ | 182 | int chunk_canaries; /* use canaries after chunks? */ |
| 182 | size_t malloc_guard; /* use guard pages after allocations? */ | 183 | size_t malloc_guard; /* use guard pages after allocations? */ |
| 183 | u_int malloc_cache; /* free pages we cache */ | 184 | u_int malloc_cache; /* free pages we cache */ |
| 184 | #ifdef MALLOC_STATS | 185 | #ifdef MALLOC_STATS |
| 185 | int malloc_stats; /* dump statistics at end */ | 186 | int malloc_stats; /* dump statistics at end */ |
| 186 | #endif | 187 | #endif |
| 187 | u_int32_t malloc_canary; /* Matched against ones in malloc_pool */ | 188 | u_int32_t malloc_canary; /* Matched against ones in malloc_pool */ |
| 188 | uintptr_t malloc_chunk_canary; | ||
| 189 | }; | 189 | }; |
| 190 | 190 | ||
| 191 | /* This object is mapped PROT_READ after initialisation to prevent tampering */ | 191 | /* This object is mapped PROT_READ after initialisation to prevent tampering */ |
| @@ -288,7 +288,7 @@ wrterror(struct dir_info *d, char *msg, void *p) | |||
| 288 | if (p == NULL) | 288 | if (p == NULL) |
| 289 | iov[5].iov_len = 0; | 289 | iov[5].iov_len = 0; |
| 290 | else { | 290 | else { |
| 291 | snprintf(buf, sizeof(buf), " %p", p); | 291 | snprintf(buf, sizeof(buf), " %010p", p); |
| 292 | iov[5].iov_len = strlen(buf); | 292 | iov[5].iov_len = strlen(buf); |
| 293 | } | 293 | } |
| 294 | iov[6].iov_base = "\n"; | 294 | iov[6].iov_base = "\n"; |
| @@ -512,10 +512,10 @@ omalloc_parseopt(char opt) | |||
| 512 | /* ignored */ | 512 | /* ignored */ |
| 513 | break; | 513 | break; |
| 514 | case 'c': | 514 | case 'c': |
| 515 | mopts.malloc_canaries = 0; | 515 | mopts.chunk_canaries = 0; |
| 516 | break; | 516 | break; |
| 517 | case 'C': | 517 | case 'C': |
| 518 | mopts.malloc_canaries = sizeof(void *); | 518 | mopts.chunk_canaries = 1; |
| 519 | break; | 519 | break; |
| 520 | #ifdef MALLOC_STATS | 520 | #ifdef MALLOC_STATS |
| 521 | case 'd': | 521 | case 'd': |
| @@ -653,9 +653,6 @@ omalloc_init(void) | |||
| 653 | 653 | ||
| 654 | while ((mopts.malloc_canary = arc4random()) == 0) | 654 | while ((mopts.malloc_canary = arc4random()) == 0) |
| 655 | ; | 655 | ; |
| 656 | |||
| 657 | arc4random_buf(&mopts.malloc_chunk_canary, | ||
| 658 | sizeof(mopts.malloc_chunk_canary)); | ||
| 659 | } | 656 | } |
| 660 | 657 | ||
| 661 | /* | 658 | /* |
| @@ -763,6 +760,8 @@ alloc_chunk_info(struct dir_info *d, int bits) | |||
| 763 | 760 | ||
| 764 | size = howmany(count, MALLOC_BITS); | 761 | size = howmany(count, MALLOC_BITS); |
| 765 | size = sizeof(struct chunk_info) + (size - 1) * sizeof(u_short); | 762 | size = sizeof(struct chunk_info) + (size - 1) * sizeof(u_short); |
| 763 | if (mopts.chunk_canaries) | ||
| 764 | size += count * sizeof(u_short); | ||
| 766 | size = ALIGN(size); | 765 | size = ALIGN(size); |
| 767 | 766 | ||
| 768 | if (LIST_EMPTY(&d->chunk_info_list[bits])) { | 767 | if (LIST_EMPTY(&d->chunk_info_list[bits])) { |
| @@ -946,16 +945,19 @@ omalloc_make_chunks(struct dir_info *d, int bits, int listnum) | |||
| 946 | * Allocate a chunk | 945 | * Allocate a chunk |
| 947 | */ | 946 | */ |
| 948 | static void * | 947 | static void * |
| 949 | malloc_bytes(struct dir_info *d, size_t size, void *f) | 948 | malloc_bytes(struct dir_info *d, size_t argsize, void *f) |
| 950 | { | 949 | { |
| 951 | int i, j, listnum; | 950 | int i, j, listnum; |
| 952 | size_t k; | 951 | size_t k, size; |
| 953 | u_short u, *lp; | 952 | u_short u, *lp; |
| 954 | struct chunk_info *bp; | 953 | struct chunk_info *bp; |
| 955 | 954 | ||
| 956 | if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || | 955 | if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || |
| 957 | d->canary1 != ~d->canary2) | 956 | d->canary1 != ~d->canary2) |
| 958 | wrterror(d, "internal struct corrupt", NULL); | 957 | wrterror(d, "internal struct corrupt", NULL); |
| 958 | |||
| 959 | size = argsize; | ||
| 960 | |||
| 959 | /* Don't bother with anything less than this */ | 961 | /* Don't bother with anything less than this */ |
| 960 | /* unless we have a malloc(0) requests */ | 962 | /* unless we have a malloc(0) requests */ |
| 961 | if (size != 0 && size < MALLOC_MINSIZE) | 963 | if (size != 0 && size < MALLOC_MINSIZE) |
| @@ -1021,22 +1023,28 @@ malloc_bytes(struct dir_info *d, size_t size, void *f) | |||
| 1021 | 1023 | ||
| 1022 | /* Adjust to the real offset of that chunk */ | 1024 | /* Adjust to the real offset of that chunk */ |
| 1023 | k += (lp - bp->bits) * MALLOC_BITS; | 1025 | k += (lp - bp->bits) * MALLOC_BITS; |
| 1026 | |||
| 1027 | if (mopts.chunk_canaries) | ||
| 1028 | bp->bits[howmany(bp->total, MALLOC_BITS) + k] = argsize; | ||
| 1029 | |||
| 1024 | k <<= bp->shift; | 1030 | k <<= bp->shift; |
| 1025 | 1031 | ||
| 1026 | if (mopts.malloc_canaries && bp->size > 0) { | 1032 | if (bp->size > 0) { |
| 1027 | char *end = (char *)bp->page + k + bp->size; | 1033 | if (mopts.malloc_junk == 2) |
| 1028 | uintptr_t *canary = (uintptr_t *)(end - mopts.malloc_canaries); | 1034 | memset((char *)bp->page + k, SOME_JUNK, bp->size); |
| 1029 | *canary = mopts.malloc_chunk_canary ^ hash(canary); | 1035 | else if (mopts.chunk_canaries) { |
| 1030 | } | 1036 | size_t sz = bp->size - argsize; |
| 1031 | 1037 | ||
| 1032 | if (mopts.malloc_junk == 2 && bp->size > 0) | 1038 | if (sz > CHUNK_CHECK_LENGTH) |
| 1033 | memset((char *)bp->page + k, SOME_JUNK, | 1039 | sz = CHUNK_CHECK_LENGTH; |
| 1034 | bp->size - mopts.malloc_canaries); | 1040 | memset((char *)bp->page + k + argsize, SOME_JUNK, sz); |
| 1041 | } | ||
| 1042 | } | ||
| 1035 | return ((char *)bp->page + k); | 1043 | return ((char *)bp->page + k); |
| 1036 | } | 1044 | } |
| 1037 | 1045 | ||
| 1038 | static uint32_t | 1046 | static uint32_t |
| 1039 | find_chunknum(struct dir_info *d, struct region_info *r, void *ptr) | 1047 | find_chunknum(struct dir_info *d, struct region_info *r, void *ptr, int check) |
| 1040 | { | 1048 | { |
| 1041 | struct chunk_info *info; | 1049 | struct chunk_info *info; |
| 1042 | uint32_t chunknum; | 1050 | uint32_t chunknum; |
| @@ -1045,15 +1053,25 @@ find_chunknum(struct dir_info *d, struct region_info *r, void *ptr) | |||
| 1045 | if (info->canary != d->canary1) | 1053 | if (info->canary != d->canary1) |
| 1046 | wrterror(d, "chunk info corrupted", NULL); | 1054 | wrterror(d, "chunk info corrupted", NULL); |
| 1047 | 1055 | ||
| 1048 | if (mopts.malloc_canaries && info->size > 0) { | ||
| 1049 | char *end = (char *)ptr + info->size; | ||
| 1050 | uintptr_t *canary = (uintptr_t *)(end - mopts.malloc_canaries); | ||
| 1051 | if (*canary != (mopts.malloc_chunk_canary ^ hash(canary))) | ||
| 1052 | wrterror(d, "chunk canary corrupted", ptr); | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | /* Find the chunk number on the page */ | 1056 | /* Find the chunk number on the page */ |
| 1056 | chunknum = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift; | 1057 | chunknum = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift; |
| 1058 | if (check && mopts.chunk_canaries && info->size > 0) { | ||
| 1059 | size_t sz = info->bits[howmany(info->total, MALLOC_BITS) + | ||
| 1060 | chunknum]; | ||
| 1061 | size_t check_sz = info->size - sz; | ||
| 1062 | u_char *p, *q; | ||
| 1063 | |||
| 1064 | if (check_sz > CHUNK_CHECK_LENGTH) | ||
| 1065 | check_sz = CHUNK_CHECK_LENGTH; | ||
| 1066 | p = (u_char *)ptr + sz; | ||
| 1067 | q = p + check_sz; | ||
| 1068 | |||
| 1069 | while (p < q) | ||
| 1070 | if (*p++ != SOME_JUNK) { | ||
| 1071 | q = (void *)(sz << 16 | p - (u_char *)ptr - 1); | ||
| 1072 | wrterror(d, "chunk canary corrupted: ", q); | ||
| 1073 | } | ||
| 1074 | } | ||
| 1057 | 1075 | ||
| 1058 | if ((uintptr_t)ptr & ((1U << (info->shift)) - 1)) | 1076 | if ((uintptr_t)ptr & ((1U << (info->shift)) - 1)) |
| 1059 | wrterror(d, "modified chunk-pointer", ptr); | 1077 | wrterror(d, "modified chunk-pointer", ptr); |
| @@ -1075,8 +1093,7 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr) | |||
| 1075 | int listnum; | 1093 | int listnum; |
| 1076 | 1094 | ||
| 1077 | info = (struct chunk_info *)r->size; | 1095 | info = (struct chunk_info *)r->size; |
| 1078 | if ((chunknum = find_chunknum(d, r, ptr)) == -1) | 1096 | chunknum = find_chunknum(d, r, ptr, 0); |
| 1079 | return; | ||
| 1080 | 1097 | ||
| 1081 | info->bits[chunknum / MALLOC_BITS] |= 1U << (chunknum % MALLOC_BITS); | 1098 | info->bits[chunknum / MALLOC_BITS] |= 1U << (chunknum % MALLOC_BITS); |
| 1082 | info->free++; | 1099 | info->free++; |
| @@ -1169,7 +1186,7 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) | |||
| 1169 | /* takes care of SOME_JUNK */ | 1186 | /* takes care of SOME_JUNK */ |
| 1170 | p = malloc_bytes(pool, sz, f); | 1187 | p = malloc_bytes(pool, sz, f); |
| 1171 | if (zero_fill && p != NULL && sz > 0) | 1188 | if (zero_fill && p != NULL && sz > 0) |
| 1172 | memset(p, 0, sz - mopts.malloc_canaries); | 1189 | memset(p, 0, sz); |
| 1173 | } | 1190 | } |
| 1174 | 1191 | ||
| 1175 | return p; | 1192 | return p; |
| @@ -1251,8 +1268,6 @@ malloc(size_t size) | |||
| 1251 | malloc_recurse(d); | 1268 | malloc_recurse(d); |
| 1252 | return NULL; | 1269 | return NULL; |
| 1253 | } | 1270 | } |
| 1254 | if (size > 0 && size <= MALLOC_MAXCHUNK) | ||
| 1255 | size += mopts.malloc_canaries; | ||
| 1256 | r = omalloc(d, size, 0, CALLER); | 1271 | r = omalloc(d, size, 0, CALLER); |
| 1257 | d->active--; | 1272 | d->active--; |
| 1258 | _MALLOC_UNLOCK(d->mutex); | 1273 | _MALLOC_UNLOCK(d->mutex); |
| @@ -1275,10 +1290,8 @@ validate_junk(struct dir_info *pool, void *p) { | |||
| 1275 | if (r == NULL) | 1290 | if (r == NULL) |
| 1276 | wrterror(pool, "bogus pointer in validate_junk", p); | 1291 | wrterror(pool, "bogus pointer in validate_junk", p); |
| 1277 | REALSIZE(sz, r); | 1292 | REALSIZE(sz, r); |
| 1278 | if (sz > 0 && sz <= MALLOC_MAXCHUNK) | 1293 | if (sz > CHUNK_CHECK_LENGTH) |
| 1279 | sz -= mopts.malloc_canaries; | 1294 | sz = CHUNK_CHECK_LENGTH; |
| 1280 | if (sz > 32) | ||
| 1281 | sz = 32; | ||
| 1282 | for (byte = 0; byte < sz; byte++) { | 1295 | for (byte = 0; byte < sz; byte++) { |
| 1283 | if (((unsigned char *)p)[byte] != SOME_FREEJUNK) | 1296 | if (((unsigned char *)p)[byte] != SOME_FREEJUNK) |
| 1284 | wrterror(pool, "use after free", p); | 1297 | wrterror(pool, "use after free", p); |
| @@ -1347,11 +1360,10 @@ ofree(struct dir_info *argpool, void *p) | |||
| 1347 | void *tmp; | 1360 | void *tmp; |
| 1348 | int i; | 1361 | int i; |
| 1349 | 1362 | ||
| 1350 | if (mopts.malloc_junk && sz > 0) | ||
| 1351 | memset(p, SOME_FREEJUNK, sz - mopts.malloc_canaries); | ||
| 1352 | if (!mopts.malloc_freenow) { | 1363 | if (!mopts.malloc_freenow) { |
| 1353 | if (find_chunknum(pool, r, p) == -1) | 1364 | find_chunknum(pool, r, p, 1); |
| 1354 | goto done; | 1365 | if (mopts.malloc_junk && sz > 0) |
| 1366 | memset(p, SOME_FREEJUNK, sz); | ||
| 1355 | i = getrbyte(pool) & MALLOC_DELAYED_CHUNK_MASK; | 1367 | i = getrbyte(pool) & MALLOC_DELAYED_CHUNK_MASK; |
| 1356 | tmp = p; | 1368 | tmp = p; |
| 1357 | p = pool->delayed_chunks[i]; | 1369 | p = pool->delayed_chunks[i]; |
| @@ -1360,6 +1372,9 @@ ofree(struct dir_info *argpool, void *p) | |||
| 1360 | if (mopts.malloc_junk) | 1372 | if (mopts.malloc_junk) |
| 1361 | validate_junk(pool, p); | 1373 | validate_junk(pool, p); |
| 1362 | pool->delayed_chunks[i] = tmp; | 1374 | pool->delayed_chunks[i] = tmp; |
| 1375 | } else { | ||
| 1376 | if (mopts.malloc_junk && sz > 0) | ||
| 1377 | memset(p, SOME_FREEJUNK, sz); | ||
| 1363 | } | 1378 | } |
| 1364 | if (p != NULL) { | 1379 | if (p != NULL) { |
| 1365 | r = find(pool, p); | 1380 | r = find(pool, p); |
| @@ -1516,28 +1531,21 @@ gotit: | |||
| 1516 | goto done; | 1531 | goto done; |
| 1517 | } | 1532 | } |
| 1518 | } | 1533 | } |
| 1519 | if (newsz <= oldsz && newsz > oldsz / 2 && !mopts.malloc_realloc) { | 1534 | if (newsz <= oldsz && newsz > oldsz / 2 && !mopts.chunk_canaries && |
| 1520 | if (mopts.malloc_junk == 2 && newsz > 0) { | 1535 | !mopts.malloc_realloc) { |
| 1521 | size_t usable_oldsz = oldsz; | 1536 | if (mopts.malloc_junk == 2 && newsz > 0) |
| 1522 | if (oldsz <= MALLOC_MAXCHUNK) | 1537 | memset((char *)p + newsz, SOME_JUNK, oldsz - newsz); |
| 1523 | usable_oldsz -= mopts.malloc_canaries; | ||
| 1524 | if (newsz < usable_oldsz) | ||
| 1525 | memset((char *)p + newsz, SOME_JUNK, usable_oldsz - newsz); | ||
| 1526 | } | ||
| 1527 | STATS_SETF(r, f); | 1538 | STATS_SETF(r, f); |
| 1528 | ret = p; | 1539 | ret = p; |
| 1529 | } else if (newsz != oldsz || mopts.malloc_realloc) { | 1540 | } else if (newsz != oldsz || mopts.chunk_canaries || |
| 1541 | mopts.malloc_realloc) { | ||
| 1530 | q = omalloc(pool, newsz, 0, f); | 1542 | q = omalloc(pool, newsz, 0, f); |
| 1531 | if (q == NULL) { | 1543 | if (q == NULL) { |
| 1532 | ret = NULL; | 1544 | ret = NULL; |
| 1533 | goto done; | 1545 | goto done; |
| 1534 | } | 1546 | } |
| 1535 | if (newsz != 0 && oldsz != 0) { | 1547 | if (newsz != 0 && oldsz != 0) |
| 1536 | size_t copysz = oldsz < newsz ? oldsz : newsz; | 1548 | memcpy(q, p, oldsz < newsz ? oldsz : newsz); |
| 1537 | if (copysz <= MALLOC_MAXCHUNK) | ||
| 1538 | copysz -= mopts.malloc_canaries; | ||
| 1539 | memcpy(q, p, copysz); | ||
| 1540 | } | ||
| 1541 | ofree(pool, p); | 1549 | ofree(pool, p); |
| 1542 | ret = q; | 1550 | ret = q; |
| 1543 | } else { | 1551 | } else { |
| @@ -1572,8 +1580,6 @@ realloc(void *ptr, size_t size) | |||
| 1572 | malloc_recurse(d); | 1580 | malloc_recurse(d); |
| 1573 | return NULL; | 1581 | return NULL; |
| 1574 | } | 1582 | } |
| 1575 | if (size > 0 && size <= MALLOC_MAXCHUNK) | ||
| 1576 | size += mopts.malloc_canaries; | ||
| 1577 | r = orealloc(d, ptr, size, CALLER); | 1583 | r = orealloc(d, ptr, size, CALLER); |
| 1578 | 1584 | ||
| 1579 | d->active--; | 1585 | d->active--; |
| @@ -1622,8 +1628,6 @@ calloc(size_t nmemb, size_t size) | |||
| 1622 | } | 1628 | } |
| 1623 | 1629 | ||
| 1624 | size *= nmemb; | 1630 | size *= nmemb; |
| 1625 | if (size > 0 && size <= MALLOC_MAXCHUNK) | ||
| 1626 | size += mopts.malloc_canaries; | ||
| 1627 | r = omalloc(d, size, 1, CALLER); | 1631 | r = omalloc(d, size, 1, CALLER); |
| 1628 | 1632 | ||
| 1629 | d->active--; | 1633 | d->active--; |
| @@ -1746,8 +1750,6 @@ posix_memalign(void **memptr, size_t alignment, size_t size) | |||
| 1746 | malloc_recurse(d); | 1750 | malloc_recurse(d); |
| 1747 | goto err; | 1751 | goto err; |
| 1748 | } | 1752 | } |
| 1749 | if (size > 0 && size <= MALLOC_MAXCHUNK) | ||
| 1750 | size += mopts.malloc_canaries; | ||
| 1751 | r = omemalign(d, alignment, size, 0, CALLER); | 1753 | r = omemalign(d, alignment, size, 0, CALLER); |
| 1752 | d->active--; | 1754 | d->active--; |
| 1753 | _MALLOC_UNLOCK(d->mutex); | 1755 | _MALLOC_UNLOCK(d->mutex); |
