summaryrefslogtreecommitdiff
path: root/src/lib/libc
diff options
context:
space:
mode:
authorotto <>2017-04-10 05:45:02 +0000
committerotto <>2017-04-10 05:45:02 +0000
commit579a4b73945db7a24a27c2678668b0db5b9f1807 (patch)
tree36a6be76ee8b2ccb1e7af2e9cf969e1f91dd6524 /src/lib/libc
parent490c04dce89599fb1ab02ea53b512fb7104eccc2 (diff)
downloadopenbsd-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/lib/libc')
-rw-r--r--src/lib/libc/stdlib/malloc.382
-rw-r--r--src/lib/libc/stdlib/malloc.c84
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
65The standard functions
66.Fn malloc ,
67.Fn calloc ,
68and
69.Fn realloc
70allocate memory space.
62The 71The
63.Fn malloc 72.Fn malloc
64function allocates uninitialized space for an object of 73function allocates uninitialized space for an object of
@@ -103,6 +112,26 @@ behaves like
103and allocates a new object. 112and allocates a new object.
104.Pp 113.Pp
105The 114The
115.Fn free
116function causes the space pointed to by
117.Fa ptr
118to be either placed on a list of free blocks to make it available for future
119allocation or, when appropiate, to be returned to the kernel using
120.Xr munmap 2 .
121If
122.Fa ptr
123is a
124.Dv NULL
125pointer, no action occurs.
126If
127.Fa ptr
128was previously freed by
129.Fn free
130or a reallocation function,
131the behavior is undefined and the double free is a security concern.
132.Pp
133Designed for safe allocation of arrays,
134the
106.Fn reallocarray 135.Fn reallocarray
107function is similar to 136function 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
118The 147Used for the allocation of memory holding sensitive data,
148the
119.Fn recallocarray 149.Fn recallocarray
120function is similar to 150function is similar to
121.Fn reallocarray 151.Fn reallocarray
@@ -150,23 +180,25 @@ is the size of the earlier allocation that returned
150otherwise the behaviour is undefined. 180otherwise the behaviour is undefined.
151.Pp 181.Pp
152The 182The
183.Fn freezero
184function is similar to the
153.Fn free 185.Fn free
154function causes the space pointed to by 186function except it ensures the memory being deallocated is explicitly
155.Fa ptr 187discarded.
156to be either placed on a list of free pages to make it available for future
157allocation or, if required, to be returned to the kernel using
158.Xr munmap 2 .
159If 188If
160.Fa ptr 189.Fa ptr
161is a 190is
162.Dv NULL 191.Dv NULL ,
163pointer, no action occurs. 192no action occurs.
164If 193If
165.Fa ptr 194.Fa ptr
166was previously freed by 195is not
167.Fn free 196.Dv NULL ,
168or a reallocation function, 197the
169the behavior is undefined and the double free is a security concern. 198.Fa size
199argument must be the size of the earlier allocation that returned
200.Fa ptr ,
201otherwise the behaviour is undefined.
170.Sh RETURN VALUES 202.Sh RETURN VALUES
171Upon successful completion, the allocation functions 203Upon successful completion, the allocation functions
172return a pointer to the allocated space; otherwise, a 204return a pointer to the allocated space; otherwise, a
@@ -319,10 +351,8 @@ function should be used for resizing objects containing sensitive data like
319keys. 351keys.
320To avoid leaking information, 352To avoid leaking information,
321it guarantees memory is cleared before placing it on the internal free list. 353it guarantees memory is cleared before placing it on the internal free list.
322A 354Deallocation of such an object should be done by calling
323.Fn free 355.Fn freezero .
324call 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
540function appeared in 570function appeared in
541.Ox 6.1 . 571.Ox 6.1 .
572The
573.Fn freezero
574function appeared in
575.Ox 6.2 .
542.Sh CAVEATS 576.Sh CAVEATS
543When using 577When 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
1299static void 1305static void
1300ofree(struct dir_info *argpool, void *p, int clear) 1306ofree(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
1446static void
1447freezero_p(void *ptr, size_t sz)
1448{
1449 explicit_bzero(ptr, sz);
1450 free(ptr);
1451}
1452
1453void
1454freezero(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}
1480DEF_WEAK(freezero);
1421 1481
1422static void * 1482static void *
1423orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) 1483orealloc(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
1756done: 1816done:
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,