diff options
author | otto <> | 2017-04-10 05:45:02 +0000 |
---|---|---|
committer | otto <> | 2017-04-10 05:45:02 +0000 |
commit | 579a4b73945db7a24a27c2678668b0db5b9f1807 (patch) | |
tree | 36a6be76ee8b2ccb1e7af2e9cf969e1f91dd6524 /src | |
parent | 490c04dce89599fb1ab02ea53b512fb7104eccc2 (diff) | |
download | openbsd-579a4b73945db7a24a27c2678668b0db5b9f1807.tar.gz openbsd-579a4b73945db7a24a27c2678668b0db5b9f1807.tar.bz2 openbsd-579a4b73945db7a24a27c2678668b0db5b9f1807.zip |
Introducing freezero(3) a version of free that guarantees the process
no longer has access to the content of a memmory object. It does
this by either clearing (if the object memory remains cached) or
by calling munmap(2). ok millert@, deraadt@, guenther@
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libc/stdlib/malloc.3 | 82 | ||||
-rw-r--r-- | src/lib/libc/stdlib/malloc.c | 84 |
2 files changed, 130 insertions, 36 deletions
diff --git a/src/lib/libc/stdlib/malloc.3 b/src/lib/libc/stdlib/malloc.3 index c65c08ef98..c7a79b5e3d 100644 --- a/src/lib/libc/stdlib/malloc.3 +++ b/src/lib/libc/stdlib/malloc.3 | |||
@@ -30,18 +30,19 @@ | |||
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.109 2017/04/06 17:00:52 otto Exp $ | 33 | .\" $OpenBSD: malloc.3,v 1.110 2017/04/10 05:45:02 otto Exp $ |
34 | .\" | 34 | .\" |
35 | .Dd $Mdocdate: April 6 2017 $ | 35 | .Dd $Mdocdate: April 10 2017 $ |
36 | .Dt MALLOC 3 | 36 | .Dt MALLOC 3 |
37 | .Os | 37 | .Os |
38 | .Sh NAME | 38 | .Sh NAME |
39 | .Nm malloc , | 39 | .Nm malloc , |
40 | .Nm calloc , | 40 | .Nm calloc , |
41 | .Nm reallocarray , | ||
42 | .Nm recallocarray , | ||
43 | .Nm realloc , | 41 | .Nm realloc , |
44 | .Nm free | 42 | .Nm free |
43 | .Nm reallocarray , | ||
44 | .Nm recallocarray , | ||
45 | .Nm freezero , | ||
45 | .Nd memory allocation and deallocation | 46 | .Nd memory allocation and deallocation |
46 | .Sh SYNOPSIS | 47 | .Sh SYNOPSIS |
47 | .In stdlib.h | 48 | .In stdlib.h |
@@ -50,15 +51,23 @@ | |||
50 | .Ft void * | 51 | .Ft void * |
51 | .Fn calloc "size_t nmemb" "size_t size" | 52 | .Fn calloc "size_t nmemb" "size_t size" |
52 | .Ft void * | 53 | .Ft void * |
54 | .Fn realloc "void *ptr" "size_t size" | ||
55 | .Ft void | ||
56 | .Fn free "void *ptr" | ||
57 | .Ft void * | ||
53 | .Fn reallocarray "void *ptr" "size_t nmemb" "size_t size" | 58 | .Fn reallocarray "void *ptr" "size_t nmemb" "size_t size" |
54 | .Ft void * | 59 | .Ft void * |
55 | .Fn recallocarray "void *ptr" "size_t oldnmemb" "size_t nmemb" "size_t size" | 60 | .Fn recallocarray "void *ptr" "size_t oldnmemb" "size_t nmemb" "size_t size" |
56 | .Ft void * | ||
57 | .Fn realloc "void *ptr" "size_t size" | ||
58 | .Ft void | 61 | .Ft void |
59 | .Fn free "void *ptr" | 62 | .Fn freezero "void *ptr" "size_t size" |
60 | .Vt char *malloc_options ; | 63 | .Vt char *malloc_options ; |
61 | .Sh DESCRIPTION | 64 | .Sh DESCRIPTION |
65 | The standard functions | ||
66 | .Fn malloc , | ||
67 | .Fn calloc , | ||
68 | and | ||
69 | .Fn realloc | ||
70 | allocate memory space. | ||
62 | The | 71 | The |
63 | .Fn malloc | 72 | .Fn malloc |
64 | function allocates uninitialized space for an object of | 73 | function allocates uninitialized space for an object of |
@@ -103,6 +112,26 @@ behaves like | |||
103 | and allocates a new object. | 112 | and allocates a new object. |
104 | .Pp | 113 | .Pp |
105 | The | 114 | The |
115 | .Fn free | ||
116 | function causes the space pointed to by | ||
117 | .Fa ptr | ||
118 | to be either placed on a list of free blocks to make it available for future | ||
119 | allocation or, when appropiate, to be returned to the kernel using | ||
120 | .Xr munmap 2 . | ||
121 | If | ||
122 | .Fa ptr | ||
123 | is a | ||
124 | .Dv NULL | ||
125 | pointer, no action occurs. | ||
126 | If | ||
127 | .Fa ptr | ||
128 | was previously freed by | ||
129 | .Fn free | ||
130 | or a reallocation function, | ||
131 | the behavior is undefined and the double free is a security concern. | ||
132 | .Pp | ||
133 | Designed for safe allocation of arrays, | ||
134 | the | ||
106 | .Fn reallocarray | 135 | .Fn reallocarray |
107 | function is similar to | 136 | function is similar to |
108 | .Fn realloc | 137 | .Fn realloc |
@@ -115,7 +144,8 @@ and checks for integer overflow in the calculation | |||
115 | * | 144 | * |
116 | .Fa size . | 145 | .Fa size . |
117 | .Pp | 146 | .Pp |
118 | The | 147 | Used for the allocation of memory holding sensitive data, |
148 | the | ||
119 | .Fn recallocarray | 149 | .Fn recallocarray |
120 | function is similar to | 150 | function is similar to |
121 | .Fn reallocarray | 151 | .Fn reallocarray |
@@ -150,23 +180,25 @@ is the size of the earlier allocation that returned | |||
150 | otherwise the behaviour is undefined. | 180 | otherwise the behaviour is undefined. |
151 | .Pp | 181 | .Pp |
152 | The | 182 | The |
183 | .Fn freezero | ||
184 | function is similar to the | ||
153 | .Fn free | 185 | .Fn free |
154 | function causes the space pointed to by | 186 | function except it ensures the memory being deallocated is explicitly |
155 | .Fa ptr | 187 | discarded. |
156 | to be either placed on a list of free pages to make it available for future | ||
157 | allocation or, if required, to be returned to the kernel using | ||
158 | .Xr munmap 2 . | ||
159 | If | 188 | If |
160 | .Fa ptr | 189 | .Fa ptr |
161 | is a | 190 | is |
162 | .Dv NULL | 191 | .Dv NULL , |
163 | pointer, no action occurs. | 192 | no action occurs. |
164 | If | 193 | If |
165 | .Fa ptr | 194 | .Fa ptr |
166 | was previously freed by | 195 | is not |
167 | .Fn free | 196 | .Dv NULL , |
168 | or a reallocation function, | 197 | the |
169 | the behavior is undefined and the double free is a security concern. | 198 | .Fa size |
199 | argument must be the size of the earlier allocation that returned | ||
200 | .Fa ptr , | ||
201 | otherwise the behaviour is undefined. | ||
170 | .Sh RETURN VALUES | 202 | .Sh RETURN VALUES |
171 | Upon successful completion, the allocation functions | 203 | Upon successful completion, the allocation functions |
172 | return a pointer to the allocated space; otherwise, a | 204 | return a pointer to the allocated space; otherwise, a |
@@ -319,10 +351,8 @@ function should be used for resizing objects containing sensitive data like | |||
319 | keys. | 351 | keys. |
320 | To avoid leaking information, | 352 | To avoid leaking information, |
321 | it guarantees memory is cleared before placing it on the internal free list. | 353 | it guarantees memory is cleared before placing it on the internal free list. |
322 | A | 354 | Deallocation of such an object should be done by calling |
323 | .Fn free | 355 | .Fn freezero . |
324 | call for such an object should still be preceded by a call to | ||
325 | .Xr explicit_bzero 3 . | ||
326 | .Sh ENVIRONMENT | 356 | .Sh ENVIRONMENT |
327 | .Bl -tag -width "/etc/malloc.conf" | 357 | .Bl -tag -width "/etc/malloc.conf" |
328 | .It Ev MALLOC_OPTIONS | 358 | .It Ev MALLOC_OPTIONS |
@@ -539,6 +569,10 @@ The | |||
539 | .Fn recallocarray | 569 | .Fn recallocarray |
540 | function appeared in | 570 | function appeared in |
541 | .Ox 6.1 . | 571 | .Ox 6.1 . |
572 | The | ||
573 | .Fn freezero | ||
574 | function appeared in | ||
575 | .Ox 6.2 . | ||
542 | .Sh CAVEATS | 576 | .Sh CAVEATS |
543 | When using | 577 | When using |
544 | .Fn malloc , | 578 | .Fn malloc , |
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index f2b8b1549b..07c73ca774 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.219 2017/04/06 08:39:47 otto Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.220 2017/04/10 05:45:02 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> |
@@ -185,7 +185,7 @@ struct malloc_readonly { | |||
185 | int malloc_realloc; /* always realloc? */ | 185 | int malloc_realloc; /* always realloc? */ |
186 | int malloc_xmalloc; /* xmalloc behaviour? */ | 186 | int malloc_xmalloc; /* xmalloc behaviour? */ |
187 | int chunk_canaries; /* use canaries after chunks? */ | 187 | int chunk_canaries; /* use canaries after chunks? */ |
188 | int internal_recallocarray; /* use better recallocarray? */ | 188 | int internal_funcs; /* use better recallocarray/freezero? */ |
189 | u_int malloc_cache; /* free pages we cache */ | 189 | u_int malloc_cache; /* free pages we cache */ |
190 | size_t malloc_guard; /* use guard pages after allocations? */ | 190 | size_t malloc_guard; /* use guard pages after allocations? */ |
191 | #ifdef MALLOC_STATS | 191 | #ifdef MALLOC_STATS |
@@ -343,7 +343,14 @@ unmap(struct dir_info *d, void *p, size_t sz, int clear) | |||
343 | if (sz != PAGEROUND(sz)) | 343 | if (sz != PAGEROUND(sz)) |
344 | wrterror(d, "munmap round"); | 344 | wrterror(d, "munmap round"); |
345 | 345 | ||
346 | if (psz > mopts.malloc_cache) { | 346 | rsz = mopts.malloc_cache - d->free_regions_size; |
347 | |||
348 | /* | ||
349 | * normally the cache holds recently freed regions, but if the region | ||
350 | * to unmap is larger than the cache size or we're clearing and the | ||
351 | * cache is full, just munmap | ||
352 | */ | ||
353 | if (psz > mopts.malloc_cache || (clear && rsz == 0)) { | ||
347 | i = munmap(p, sz); | 354 | i = munmap(p, sz); |
348 | if (i) | 355 | if (i) |
349 | wrterror(d, "munmap %p", p); | 356 | wrterror(d, "munmap %p", p); |
@@ -351,7 +358,6 @@ unmap(struct dir_info *d, void *p, size_t sz, int clear) | |||
351 | return; | 358 | return; |
352 | } | 359 | } |
353 | tounmap = 0; | 360 | tounmap = 0; |
354 | rsz = mopts.malloc_cache - d->free_regions_size; | ||
355 | if (psz > rsz) | 361 | if (psz > rsz) |
356 | tounmap = psz - rsz; | 362 | tounmap = psz - rsz; |
357 | offset = getrbyte(d); | 363 | offset = getrbyte(d); |
@@ -1234,7 +1240,7 @@ _malloc_init(int from_rthreads) | |||
1234 | if (from_rthreads) | 1240 | if (from_rthreads) |
1235 | mopts.malloc_mt = 1; | 1241 | mopts.malloc_mt = 1; |
1236 | else | 1242 | else |
1237 | mopts.internal_recallocarray = 1; | 1243 | mopts.internal_funcs = 1; |
1238 | 1244 | ||
1239 | /* | 1245 | /* |
1240 | * Options have been set and will never be reset. | 1246 | * Options have been set and will never be reset. |
@@ -1297,7 +1303,7 @@ validate_junk(struct dir_info *pool, void *p) | |||
1297 | } | 1303 | } |
1298 | 1304 | ||
1299 | static void | 1305 | static void |
1300 | ofree(struct dir_info *argpool, void *p, int clear) | 1306 | ofree(struct dir_info *argpool, void *p, int clear, int check, size_t argsz) |
1301 | { | 1307 | { |
1302 | struct dir_info *pool; | 1308 | struct dir_info *pool; |
1303 | struct region_info *r; | 1309 | struct region_info *r; |
@@ -1326,6 +1332,25 @@ ofree(struct dir_info *argpool, void *p, int clear) | |||
1326 | } | 1332 | } |
1327 | 1333 | ||
1328 | REALSIZE(sz, r); | 1334 | REALSIZE(sz, r); |
1335 | if (check) { | ||
1336 | if (sz <= MALLOC_MAXCHUNK) { | ||
1337 | if (mopts.chunk_canaries) { | ||
1338 | struct chunk_info *info = | ||
1339 | (struct chunk_info *)r->size; | ||
1340 | uint32_t chunknum = | ||
1341 | find_chunknum(pool, r, p, 0); | ||
1342 | |||
1343 | if (info->bits[info->offset + chunknum] != | ||
1344 | argsz) | ||
1345 | wrterror(pool, "recorded old size %hu" | ||
1346 | " != %zu", | ||
1347 | info->bits[info->offset + chunknum], | ||
1348 | argsz); | ||
1349 | } | ||
1350 | } else if (argsz != sz - mopts.malloc_guard) | ||
1351 | wrterror(pool, "recorded old size %zu != %zu", | ||
1352 | sz - mopts.malloc_guard, argsz); | ||
1353 | } | ||
1329 | if (sz > MALLOC_MAXCHUNK) { | 1354 | if (sz > MALLOC_MAXCHUNK) { |
1330 | if (!MALLOC_MOVE_COND(sz)) { | 1355 | if (!MALLOC_MOVE_COND(sz)) { |
1331 | if (r->p != p) | 1356 | if (r->p != p) |
@@ -1411,13 +1436,48 @@ free(void *ptr) | |||
1411 | malloc_recurse(d); | 1436 | malloc_recurse(d); |
1412 | return; | 1437 | return; |
1413 | } | 1438 | } |
1414 | ofree(d, ptr, 0); | 1439 | ofree(d, ptr, 0, 0, 0); |
1415 | d->active--; | 1440 | d->active--; |
1416 | _MALLOC_UNLOCK(d->mutex); | 1441 | _MALLOC_UNLOCK(d->mutex); |
1417 | errno = saved_errno; | 1442 | errno = saved_errno; |
1418 | } | 1443 | } |
1419 | /*DEF_STRONG(free);*/ | 1444 | /*DEF_STRONG(free);*/ |
1420 | 1445 | ||
1446 | static void | ||
1447 | freezero_p(void *ptr, size_t sz) | ||
1448 | { | ||
1449 | explicit_bzero(ptr, sz); | ||
1450 | free(ptr); | ||
1451 | } | ||
1452 | |||
1453 | void | ||
1454 | freezero(void *ptr, size_t sz) | ||
1455 | { | ||
1456 | struct dir_info *d; | ||
1457 | int saved_errno = errno; | ||
1458 | |||
1459 | /* This is legal. */ | ||
1460 | if (ptr == NULL) | ||
1461 | return; | ||
1462 | |||
1463 | if (!mopts.internal_funcs) | ||
1464 | return freezero_p(ptr, sz); | ||
1465 | |||
1466 | d = getpool(); | ||
1467 | if (d == NULL) | ||
1468 | wrterror(d, "freezero() called before allocation"); | ||
1469 | _MALLOC_LOCK(d->mutex); | ||
1470 | d->func = "freezero"; | ||
1471 | if (d->active++) { | ||
1472 | malloc_recurse(d); | ||
1473 | return; | ||
1474 | } | ||
1475 | ofree(d, ptr, 1, 1, sz); | ||
1476 | d->active--; | ||
1477 | _MALLOC_UNLOCK(d->mutex); | ||
1478 | errno = saved_errno; | ||
1479 | } | ||
1480 | DEF_WEAK(freezero); | ||
1421 | 1481 | ||
1422 | static void * | 1482 | static void * |
1423 | orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) | 1483 | orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) |
@@ -1591,7 +1651,7 @@ gotit: | |||
1591 | } | 1651 | } |
1592 | if (newsz != 0 && oldsz != 0) | 1652 | if (newsz != 0 && oldsz != 0) |
1593 | memcpy(q, p, oldsz < newsz ? oldsz : newsz); | 1653 | memcpy(q, p, oldsz < newsz ? oldsz : newsz); |
1594 | ofree(pool, p, 0); | 1654 | ofree(pool, p, 0, 0, 0); |
1595 | ret = q; | 1655 | ret = q; |
1596 | } else { | 1656 | } else { |
1597 | /* oldsz == newsz */ | 1657 | /* oldsz == newsz */ |
@@ -1751,7 +1811,7 @@ orecallocarray(struct dir_info *argpool, void *p, size_t oldsize, | |||
1751 | } else | 1811 | } else |
1752 | memcpy(newptr, p, newsize); | 1812 | memcpy(newptr, p, newsize); |
1753 | 1813 | ||
1754 | ofree(pool, p, 1); | 1814 | ofree(pool, p, 1, 0, 0); |
1755 | 1815 | ||
1756 | done: | 1816 | done: |
1757 | if (argpool != pool) { | 1817 | if (argpool != pool) { |
@@ -1824,7 +1884,7 @@ recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) | |||
1824 | void *r; | 1884 | void *r; |
1825 | int saved_errno = errno; | 1885 | int saved_errno = errno; |
1826 | 1886 | ||
1827 | if (!mopts.internal_recallocarray) | 1887 | if (!mopts.internal_funcs) |
1828 | return recallocarray_p(ptr, oldnmemb, newnmemb, size); | 1888 | return recallocarray_p(ptr, oldnmemb, newnmemb, size); |
1829 | 1889 | ||
1830 | d = getpool(); | 1890 | d = getpool(); |
@@ -2275,8 +2335,8 @@ malloc_exit(void) | |||
2275 | __progname); | 2335 | __progname); |
2276 | write(fd, buf, strlen(buf)); | 2336 | write(fd, buf, strlen(buf)); |
2277 | snprintf(buf, sizeof(buf), | 2337 | snprintf(buf, sizeof(buf), |
2278 | "MT=%d IRC=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u G=%zu\n", | 2338 | "MT=%d I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u G=%zu\n", |
2279 | mopts.malloc_mt, mopts.internal_recallocarray, | 2339 | mopts.malloc_mt, mopts.internal_funcs, |
2280 | mopts.malloc_freenow, | 2340 | mopts.malloc_freenow, |
2281 | mopts.malloc_freeunmap, mopts.malloc_junk, | 2341 | mopts.malloc_freeunmap, mopts.malloc_junk, |
2282 | mopts.malloc_realloc, mopts.malloc_xmalloc, | 2342 | mopts.malloc_realloc, mopts.malloc_xmalloc, |