diff options
author | djm <> | 2012-11-02 18:18:15 +0000 |
---|---|---|
committer | djm <> | 2012-11-02 18:18:15 +0000 |
commit | cc27290cd0ac9c8a750e38719b5dff3cb0219726 (patch) | |
tree | 5ac95b51be61afab38ef830fc97336aacc9973c7 | |
parent | e05982c56ef9dbde368df8fe35a8458f34b76f76 (diff) | |
download | openbsd-cc27290cd0ac9c8a750e38719b5dff3cb0219726.tar.gz openbsd-cc27290cd0ac9c8a750e38719b5dff3cb0219726.tar.bz2 openbsd-cc27290cd0ac9c8a750e38719b5dff3cb0219726.zip |
Add a new malloc option 'U' => "Free unmap" that does the guarding/
unmapping of freed allocations without disabling chunk randomisation
like the "Freeguard" ('F') option does. Make security 'S' option
use 'U' and not 'F'.
Rationale: guarding with no chunk randomisation is great for debugging
use-after-free, but chunk randomisation offers better defence against
"heap feng shui" style attacks that depend on carefully constructing a
particular heap layout so we should leave this enabled when requesting
security options.
-rw-r--r-- | src/lib/libc/stdlib/malloc.3 | 15 | ||||
-rw-r--r-- | src/lib/libc/stdlib/malloc.c | 39 |
2 files changed, 36 insertions, 18 deletions
diff --git a/src/lib/libc/stdlib/malloc.3 b/src/lib/libc/stdlib/malloc.3 index 6a012fd23d..74df922f4b 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.70 2011/07/22 07:00:44 otto Exp $ | 33 | .\" $OpenBSD: malloc.3,v 1.71 2012/11/02 18:18:15 djm Exp $ |
34 | .\" | 34 | .\" |
35 | .Dd $Mdocdate: July 22 2011 $ | 35 | .Dd $Mdocdate: November 2 2012 $ |
36 | .Dt MALLOC 3 | 36 | .Dt MALLOC 3 |
37 | .Os | 37 | .Os |
38 | .Sh NAME | 38 | .Sh NAME |
@@ -231,13 +231,17 @@ This option requires the library to have been compiled with -DMALLOC_STATS in | |||
231 | order to have any effect. | 231 | order to have any effect. |
232 | .It Cm F | 232 | .It Cm F |
233 | .Dq Freeguard . | 233 | .Dq Freeguard . |
234 | Enable use after free protection. | 234 | Enable use after free detection. |
235 | Unused pages on the freelist are read and write protected to | 235 | Unused pages on the freelist are read and write protected to |
236 | cause a segmentation fault upon access. | 236 | cause a segmentation fault upon access. |
237 | This will also switch off the delayed freeing of chunks, | 237 | This will also switch off the delayed freeing of chunks, |
238 | reducing random behaviour but detecting double | 238 | reducing random behaviour but detecting double |
239 | .Fn free | 239 | .Fn free |
240 | calls as early as possible. | 240 | calls as early as possible. |
241 | This option is intended for debugging rather than improved security | ||
242 | (use the | ||
243 | .Cm U | ||
244 | option for security). | ||
241 | .It Cm G | 245 | .It Cm G |
242 | .Dq Guard . | 246 | .Dq Guard . |
243 | Enable guard pages. | 247 | Enable guard pages. |
@@ -275,6 +279,11 @@ This can substantially aid in compacting memory. | |||
275 | .\"Consult the source for this one. | 279 | .\"Consult the source for this one. |
276 | .It Cm S | 280 | .It Cm S |
277 | Enable all options suitable for security auditing. | 281 | Enable all options suitable for security auditing. |
282 | .It Cm U | ||
283 | .Dq Free unmap . | ||
284 | Enable use after free protection for larger allocations. | ||
285 | Unused pages on the freelist are read and write protected to | ||
286 | cause a segmentation fault upon access. | ||
278 | .It Cm X | 287 | .It Cm X |
279 | .Dq xmalloc . | 288 | .Dq xmalloc . |
280 | Rather than return failure, | 289 | Rather than return failure, |
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index c69ec8316a..b14c747652 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.147 2012/09/13 10:45:41 pirofti Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.148 2012/11/02 18:18:15 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> | 3 | * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> |
4 | * | 4 | * |
@@ -165,7 +165,8 @@ struct chunk_info { | |||
165 | struct malloc_readonly { | 165 | struct malloc_readonly { |
166 | struct dir_info *g_pool; /* Main bookkeeping information */ | 166 | struct dir_info *g_pool; /* Main bookkeeping information */ |
167 | int malloc_abort; /* abort() on error */ | 167 | int malloc_abort; /* abort() on error */ |
168 | int malloc_freeprot; /* mprotect free pages PROT_NONE? */ | 168 | int malloc_freenow; /* Free quickly - disable chunk rnd */ |
169 | int malloc_freeunmap; /* mprotect free pages PROT_NONE? */ | ||
169 | int malloc_hint; /* call madvice on free pages? */ | 170 | int malloc_hint; /* call madvice on free pages? */ |
170 | int malloc_junk; /* junk fill? */ | 171 | int malloc_junk; /* junk fill? */ |
171 | int malloc_move; /* move allocations to end of page? */ | 172 | int malloc_move; /* move allocations to end of page? */ |
@@ -344,7 +345,7 @@ unmap(struct dir_info *d, void *p, size_t sz) | |||
344 | if (r->p == NULL) { | 345 | if (r->p == NULL) { |
345 | if (mopts.malloc_hint) | 346 | if (mopts.malloc_hint) |
346 | madvise(p, sz, MADV_FREE); | 347 | madvise(p, sz, MADV_FREE); |
347 | if (mopts.malloc_freeprot) | 348 | if (mopts.malloc_freeunmap) |
348 | mprotect(p, sz, PROT_NONE); | 349 | mprotect(p, sz, PROT_NONE); |
349 | r->p = p; | 350 | r->p = p; |
350 | r->size = psz; | 351 | r->size = psz; |
@@ -407,7 +408,7 @@ map(struct dir_info *d, size_t sz, int zero_fill) | |||
407 | if (r->p != NULL) { | 408 | if (r->p != NULL) { |
408 | if (r->size == psz) { | 409 | if (r->size == psz) { |
409 | p = r->p; | 410 | p = r->p; |
410 | if (mopts.malloc_freeprot) | 411 | if (mopts.malloc_freeunmap) |
411 | mprotect(p, sz, PROT_READ | PROT_WRITE); | 412 | mprotect(p, sz, PROT_READ | PROT_WRITE); |
412 | if (mopts.malloc_hint) | 413 | if (mopts.malloc_hint) |
413 | madvise(p, sz, MADV_NORMAL); | 414 | madvise(p, sz, MADV_NORMAL); |
@@ -417,7 +418,7 @@ map(struct dir_info *d, size_t sz, int zero_fill) | |||
417 | if (zero_fill) | 418 | if (zero_fill) |
418 | memset(p, 0, sz); | 419 | memset(p, 0, sz); |
419 | else if (mopts.malloc_junk && | 420 | else if (mopts.malloc_junk && |
420 | mopts.malloc_freeprot) | 421 | mopts.malloc_freeunmap) |
421 | memset(p, SOME_FREEJUNK, sz); | 422 | memset(p, SOME_FREEJUNK, sz); |
422 | return p; | 423 | return p; |
423 | } else if (r->size > psz) | 424 | } else if (r->size > psz) |
@@ -427,7 +428,7 @@ map(struct dir_info *d, size_t sz, int zero_fill) | |||
427 | if (big != NULL) { | 428 | if (big != NULL) { |
428 | r = big; | 429 | r = big; |
429 | p = (char *)r->p + ((r->size - psz) << MALLOC_PAGESHIFT); | 430 | p = (char *)r->p + ((r->size - psz) << MALLOC_PAGESHIFT); |
430 | if (mopts.malloc_freeprot) | 431 | if (mopts.malloc_freeunmap) |
431 | mprotect(p, sz, PROT_READ | PROT_WRITE); | 432 | mprotect(p, sz, PROT_READ | PROT_WRITE); |
432 | if (mopts.malloc_hint) | 433 | if (mopts.malloc_hint) |
433 | madvise(p, sz, MADV_NORMAL); | 434 | madvise(p, sz, MADV_NORMAL); |
@@ -435,7 +436,7 @@ map(struct dir_info *d, size_t sz, int zero_fill) | |||
435 | d->free_regions_size -= psz; | 436 | d->free_regions_size -= psz; |
436 | if (zero_fill) | 437 | if (zero_fill) |
437 | memset(p, 0, sz); | 438 | memset(p, 0, sz); |
438 | else if (mopts.malloc_junk && mopts.malloc_freeprot) | 439 | else if (mopts.malloc_junk && mopts.malloc_freeunmap) |
439 | memset(p, SOME_FREEJUNK, sz); | 440 | memset(p, SOME_FREEJUNK, sz); |
440 | return p; | 441 | return p; |
441 | } | 442 | } |
@@ -515,10 +516,12 @@ omalloc_init(struct dir_info **dp) | |||
515 | break; | 516 | break; |
516 | #endif /* MALLOC_STATS */ | 517 | #endif /* MALLOC_STATS */ |
517 | case 'f': | 518 | case 'f': |
518 | mopts.malloc_freeprot = 0; | 519 | mopts.malloc_freenow = 0; |
520 | mopts.malloc_freeunmap = 0; | ||
519 | break; | 521 | break; |
520 | case 'F': | 522 | case 'F': |
521 | mopts.malloc_freeprot = 1; | 523 | mopts.malloc_freenow = 1; |
524 | mopts.malloc_freeunmap = 1; | ||
522 | break; | 525 | break; |
523 | case 'g': | 526 | case 'g': |
524 | mopts.malloc_guard = 0; | 527 | mopts.malloc_guard = 0; |
@@ -554,15 +557,21 @@ omalloc_init(struct dir_info **dp) | |||
554 | mopts.malloc_realloc = 1; | 557 | mopts.malloc_realloc = 1; |
555 | break; | 558 | break; |
556 | case 's': | 559 | case 's': |
557 | mopts.malloc_freeprot = mopts.malloc_junk = 0; | 560 | mopts.malloc_freeunmap = mopts.malloc_junk = 0; |
558 | mopts.malloc_guard = 0; | 561 | mopts.malloc_guard = 0; |
559 | mopts.malloc_cache = MALLOC_DEFAULT_CACHE; | 562 | mopts.malloc_cache = MALLOC_DEFAULT_CACHE; |
560 | break; | 563 | break; |
561 | case 'S': | 564 | case 'S': |
562 | mopts.malloc_freeprot = mopts.malloc_junk = 1; | 565 | mopts.malloc_freeunmap = mopts.malloc_junk = 1; |
563 | mopts.malloc_guard = MALLOC_PAGESIZE; | 566 | mopts.malloc_guard = MALLOC_PAGESIZE; |
564 | mopts.malloc_cache = 0; | 567 | mopts.malloc_cache = 0; |
565 | break; | 568 | break; |
569 | case 'u': | ||
570 | mopts.malloc_freeunmap = 0; | ||
571 | break; | ||
572 | case 'U': | ||
573 | mopts.malloc_freeunmap = 1; | ||
574 | break; | ||
566 | case 'x': | 575 | case 'x': |
567 | mopts.malloc_xmalloc = 0; | 576 | mopts.malloc_xmalloc = 0; |
568 | break; | 577 | break; |
@@ -1015,7 +1024,7 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr) | |||
1015 | 1024 | ||
1016 | LIST_REMOVE(info, entries); | 1025 | LIST_REMOVE(info, entries); |
1017 | 1026 | ||
1018 | if (info->size == 0 && !mopts.malloc_freeprot) | 1027 | if (info->size == 0 && !mopts.malloc_freeunmap) |
1019 | mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); | 1028 | mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); |
1020 | unmap(d, info->page, MALLOC_PAGESIZE); | 1029 | unmap(d, info->page, MALLOC_PAGESIZE); |
1021 | 1030 | ||
@@ -1184,7 +1193,7 @@ ofree(void *p) | |||
1184 | if (mopts.malloc_guard) { | 1193 | if (mopts.malloc_guard) { |
1185 | if (sz < mopts.malloc_guard) | 1194 | if (sz < mopts.malloc_guard) |
1186 | wrterror("guard size", NULL); | 1195 | wrterror("guard size", NULL); |
1187 | if (!mopts.malloc_freeprot) { | 1196 | if (!mopts.malloc_freeunmap) { |
1188 | if (mprotect((char *)p + PAGEROUND(sz) - | 1197 | if (mprotect((char *)p + PAGEROUND(sz) - |
1189 | mopts.malloc_guard, mopts.malloc_guard, | 1198 | mopts.malloc_guard, mopts.malloc_guard, |
1190 | PROT_READ | PROT_WRITE)) | 1199 | PROT_READ | PROT_WRITE)) |
@@ -1192,7 +1201,7 @@ ofree(void *p) | |||
1192 | } | 1201 | } |
1193 | malloc_guarded -= mopts.malloc_guard; | 1202 | malloc_guarded -= mopts.malloc_guard; |
1194 | } | 1203 | } |
1195 | if (mopts.malloc_junk && !mopts.malloc_freeprot) | 1204 | if (mopts.malloc_junk && !mopts.malloc_freeunmap) |
1196 | memset(p, SOME_FREEJUNK, | 1205 | memset(p, SOME_FREEJUNK, |
1197 | PAGEROUND(sz) - mopts.malloc_guard); | 1206 | PAGEROUND(sz) - mopts.malloc_guard); |
1198 | unmap(g_pool, p, PAGEROUND(sz)); | 1207 | unmap(g_pool, p, PAGEROUND(sz)); |
@@ -1203,7 +1212,7 @@ ofree(void *p) | |||
1203 | 1212 | ||
1204 | if (mopts.malloc_junk && sz > 0) | 1213 | if (mopts.malloc_junk && sz > 0) |
1205 | memset(p, SOME_FREEJUNK, sz); | 1214 | memset(p, SOME_FREEJUNK, sz); |
1206 | if (!mopts.malloc_freeprot) { | 1215 | if (!mopts.malloc_freenow) { |
1207 | i = getrnibble(); | 1216 | i = getrnibble(); |
1208 | tmp = p; | 1217 | tmp = p; |
1209 | p = g_pool->delayed_chunks[i]; | 1218 | p = g_pool->delayed_chunks[i]; |