summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorotto <>2021-02-25 15:20:18 +0000
committerotto <>2021-02-25 15:20:18 +0000
commit827c99d1b55bae4268d905d4c4817f7add395c94 (patch)
tree1e53fb8f01e90fb2daabd5c89841f55313e700eb
parent851054bdc5a74d09ef9d5133842a93fed3fc408e (diff)
downloadopenbsd-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
-rw-r--r--src/lib/libc/stdlib/malloc.36
-rw-r--r--src/lib/libc/stdlib/malloc.c126
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
619reallocate an unallocated pointer was made. 619reallocate an unallocated pointer was made.
620.It Dq chunk is already free 620.It Dq chunk is already free
621There was an attempt to free a chunk that had already been freed. 621There was an attempt to free a chunk that had already been freed.
622.It Dq use after free 622.It Dq write after free
623A chunk has been modified after it was freed. 623A chunk has been modified after it was freed.
624.It Dq modified chunk-pointer 624.It Dq modified chunk-pointer
625The pointer passed to 625The 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
659static inline void
660junk_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
679static inline void
680validate_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 */
665static void 709static void
666unmap(struct dir_info *d, void *p, size_t sz, size_t clear, int junk) 710unmap(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
894err: 946err:
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}
1283DEF_WEAK(malloc_conceal); 1335DEF_WEAK(malloc_conceal);
1284 1336
1285static void
1286validate_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
1306static struct region_info * 1337static struct region_info *
1307findpool(void *p, struct dir_info *argpool, struct dir_info **foundpool, 1338findpool(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 }