diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libc/stdlib/malloc.c | 133 |
1 files changed, 87 insertions, 46 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index a353c933ef..30cfd48986 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.211 2016/11/04 09:11:20 otto Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.212 2017/01/21 07:47:42 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> |
@@ -73,6 +73,12 @@ | |||
73 | * Set to zero to be the most strict. | 73 | * Set to zero to be the most strict. |
74 | */ | 74 | */ |
75 | #define MALLOC_LEEWAY 0 | 75 | #define MALLOC_LEEWAY 0 |
76 | #define MALLOC_MOVE_COND(sz) ((sz) - mopts.malloc_guard < \ | ||
77 | MALLOC_PAGESIZE - MALLOC_LEEWAY) | ||
78 | #define MALLOC_MOVE(p, sz) (((char *)(p)) + \ | ||
79 | ((MALLOC_PAGESIZE - MALLOC_LEEWAY - \ | ||
80 | ((sz) - mopts.malloc_guard)) & \ | ||
81 | ~(MALLOC_MINSIZE - 1))) | ||
76 | 82 | ||
77 | #define PAGEROUND(x) (((x) + (MALLOC_PAGEMASK)) & ~MALLOC_PAGEMASK) | 83 | #define PAGEROUND(x) (((x) + (MALLOC_PAGEMASK)) & ~MALLOC_PAGEMASK) |
78 | 84 | ||
@@ -199,6 +205,7 @@ char *malloc_options; /* compile-time options */ | |||
199 | static u_char getrbyte(struct dir_info *d); | 205 | static u_char getrbyte(struct dir_info *d); |
200 | static __dead void wrterror(struct dir_info *d, char *msg, ...) | 206 | static __dead void wrterror(struct dir_info *d, char *msg, ...) |
201 | __attribute__((__format__ (printf, 2, 3))); | 207 | __attribute__((__format__ (printf, 2, 3))); |
208 | static void fill_canary(char *ptr, size_t sz, size_t allocated); | ||
202 | 209 | ||
203 | #ifdef MALLOC_STATS | 210 | #ifdef MALLOC_STATS |
204 | void malloc_dump(int, int, struct dir_info *); | 211 | void malloc_dump(int, int, struct dir_info *); |
@@ -209,8 +216,8 @@ static void malloc_exit(void); | |||
209 | #define CALLER NULL | 216 | #define CALLER NULL |
210 | #endif | 217 | #endif |
211 | 218 | ||
212 | /* low bits of r->p determine size: 0 means >= page size and p->size holding | 219 | /* low bits of r->p determine size: 0 means >= page size and r->size holding |
213 | * real size, otherwise r->size is a shift count, or 1 for malloc(0) | 220 | * real size, otherwise low bits are a shift count, or 1 for malloc(0) |
214 | */ | 221 | */ |
215 | #define REALSIZE(sz, r) \ | 222 | #define REALSIZE(sz, r) \ |
216 | (sz) = (uintptr_t)(r)->p & MALLOC_PAGEMASK, \ | 223 | (sz) = (uintptr_t)(r)->p & MALLOC_PAGEMASK, \ |
@@ -905,23 +912,10 @@ omalloc_make_chunks(struct dir_info *d, int bits, int listnum) | |||
905 | return bp; | 912 | return bp; |
906 | } | 913 | } |
907 | 914 | ||
908 | 915 | static int | |
909 | /* | 916 | find_chunksize(size_t size) |
910 | * Allocate a chunk | ||
911 | */ | ||
912 | static void * | ||
913 | malloc_bytes(struct dir_info *d, size_t argsize, void *f) | ||
914 | { | 917 | { |
915 | int i, j, listnum; | 918 | int i, j; |
916 | size_t k, size; | ||
917 | u_short u, *lp; | ||
918 | struct chunk_info *bp; | ||
919 | |||
920 | if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || | ||
921 | d->canary1 != ~d->canary2) | ||
922 | wrterror(d, "internal struct corrupt"); | ||
923 | |||
924 | size = argsize; | ||
925 | 919 | ||
926 | /* Don't bother with anything less than this */ | 920 | /* Don't bother with anything less than this */ |
927 | /* unless we have a malloc(0) requests */ | 921 | /* unless we have a malloc(0) requests */ |
@@ -937,6 +931,25 @@ malloc_bytes(struct dir_info *d, size_t argsize, void *f) | |||
937 | while (i >>= 1) | 931 | while (i >>= 1) |
938 | j++; | 932 | j++; |
939 | } | 933 | } |
934 | return j; | ||
935 | } | ||
936 | |||
937 | /* | ||
938 | * Allocate a chunk | ||
939 | */ | ||
940 | static void * | ||
941 | malloc_bytes(struct dir_info *d, size_t size, void *f) | ||
942 | { | ||
943 | int i, j, listnum; | ||
944 | size_t k; | ||
945 | u_short u, *lp; | ||
946 | struct chunk_info *bp; | ||
947 | |||
948 | if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || | ||
949 | d->canary1 != ~d->canary2) | ||
950 | wrterror(d, "internal struct corrupt"); | ||
951 | |||
952 | j = find_chunksize(size); | ||
940 | 953 | ||
941 | listnum = getrbyte(d) % MALLOC_CHUNK_LISTS; | 954 | listnum = getrbyte(d) % MALLOC_CHUNK_LISTS; |
942 | /* If it's empty, make a page more of that size chunks */ | 955 | /* If it's empty, make a page more of that size chunks */ |
@@ -990,25 +1003,30 @@ malloc_bytes(struct dir_info *d, size_t argsize, void *f) | |||
990 | k += (lp - bp->bits) * MALLOC_BITS; | 1003 | k += (lp - bp->bits) * MALLOC_BITS; |
991 | 1004 | ||
992 | if (mopts.chunk_canaries) | 1005 | if (mopts.chunk_canaries) |
993 | bp->bits[bp->offset + k] = argsize; | 1006 | bp->bits[bp->offset + k] = size; |
994 | 1007 | ||
995 | k <<= bp->shift; | 1008 | k <<= bp->shift; |
996 | 1009 | ||
997 | if (bp->size > 0) { | 1010 | if (bp->size > 0) { |
998 | if (mopts.malloc_junk == 2) | 1011 | if (mopts.malloc_junk == 2) |
999 | memset((char *)bp->page + k, SOME_JUNK, bp->size); | 1012 | memset((char *)bp->page + k, SOME_JUNK, bp->size); |
1000 | else if (mopts.chunk_canaries) { | 1013 | else if (mopts.chunk_canaries) |
1001 | size_t sz = bp->size - argsize; | 1014 | fill_canary((char *)bp->page + k, size, bp->size); |
1002 | |||
1003 | if (sz > CHUNK_CHECK_LENGTH) | ||
1004 | sz = CHUNK_CHECK_LENGTH; | ||
1005 | memset((char *)bp->page + k + argsize, SOME_JUNK, sz); | ||
1006 | } | ||
1007 | } | 1015 | } |
1008 | return ((char *)bp->page + k); | 1016 | return ((char *)bp->page + k); |
1009 | } | 1017 | } |
1010 | 1018 | ||
1011 | static void | 1019 | static void |
1020 | fill_canary(char *ptr, size_t sz, size_t allocated) | ||
1021 | { | ||
1022 | size_t check_sz = allocated - sz; | ||
1023 | |||
1024 | if (check_sz > CHUNK_CHECK_LENGTH) | ||
1025 | check_sz = CHUNK_CHECK_LENGTH; | ||
1026 | memset(ptr + sz, SOME_JUNK, check_sz); | ||
1027 | } | ||
1028 | |||
1029 | static void | ||
1012 | validate_canary(struct dir_info *d, u_char *ptr, size_t sz, size_t allocated) | 1030 | validate_canary(struct dir_info *d, u_char *ptr, size_t sz, size_t allocated) |
1013 | { | 1031 | { |
1014 | size_t check_sz = allocated - sz; | 1032 | size_t check_sz = allocated - sz; |
@@ -1130,13 +1148,12 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) | |||
1130 | STATS_ADD(pool->malloc_guarded, mopts.malloc_guard); | 1148 | STATS_ADD(pool->malloc_guarded, mopts.malloc_guard); |
1131 | } | 1149 | } |
1132 | 1150 | ||
1133 | if (sz - mopts.malloc_guard < MALLOC_PAGESIZE - MALLOC_LEEWAY) { | 1151 | if (MALLOC_MOVE_COND(sz)) { |
1134 | /* fill whole allocation */ | 1152 | /* fill whole allocation */ |
1135 | if (mopts.malloc_junk == 2) | 1153 | if (mopts.malloc_junk == 2) |
1136 | memset(p, SOME_JUNK, psz - mopts.malloc_guard); | 1154 | memset(p, SOME_JUNK, psz - mopts.malloc_guard); |
1137 | /* shift towards the end */ | 1155 | /* shift towards the end */ |
1138 | p = ((char *)p) + ((MALLOC_PAGESIZE - MALLOC_LEEWAY - | 1156 | p = MALLOC_MOVE(p, sz); |
1139 | (sz - mopts.malloc_guard)) & ~(MALLOC_MINSIZE-1)); | ||
1140 | /* fill zeros if needed and overwritten above */ | 1157 | /* fill zeros if needed and overwritten above */ |
1141 | if (zero_fill && mopts.malloc_junk == 2) | 1158 | if (zero_fill && mopts.malloc_junk == 2) |
1142 | memset(p, 0, sz - mopts.malloc_guard); | 1159 | memset(p, 0, sz - mopts.malloc_guard); |
@@ -1149,14 +1166,9 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) | |||
1149 | memset(p, SOME_JUNK, | 1166 | memset(p, SOME_JUNK, |
1150 | psz - mopts.malloc_guard); | 1167 | psz - mopts.malloc_guard); |
1151 | } | 1168 | } |
1152 | else if (mopts.chunk_canaries) { | 1169 | else if (mopts.chunk_canaries) |
1153 | size_t csz = psz - sz; | 1170 | fill_canary(p, sz - mopts.malloc_guard, |
1154 | 1171 | psz - mopts.malloc_guard); | |
1155 | if (csz > CHUNK_CHECK_LENGTH) | ||
1156 | csz = CHUNK_CHECK_LENGTH; | ||
1157 | memset((char *)p + sz - mopts.malloc_guard, | ||
1158 | SOME_JUNK, csz); | ||
1159 | } | ||
1160 | } | 1172 | } |
1161 | 1173 | ||
1162 | } else { | 1174 | } else { |
@@ -1308,8 +1320,7 @@ ofree(struct dir_info *argpool, void *p) | |||
1308 | 1320 | ||
1309 | REALSIZE(sz, r); | 1321 | REALSIZE(sz, r); |
1310 | if (sz > MALLOC_MAXCHUNK) { | 1322 | if (sz > MALLOC_MAXCHUNK) { |
1311 | if (sz - mopts.malloc_guard >= MALLOC_PAGESIZE - | 1323 | if (!MALLOC_MOVE_COND(sz)) { |
1312 | MALLOC_LEEWAY) { | ||
1313 | if (r->p != p) | 1324 | if (r->p != p) |
1314 | wrterror(pool, "bogus pointer %p", p); | 1325 | wrterror(pool, "bogus pointer %p", p); |
1315 | if (mopts.chunk_canaries) | 1326 | if (mopts.chunk_canaries) |
@@ -1410,9 +1421,11 @@ orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) | |||
1410 | { | 1421 | { |
1411 | struct dir_info *pool; | 1422 | struct dir_info *pool; |
1412 | struct region_info *r; | 1423 | struct region_info *r; |
1424 | struct chunk_info *info; | ||
1413 | size_t oldsz, goldsz, gnewsz; | 1425 | size_t oldsz, goldsz, gnewsz; |
1414 | void *q, *ret; | 1426 | void *q, *ret; |
1415 | int i; | 1427 | int i; |
1428 | uint32_t chunknum; | ||
1416 | 1429 | ||
1417 | pool = argpool; | 1430 | pool = argpool; |
1418 | 1431 | ||
@@ -1445,6 +1458,11 @@ orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) | |||
1445 | } | 1458 | } |
1446 | 1459 | ||
1447 | REALSIZE(oldsz, r); | 1460 | REALSIZE(oldsz, r); |
1461 | if (mopts.chunk_canaries && oldsz <= MALLOC_MAXCHUNK) { | ||
1462 | chunknum = find_chunknum(pool, r, p, 0); | ||
1463 | info = (struct chunk_info *)r->size; | ||
1464 | } | ||
1465 | |||
1448 | goldsz = oldsz; | 1466 | goldsz = oldsz; |
1449 | if (oldsz > MALLOC_MAXCHUNK) { | 1467 | if (oldsz > MALLOC_MAXCHUNK) { |
1450 | if (oldsz < mopts.malloc_guard) | 1468 | if (oldsz < mopts.malloc_guard) |
@@ -1457,11 +1475,14 @@ orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) | |||
1457 | gnewsz += mopts.malloc_guard; | 1475 | gnewsz += mopts.malloc_guard; |
1458 | 1476 | ||
1459 | if (newsz > MALLOC_MAXCHUNK && oldsz > MALLOC_MAXCHUNK && p == r->p && | 1477 | if (newsz > MALLOC_MAXCHUNK && oldsz > MALLOC_MAXCHUNK && p == r->p && |
1460 | !mopts.chunk_canaries && !mopts.malloc_realloc) { | 1478 | !mopts.malloc_realloc) { |
1479 | /* First case: from n pages sized allocation to m pages sized | ||
1480 | allocation, no malloc_move in effect */ | ||
1461 | size_t roldsz = PAGEROUND(goldsz); | 1481 | size_t roldsz = PAGEROUND(goldsz); |
1462 | size_t rnewsz = PAGEROUND(gnewsz); | 1482 | size_t rnewsz = PAGEROUND(gnewsz); |
1463 | 1483 | ||
1464 | if (rnewsz > roldsz) { | 1484 | if (rnewsz > roldsz) { |
1485 | /* try to extend existing region */ | ||
1465 | if (!mopts.malloc_guard) { | 1486 | if (!mopts.malloc_guard) { |
1466 | void *hint = (char *)p + roldsz; | 1487 | void *hint = (char *)p + roldsz; |
1467 | size_t needed = rnewsz - roldsz; | 1488 | size_t needed = rnewsz - roldsz; |
@@ -1482,6 +1503,8 @@ gotit: | |||
1482 | if (mopts.malloc_junk == 2) | 1503 | if (mopts.malloc_junk == 2) |
1483 | memset(q, SOME_JUNK, needed); | 1504 | memset(q, SOME_JUNK, needed); |
1484 | r->size = newsz; | 1505 | r->size = newsz; |
1506 | if (mopts.chunk_canaries) | ||
1507 | fill_canary(p, newsz, PAGEROUND(newsz)); | ||
1485 | STATS_SETF(r, f); | 1508 | STATS_SETF(r, f); |
1486 | STATS_INC(pool->cheap_reallocs); | 1509 | STATS_INC(pool->cheap_reallocs); |
1487 | ret = p; | 1510 | ret = p; |
@@ -1492,6 +1515,7 @@ gotit: | |||
1492 | } | 1515 | } |
1493 | } | 1516 | } |
1494 | } else if (rnewsz < roldsz) { | 1517 | } else if (rnewsz < roldsz) { |
1518 | /* shrink number of pages */ | ||
1495 | if (mopts.malloc_guard) { | 1519 | if (mopts.malloc_guard) { |
1496 | if (mprotect((char *)p + roldsz - | 1520 | if (mprotect((char *)p + roldsz - |
1497 | mopts.malloc_guard, mopts.malloc_guard, | 1521 | mopts.malloc_guard, mopts.malloc_guard, |
@@ -1504,27 +1528,38 @@ gotit: | |||
1504 | } | 1528 | } |
1505 | unmap(pool, (char *)p + rnewsz, roldsz - rnewsz); | 1529 | unmap(pool, (char *)p + rnewsz, roldsz - rnewsz); |
1506 | r->size = gnewsz; | 1530 | r->size = gnewsz; |
1531 | if (mopts.chunk_canaries) | ||
1532 | fill_canary(p, newsz, PAGEROUND(newsz)); | ||
1507 | STATS_SETF(r, f); | 1533 | STATS_SETF(r, f); |
1508 | ret = p; | 1534 | ret = p; |
1509 | goto done; | 1535 | goto done; |
1510 | } else { | 1536 | } else { |
1537 | /* number of pages remains the same */ | ||
1511 | if (newsz > oldsz && mopts.malloc_junk == 2) | 1538 | if (newsz > oldsz && mopts.malloc_junk == 2) |
1512 | memset((char *)p + newsz, SOME_JUNK, | 1539 | memset((char *)p + newsz, SOME_JUNK, |
1513 | rnewsz - mopts.malloc_guard - newsz); | 1540 | rnewsz - mopts.malloc_guard - newsz); |
1514 | r->size = gnewsz; | 1541 | r->size = gnewsz; |
1542 | if (mopts.chunk_canaries) | ||
1543 | fill_canary(p, newsz, PAGEROUND(newsz)); | ||
1515 | STATS_SETF(r, f); | 1544 | STATS_SETF(r, f); |
1516 | ret = p; | 1545 | ret = p; |
1517 | goto done; | 1546 | goto done; |
1518 | } | 1547 | } |
1519 | } | 1548 | } |
1520 | if (newsz <= oldsz && newsz > oldsz / 2 && !mopts.chunk_canaries && | 1549 | if (oldsz <= MALLOC_MAXCHUNK && oldsz > 0 && |
1521 | !mopts.malloc_realloc) { | 1550 | newsz <= MALLOC_MAXCHUNK && newsz > 0 && |
1522 | if (mopts.malloc_junk == 2 && newsz > 0) | 1551 | 1 << find_chunksize(newsz) == oldsz && !mopts.malloc_realloc) { |
1552 | /* do not reallocate if new size fits good in existing chunk */ | ||
1553 | if (mopts.malloc_junk == 2) | ||
1523 | memset((char *)p + newsz, SOME_JUNK, oldsz - newsz); | 1554 | memset((char *)p + newsz, SOME_JUNK, oldsz - newsz); |
1555 | if (mopts.chunk_canaries) { | ||
1556 | info->bits[info->offset + chunknum] = newsz; | ||
1557 | fill_canary(p, newsz, info->size); | ||
1558 | } | ||
1524 | STATS_SETF(r, f); | 1559 | STATS_SETF(r, f); |
1525 | ret = p; | 1560 | ret = p; |
1526 | } else if (newsz != oldsz || mopts.chunk_canaries || | 1561 | } else if (newsz != oldsz || mopts.malloc_realloc) { |
1527 | mopts.malloc_realloc) { | 1562 | /* create new allocation */ |
1528 | q = omalloc(pool, newsz, 0, f); | 1563 | q = omalloc(pool, newsz, 0, f); |
1529 | if (q == NULL) { | 1564 | if (q == NULL) { |
1530 | ret = NULL; | 1565 | ret = NULL; |
@@ -1535,6 +1570,12 @@ gotit: | |||
1535 | ofree(pool, p); | 1570 | ofree(pool, p); |
1536 | ret = q; | 1571 | ret = q; |
1537 | } else { | 1572 | } else { |
1573 | /* > page size allocation didnt change */ | ||
1574 | if (mopts.chunk_canaries && oldsz <= MALLOC_MAXCHUNK) { | ||
1575 | info->bits[info->offset + chunknum] = newsz; | ||
1576 | if (info->size > 0) | ||
1577 | fill_canary(p, newsz, info->size); | ||
1578 | } | ||
1538 | STATS_SETF(r, f); | 1579 | STATS_SETF(r, f); |
1539 | ret = p; | 1580 | ret = p; |
1540 | } | 1581 | } |