diff options
| author | otto <> | 2021-02-25 15:20:18 +0000 |
|---|---|---|
| committer | otto <> | 2021-02-25 15:20:18 +0000 |
| commit | 827c99d1b55bae4268d905d4c4817f7add395c94 (patch) | |
| tree | 1e53fb8f01e90fb2daabd5c89841f55313e700eb /src/lib | |
| parent | 851054bdc5a74d09ef9d5133842a93fed3fc408e (diff) | |
| download | openbsd-827c99d1b55bae4268d905d4c4817f7add395c94.tar.gz openbsd-827c99d1b55bae4268d905d4c4817f7add395c94.tar.bz2 openbsd-827c99d1b55bae4268d905d4c4817f7add395c94.zip | |
- Make use of the fact that we know how the chunks are aligned, and
write 8 bytes at the time by using a uint64_t pointer. For an
allocation a max of 4 such uint64_t's are written spread over the
allocation. For pages sized and larger, the first page is junked in
such a way.
- Delayed free of a small chunk checks the corresponiding way.
- Pages ending up in the cache are validated upon unmapping or re-use.
In snaps for a while
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/libc/stdlib/malloc.3 | 6 | ||||
| -rw-r--r-- | src/lib/libc/stdlib/malloc.c | 126 |
2 files changed, 83 insertions, 49 deletions
diff --git a/src/lib/libc/stdlib/malloc.3 b/src/lib/libc/stdlib/malloc.3 index 0c7574034b..c27f965d0a 100644 --- a/src/lib/libc/stdlib/malloc.3 +++ b/src/lib/libc/stdlib/malloc.3 | |||
| @@ -30,9 +30,9 @@ | |||
| 30 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 30 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 31 | .\" SUCH DAMAGE. | 31 | .\" SUCH DAMAGE. |
| 32 | .\" | 32 | .\" |
| 33 | .\" $OpenBSD: malloc.3,v 1.126 2019/09/14 13:16:50 otto Exp $ | 33 | .\" $OpenBSD: malloc.3,v 1.127 2021/02/25 15:20:18 otto Exp $ |
| 34 | .\" | 34 | .\" |
| 35 | .Dd $Mdocdate: September 14 2019 $ | 35 | .Dd $Mdocdate: February 25 2021 $ |
| 36 | .Dt MALLOC 3 | 36 | .Dt MALLOC 3 |
| 37 | .Os | 37 | .Os |
| 38 | .Sh NAME | 38 | .Sh NAME |
| @@ -619,7 +619,7 @@ or | |||
| 619 | reallocate an unallocated pointer was made. | 619 | reallocate an unallocated pointer was made. |
| 620 | .It Dq chunk is already free | 620 | .It Dq chunk is already free |
| 621 | There was an attempt to free a chunk that had already been freed. | 621 | There was an attempt to free a chunk that had already been freed. |
| 622 | .It Dq use after free | 622 | .It Dq write after free |
| 623 | A chunk has been modified after it was freed. | 623 | A chunk has been modified after it was freed. |
| 624 | .It Dq modified chunk-pointer | 624 | .It Dq modified chunk-pointer |
| 625 | The pointer passed to | 625 | The pointer passed to |
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index 7004a0aa36..46b07ff77d 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.267 2020/11/23 15:42:11 otto Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.268 2021/02/25 15:20:18 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> |
| @@ -89,6 +89,7 @@ | |||
| 89 | */ | 89 | */ |
| 90 | #define SOME_JUNK 0xdb /* deadbeef */ | 90 | #define SOME_JUNK 0xdb /* deadbeef */ |
| 91 | #define SOME_FREEJUNK 0xdf /* dead, free */ | 91 | #define SOME_FREEJUNK 0xdf /* dead, free */ |
| 92 | #define SOME_FREEJUNK_ULL 0xdfdfdfdfdfdfdfdfULL | ||
| 92 | 93 | ||
| 93 | #define MMAP(sz,f) mmap(NULL, (sz), PROT_READ | PROT_WRITE, \ | 94 | #define MMAP(sz,f) mmap(NULL, (sz), PROT_READ | PROT_WRITE, \ |
| 94 | MAP_ANON | MAP_PRIVATE | (f), -1, 0) | 95 | MAP_ANON | MAP_PRIVATE | (f), -1, 0) |
| @@ -655,6 +656,49 @@ delete(struct dir_info *d, struct region_info *ri) | |||
| 655 | } | 656 | } |
| 656 | } | 657 | } |
| 657 | 658 | ||
| 659 | static inline void | ||
| 660 | junk_free(int junk, void *p, size_t sz) | ||
| 661 | { | ||
| 662 | size_t i, step = 1; | ||
| 663 | uint64_t *lp = p; | ||
| 664 | |||
| 665 | if (junk == 0 || sz == 0) | ||
| 666 | return; | ||
| 667 | sz /= sizeof(uint64_t); | ||
| 668 | if (junk == 1) { | ||
| 669 | if (sz > MALLOC_PAGESIZE / sizeof(uint64_t)) | ||
| 670 | sz = MALLOC_PAGESIZE / sizeof(uint64_t); | ||
| 671 | step = sz / 4; | ||
| 672 | if (step == 0) | ||
| 673 | step = 1; | ||
| 674 | } | ||
| 675 | for (i = 0; i < sz; i += step) | ||
| 676 | lp[i] = SOME_FREEJUNK_ULL; | ||
| 677 | } | ||
| 678 | |||
| 679 | static inline void | ||
| 680 | validate_junk(struct dir_info *pool, void *p, size_t sz) | ||
| 681 | { | ||
| 682 | size_t i, step = 1; | ||
| 683 | uint64_t *lp = p; | ||
| 684 | |||
| 685 | if (pool->malloc_junk == 0 || sz == 0) | ||
| 686 | return; | ||
| 687 | sz /= sizeof(uint64_t); | ||
| 688 | if (pool->malloc_junk == 1) { | ||
| 689 | if (sz > MALLOC_PAGESIZE / sizeof(uint64_t)) | ||
| 690 | sz = MALLOC_PAGESIZE / sizeof(uint64_t); | ||
| 691 | step = sz / 4; | ||
| 692 | if (step == 0) | ||
| 693 | step = 1; | ||
| 694 | } | ||
| 695 | for (i = 0; i < sz; i += step) { | ||
| 696 | if (lp[i] != SOME_FREEJUNK_ULL) | ||
| 697 | wrterror(pool, "write after free %p", p); | ||
| 698 | } | ||
| 699 | } | ||
| 700 | |||
| 701 | |||
| 658 | /* | 702 | /* |
| 659 | * Cache maintenance. We keep at most malloc_cache pages cached. | 703 | * Cache maintenance. We keep at most malloc_cache pages cached. |
| 660 | * If the cache is becoming full, unmap pages in the cache for real, | 704 | * If the cache is becoming full, unmap pages in the cache for real, |
| @@ -663,7 +707,7 @@ delete(struct dir_info *d, struct region_info *ri) | |||
| 663 | * cache are in MALLOC_PAGESIZE units. | 707 | * cache are in MALLOC_PAGESIZE units. |
| 664 | */ | 708 | */ |
| 665 | static void | 709 | static void |
| 666 | unmap(struct dir_info *d, void *p, size_t sz, size_t clear, int junk) | 710 | unmap(struct dir_info *d, void *p, size_t sz, size_t clear) |
| 667 | { | 711 | { |
| 668 | size_t psz = sz >> MALLOC_PAGESHIFT; | 712 | size_t psz = sz >> MALLOC_PAGESHIFT; |
| 669 | size_t rsz; | 713 | size_t rsz; |
| @@ -695,6 +739,8 @@ unmap(struct dir_info *d, void *p, size_t sz, size_t clear, int junk) | |||
| 695 | r = &d->free_regions[(i + offset) & mask]; | 739 | r = &d->free_regions[(i + offset) & mask]; |
| 696 | if (r->p != NULL) { | 740 | if (r->p != NULL) { |
| 697 | rsz = r->size << MALLOC_PAGESHIFT; | 741 | rsz = r->size << MALLOC_PAGESHIFT; |
| 742 | if (!mopts.malloc_freeunmap) | ||
| 743 | validate_junk(d, r->p, rsz); | ||
| 698 | if (munmap(r->p, rsz)) | 744 | if (munmap(r->p, rsz)) |
| 699 | wrterror(d, "munmap %p", r->p); | 745 | wrterror(d, "munmap %p", r->p); |
| 700 | r->p = NULL; | 746 | r->p = NULL; |
| @@ -716,12 +762,11 @@ unmap(struct dir_info *d, void *p, size_t sz, size_t clear, int junk) | |||
| 716 | if (r->p == NULL) { | 762 | if (r->p == NULL) { |
| 717 | if (clear > 0) | 763 | if (clear > 0) |
| 718 | memset(p, 0, clear); | 764 | memset(p, 0, clear); |
| 719 | if (junk && !mopts.malloc_freeunmap) { | ||
| 720 | size_t amt = junk == 1 ? MALLOC_MAXCHUNK : sz; | ||
| 721 | memset(p, SOME_FREEJUNK, amt); | ||
| 722 | } | ||
| 723 | if (mopts.malloc_freeunmap) | 765 | if (mopts.malloc_freeunmap) |
| 724 | mprotect(p, sz, PROT_NONE); | 766 | mprotect(p, sz, PROT_NONE); |
| 767 | else | ||
| 768 | junk_free(d->malloc_junk, p, | ||
| 769 | psz << MALLOC_PAGESHIFT); | ||
| 725 | r->p = p; | 770 | r->p = p; |
| 726 | r->size = psz; | 771 | r->size = psz; |
| 727 | d->free_regions_size += psz; | 772 | d->free_regions_size += psz; |
| @@ -760,15 +805,17 @@ map(struct dir_info *d, size_t sz, int zero_fill) | |||
| 760 | if (r->p != NULL) { | 805 | if (r->p != NULL) { |
| 761 | if (r->size == psz) { | 806 | if (r->size == psz) { |
| 762 | p = r->p; | 807 | p = r->p; |
| 808 | if (!mopts.malloc_freeunmap) | ||
| 809 | validate_junk(d, p, | ||
| 810 | psz << MALLOC_PAGESHIFT); | ||
| 763 | r->p = NULL; | 811 | r->p = NULL; |
| 764 | d->free_regions_size -= psz; | 812 | d->free_regions_size -= psz; |
| 765 | if (mopts.malloc_freeunmap) | 813 | if (mopts.malloc_freeunmap) |
| 766 | mprotect(p, sz, PROT_READ | PROT_WRITE); | 814 | mprotect(p, sz, PROT_READ | PROT_WRITE); |
| 767 | if (zero_fill) | 815 | if (zero_fill) |
| 768 | memset(p, 0, sz); | 816 | memset(p, 0, sz); |
| 769 | else if (d->malloc_junk == 2 && | 817 | else if (mopts.malloc_freeunmap) |
| 770 | mopts.malloc_freeunmap) | 818 | junk_free(d->malloc_junk, p, sz); |
| 771 | memset(p, SOME_FREEJUNK, sz); | ||
| 772 | d->rotor += i + 1; | 819 | d->rotor += i + 1; |
| 773 | return p; | 820 | return p; |
| 774 | } else if (r->size > psz) | 821 | } else if (r->size > psz) |
| @@ -778,15 +825,20 @@ map(struct dir_info *d, size_t sz, int zero_fill) | |||
| 778 | if (big != NULL) { | 825 | if (big != NULL) { |
| 779 | r = big; | 826 | r = big; |
| 780 | p = r->p; | 827 | p = r->p; |
| 828 | if (!mopts.malloc_freeunmap) | ||
| 829 | validate_junk(d, p, r->size << MALLOC_PAGESHIFT); | ||
| 781 | r->p = (char *)r->p + (psz << MALLOC_PAGESHIFT); | 830 | r->p = (char *)r->p + (psz << MALLOC_PAGESHIFT); |
| 782 | if (mopts.malloc_freeunmap) | ||
| 783 | mprotect(p, sz, PROT_READ | PROT_WRITE); | ||
| 784 | r->size -= psz; | 831 | r->size -= psz; |
| 785 | d->free_regions_size -= psz; | 832 | d->free_regions_size -= psz; |
| 833 | if (mopts.malloc_freeunmap) | ||
| 834 | mprotect(p, sz, PROT_READ | PROT_WRITE); | ||
| 835 | else | ||
| 836 | junk_free(d->malloc_junk, r->p, | ||
| 837 | r->size << MALLOC_PAGESHIFT); | ||
| 786 | if (zero_fill) | 838 | if (zero_fill) |
| 787 | memset(p, 0, sz); | 839 | memset(p, 0, sz); |
| 788 | else if (d->malloc_junk == 2 && mopts.malloc_freeunmap) | 840 | else if (mopts.malloc_freeunmap) |
| 789 | memset(p, SOME_FREEJUNK, sz); | 841 | junk_free(d->malloc_junk, p, sz); |
| 790 | return p; | 842 | return p; |
| 791 | } | 843 | } |
| 792 | if (d->free_regions_size > d->malloc_cache) | 844 | if (d->free_regions_size > d->malloc_cache) |
| @@ -892,7 +944,7 @@ omalloc_make_chunks(struct dir_info *d, int bits, int listnum) | |||
| 892 | return bp; | 944 | return bp; |
| 893 | 945 | ||
| 894 | err: | 946 | err: |
| 895 | unmap(d, pp, MALLOC_PAGESIZE, 0, d->malloc_junk); | 947 | unmap(d, pp, MALLOC_PAGESIZE, 0); |
| 896 | return NULL; | 948 | return NULL; |
| 897 | } | 949 | } |
| 898 | 950 | ||
| @@ -1091,7 +1143,7 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr) | |||
| 1091 | 1143 | ||
| 1092 | if (info->size == 0 && !mopts.malloc_freeunmap) | 1144 | if (info->size == 0 && !mopts.malloc_freeunmap) |
| 1093 | mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); | 1145 | mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); |
| 1094 | unmap(d, info->page, MALLOC_PAGESIZE, 0, 0); | 1146 | unmap(d, info->page, MALLOC_PAGESIZE, 0); |
| 1095 | 1147 | ||
| 1096 | delete(d, r); | 1148 | delete(d, r); |
| 1097 | if (info->size != 0) | 1149 | if (info->size != 0) |
| @@ -1122,7 +1174,7 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) | |||
| 1122 | return NULL; | 1174 | return NULL; |
| 1123 | } | 1175 | } |
| 1124 | if (insert(pool, p, sz, f)) { | 1176 | if (insert(pool, p, sz, f)) { |
| 1125 | unmap(pool, p, psz, 0, 0); | 1177 | unmap(pool, p, psz, 0); |
| 1126 | errno = ENOMEM; | 1178 | errno = ENOMEM; |
| 1127 | return NULL; | 1179 | return NULL; |
| 1128 | } | 1180 | } |
| @@ -1282,27 +1334,6 @@ malloc_conceal(size_t size) | |||
| 1282 | } | 1334 | } |
| 1283 | DEF_WEAK(malloc_conceal); | 1335 | DEF_WEAK(malloc_conceal); |
| 1284 | 1336 | ||
| 1285 | static void | ||
| 1286 | validate_junk(struct dir_info *pool, void *p) | ||
| 1287 | { | ||
| 1288 | struct region_info *r; | ||
| 1289 | size_t byte, sz; | ||
| 1290 | |||
| 1291 | if (p == NULL) | ||
| 1292 | return; | ||
| 1293 | r = find(pool, p); | ||
| 1294 | if (r == NULL) | ||
| 1295 | wrterror(pool, "bogus pointer in validate_junk %p", p); | ||
| 1296 | REALSIZE(sz, r); | ||
| 1297 | if (sz > CHUNK_CHECK_LENGTH) | ||
| 1298 | sz = CHUNK_CHECK_LENGTH; | ||
| 1299 | for (byte = 0; byte < sz; byte++) { | ||
| 1300 | if (((unsigned char *)p)[byte] != SOME_FREEJUNK) | ||
| 1301 | wrterror(pool, "use after free %p", p); | ||
| 1302 | } | ||
| 1303 | } | ||
| 1304 | |||
| 1305 | |||
| 1306 | static struct region_info * | 1337 | static struct region_info * |
| 1307 | findpool(void *p, struct dir_info *argpool, struct dir_info **foundpool, | 1338 | findpool(void *p, struct dir_info *argpool, struct dir_info **foundpool, |
| 1308 | char **saved_function) | 1339 | char **saved_function) |
| @@ -1402,8 +1433,7 @@ ofree(struct dir_info **argpool, void *p, int clear, int check, size_t argsz) | |||
| 1402 | } | 1433 | } |
| 1403 | STATS_SUB(pool->malloc_guarded, mopts.malloc_guard); | 1434 | STATS_SUB(pool->malloc_guarded, mopts.malloc_guard); |
| 1404 | } | 1435 | } |
| 1405 | unmap(pool, p, PAGEROUND(sz), clear ? argsz : 0, | 1436 | unmap(pool, p, PAGEROUND(sz), clear ? argsz : 0); |
| 1406 | pool->malloc_junk); | ||
| 1407 | delete(pool, r); | 1437 | delete(pool, r); |
| 1408 | } else { | 1438 | } else { |
| 1409 | /* Validate and optionally canary check */ | 1439 | /* Validate and optionally canary check */ |
| @@ -1419,20 +1449,24 @@ ofree(struct dir_info **argpool, void *p, int clear, int check, size_t argsz) | |||
| 1419 | wrterror(pool, | 1449 | wrterror(pool, |
| 1420 | "double free %p", p); | 1450 | "double free %p", p); |
| 1421 | } | 1451 | } |
| 1422 | if (pool->malloc_junk && sz > 0) | 1452 | junk_free(pool->malloc_junk, p, sz); |
| 1423 | memset(p, SOME_FREEJUNK, sz); | ||
| 1424 | i = getrbyte(pool) & MALLOC_DELAYED_CHUNK_MASK; | 1453 | i = getrbyte(pool) & MALLOC_DELAYED_CHUNK_MASK; |
| 1425 | tmp = p; | 1454 | tmp = p; |
| 1426 | p = pool->delayed_chunks[i]; | 1455 | p = pool->delayed_chunks[i]; |
| 1427 | if (tmp == p) | 1456 | if (tmp == p) |
| 1428 | wrterror(pool, "double free %p", tmp); | 1457 | wrterror(pool, "double free %p", tmp); |
| 1429 | pool->delayed_chunks[i] = tmp; | 1458 | pool->delayed_chunks[i] = tmp; |
| 1430 | if (pool->malloc_junk) | 1459 | if (p != NULL) { |
| 1431 | validate_junk(pool, p); | 1460 | r = find(pool, p); |
| 1432 | } else if (argsz > 0) | 1461 | REALSIZE(sz, r); |
| 1462 | if (r != NULL) | ||
| 1463 | validate_junk(pool, p, sz); | ||
| 1464 | } | ||
| 1465 | } else if (argsz > 0) { | ||
| 1466 | r = find(pool, p); | ||
| 1433 | memset(p, 0, argsz); | 1467 | memset(p, 0, argsz); |
| 1468 | } | ||
| 1434 | if (p != NULL) { | 1469 | if (p != NULL) { |
| 1435 | r = find(pool, p); | ||
| 1436 | if (r == NULL) | 1470 | if (r == NULL) |
| 1437 | wrterror(pool, | 1471 | wrterror(pool, |
| 1438 | "bogus pointer (double free?) %p", p); | 1472 | "bogus pointer (double free?) %p", p); |
| @@ -1969,7 +2003,7 @@ omemalign(struct dir_info *pool, size_t alignment, size_t sz, int zero_fill, | |||
| 1969 | } | 2003 | } |
| 1970 | 2004 | ||
| 1971 | if (insert(pool, p, sz, f)) { | 2005 | if (insert(pool, p, sz, f)) { |
| 1972 | unmap(pool, p, psz, 0, 0); | 2006 | unmap(pool, p, psz, 0); |
| 1973 | errno = ENOMEM; | 2007 | errno = ENOMEM; |
| 1974 | return NULL; | 2008 | return NULL; |
| 1975 | } | 2009 | } |
