summaryrefslogtreecommitdiffstats
path: root/mm/slub.c
diff options
context:
space:
mode:
authorThomas Garnier <thgarnie@google.com>2016-07-26 18:21:59 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-26 19:19:19 -0400
commit210e7a43fa905bccafa9bb5966fba1d71f33eb8b (patch)
tree14bed0c36f208114d8fd1e9188e34382d00c1ee3 /mm/slub.c
parent7c00fce98c3e15334a603925b41aa49f76e83227 (diff)
mm: SLUB freelist randomization
Implements freelist randomization for the SLUB allocator. It was previous implemented for the SLAB allocator. Both use the same configuration option (CONFIG_SLAB_FREELIST_RANDOM). The list is randomized during initialization of a new set of pages. The order on different freelist sizes is pre-computed at boot for performance. Each kmem_cache has its own randomized freelist. This security feature reduces the predictability of the kernel SLUB allocator against heap overflows rendering attacks much less stable. For example these attacks exploit the predictability of the heap: - Linux Kernel CAN SLUB overflow (https://goo.gl/oMNWkU) - Exploiting Linux Kernel Heap corruptions (http://goo.gl/EXLn95) Performance results: slab_test impact is between 3% to 4% on average for 100000 attempts without smp. It is a very focused testing, kernbench show the overall impact on the system is way lower. Before: Single thread testing ===================== 1. Kmalloc: Repeatedly allocate then free test 100000 times kmalloc(8) -> 49 cycles kfree -> 77 cycles 100000 times kmalloc(16) -> 51 cycles kfree -> 79 cycles 100000 times kmalloc(32) -> 53 cycles kfree -> 83 cycles 100000 times kmalloc(64) -> 62 cycles kfree -> 90 cycles 100000 times kmalloc(128) -> 81 cycles kfree -> 97 cycles 100000 times kmalloc(256) -> 98 cycles kfree -> 121 cycles 100000 times kmalloc(512) -> 95 cycles kfree -> 122 cycles 100000 times kmalloc(1024) -> 96 cycles kfree -> 126 cycles 100000 times kmalloc(2048) -> 115 cycles kfree -> 140 cycles 100000 times kmalloc(4096) -> 149 cycles kfree -> 171 cycles 2. Kmalloc: alloc/free test 100000 times kmalloc(8)/kfree -> 70 cycles 100000 times kmalloc(16)/kfree -> 70 cycles 100000 times kmalloc(32)/kfree -> 70 cycles 100000 times kmalloc(64)/kfree -> 70 cycles 100000 times kmalloc(128)/kfree -> 70 cycles 100000 times kmalloc(256)/kfree -> 69 cycles 100000 times kmalloc(512)/kfree -> 70 cycles 100000 times kmalloc(1024)/kfree -> 73 cycles 100000 times kmalloc(2048)/kfree -> 72 cycles 100000 times kmalloc(4096)/kfree -> 71 cycles After: Single thread testing ===================== 1. Kmalloc: Repeatedly allocate then free test 100000 times kmalloc(8) -> 57 cycles kfree -> 78 cycles 100000 times kmalloc(16) -> 61 cycles kfree -> 81 cycles 100000 times kmalloc(32) -> 76 cycles kfree -> 93 cycles 100000 times kmalloc(64) -> 83 cycles kfree -> 94 cycles 100000 times kmalloc(128) -> 106 cycles kfree -> 107 cycles 100000 times kmalloc(256) -> 118 cycles kfree -> 117 cycles 100000 times kmalloc(512) -> 114 cycles kfree -> 116 cycles 100000 times kmalloc(1024) -> 115 cycles kfree -> 118 cycles 100000 times kmalloc(2048) -> 147 cycles kfree -> 131 cycles 100000 times kmalloc(4096) -> 214 cycles kfree -> 161 cycles 2. Kmalloc: alloc/free test 100000 times kmalloc(8)/kfree -> 66 cycles 100000 times kmalloc(16)/kfree -> 66 cycles 100000 times kmalloc(32)/kfree -> 66 cycles 100000 times kmalloc(64)/kfree -> 66 cycles 100000 times kmalloc(128)/kfree -> 65 cycles 100000 times kmalloc(256)/kfree -> 67 cycles 100000 times kmalloc(512)/kfree -> 67 cycles 100000 times kmalloc(1024)/kfree -> 64 cycles 100000 times kmalloc(2048)/kfree -> 67 cycles 100000 times kmalloc(4096)/kfree -> 67 cycles Kernbench, before: Average Optimal load -j 12 Run (std deviation): Elapsed Time 101.873 (1.16069) User Time 1045.22 (1.60447) System Time 88.969 (0.559195) Percent CPU 1112.9 (13.8279) Context Switches 189140 (2282.15) Sleeps 99008.6 (768.091) After: Average Optimal load -j 12 Run (std deviation): Elapsed Time 102.47 (0.562732) User Time 1045.3 (1.34263) System Time 88.311 (0.342554) Percent CPU 1105.8 (6.49444) Context Switches 189081 (2355.78) Sleeps 99231.5 (800.358) Link: http://lkml.kernel.org/r/1464295031-26375-3-git-send-email-thgarnie@google.com Signed-off-by: Thomas Garnier <thgarnie@google.com> Reviewed-by: Kees Cook <keescook@chromium.org> Cc: Christoph Lameter <cl@linux.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: David Rientjes <rientjes@google.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/slub.c')
-rw-r--r--mm/slub.c133
1 files changed, 126 insertions, 7 deletions
diff --git a/mm/slub.c b/mm/slub.c
index 825ff4505336..f5b3114b6a97 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1405,6 +1405,109 @@ static inline struct page *alloc_slab_page(struct kmem_cache *s,
1405 return page; 1405 return page;
1406} 1406}
1407 1407
1408#ifdef CONFIG_SLAB_FREELIST_RANDOM
1409/* Pre-initialize the random sequence cache */
1410static int init_cache_random_seq(struct kmem_cache *s)
1411{
1412 int err;
1413 unsigned long i, count = oo_objects(s->oo);
1414
1415 err = cache_random_seq_create(s, count, GFP_KERNEL);
1416 if (err) {
1417 pr_err("SLUB: Unable to initialize free list for %s\n",
1418 s->name);
1419 return err;
1420 }
1421
1422 /* Transform to an offset on the set of pages */
1423 if (s->random_seq) {
1424 for (i = 0; i < count; i++)
1425 s->random_seq[i] *= s->size;
1426 }
1427 return 0;
1428}
1429
1430/* Initialize each random sequence freelist per cache */
1431static void __init init_freelist_randomization(void)
1432{
1433 struct kmem_cache *s;
1434
1435 mutex_lock(&slab_mutex);
1436
1437 list_for_each_entry(s, &slab_caches, list)
1438 init_cache_random_seq(s);
1439
1440 mutex_unlock(&slab_mutex);
1441}
1442
1443/* Get the next entry on the pre-computed freelist randomized */
1444static void *next_freelist_entry(struct kmem_cache *s, struct page *page,
1445 unsigned long *pos, void *start,
1446 unsigned long page_limit,
1447 unsigned long freelist_count)
1448{
1449 unsigned int idx;
1450
1451 /*
1452 * If the target page allocation failed, the number of objects on the
1453 * page might be smaller than the usual size defined by the cache.
1454 */
1455 do {
1456 idx = s->random_seq[*pos];
1457 *pos += 1;
1458 if (*pos >= freelist_count)
1459 *pos = 0;
1460 } while (unlikely(idx >= page_limit));
1461
1462 return (char *)start + idx;
1463}
1464
1465/* Shuffle the single linked freelist based on a random pre-computed sequence */
1466static bool shuffle_freelist(struct kmem_cache *s, struct page *page)
1467{
1468 void *start;
1469 void *cur;
1470 void *next;
1471 unsigned long idx, pos, page_limit, freelist_count;
1472
1473 if (page->objects < 2 || !s->random_seq)
1474 return false;
1475
1476 freelist_count = oo_objects(s->oo);
1477 pos = get_random_int() % freelist_count;
1478
1479 page_limit = page->objects * s->size;
1480 start = fixup_red_left(s, page_address(page));
1481
1482 /* First entry is used as the base of the freelist */
1483 cur = next_freelist_entry(s, page, &pos, start, page_limit,
1484 freelist_count);
1485 page->freelist = cur;
1486
1487 for (idx = 1; idx < page->objects; idx++) {
1488 setup_object(s, page, cur);
1489 next = next_freelist_entry(s, page, &pos, start, page_limit,
1490 freelist_count);
1491 set_freepointer(s, cur, next);
1492 cur = next;
1493 }
1494 setup_object(s, page, cur);
1495 set_freepointer(s, cur, NULL);
1496
1497 return true;
1498}
1499#else
1500static inline int init_cache_random_seq(struct kmem_cache *s)
1501{
1502 return 0;
1503}
1504static inline void init_freelist_randomization(void) { }
1505static inline bool shuffle_freelist(struct kmem_cache *s, struct page *page)
1506{
1507 return false;
1508}
1509#endif /* CONFIG_SLAB_FREELIST_RANDOM */
1510
1408static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node) 1511static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
1409{ 1512{
1410 struct page *page; 1513 struct page *page;
@@ -1412,6 +1515,7 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
1412 gfp_t alloc_gfp; 1515 gfp_t alloc_gfp;
1413 void *start, *p; 1516 void *start, *p;
1414 int idx, order; 1517 int idx, order;
1518 bool shuffle;
1415 1519
1416 flags &= gfp_allowed_mask; 1520 flags &= gfp_allowed_mask;
1417 1521
@@ -1473,15 +1577,19 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
1473 1577
1474 kasan_poison_slab(page); 1578 kasan_poison_slab(page);
1475 1579
1476 for_each_object_idx(p, idx, s, start, page->objects) { 1580 shuffle = shuffle_freelist(s, page);
1477 setup_object(s, page, p); 1581
1478 if (likely(idx < page->objects)) 1582 if (!shuffle) {
1479 set_freepointer(s, p, p + s->size); 1583 for_each_object_idx(p, idx, s, start, page->objects) {
1480 else 1584 setup_object(s, page, p);
1481 set_freepointer(s, p, NULL); 1585 if (likely(idx < page->objects))
1586 set_freepointer(s, p, p + s->size);
1587 else
1588 set_freepointer(s, p, NULL);
1589 }
1590 page->freelist = fixup_red_left(s, start);
1482 } 1591 }
1483 1592
1484 page->freelist = fixup_red_left(s, start);
1485 page->inuse = page->objects; 1593 page->inuse = page->objects;
1486 page->frozen = 1; 1594 page->frozen = 1;
1487 1595
@@ -3207,6 +3315,7 @@ static void free_kmem_cache_nodes(struct kmem_cache *s)
3207 3315
3208void __kmem_cache_release(struct kmem_cache *s) 3316void __kmem_cache_release(struct kmem_cache *s)
3209{ 3317{
3318 cache_random_seq_destroy(s);
3210 free_percpu(s->cpu_slab); 3319 free_percpu(s->cpu_slab);
3211 free_kmem_cache_nodes(s); 3320 free_kmem_cache_nodes(s);
3212} 3321}
@@ -3431,6 +3540,13 @@ static int kmem_cache_open(struct kmem_cache *s, unsigned long flags)
3431#ifdef CONFIG_NUMA 3540#ifdef CONFIG_NUMA
3432 s->remote_node_defrag_ratio = 1000; 3541 s->remote_node_defrag_ratio = 1000;
3433#endif 3542#endif
3543
3544 /* Initialize the pre-computed randomized freelist if slab is up */
3545 if (slab_state >= UP) {
3546 if (init_cache_random_seq(s))
3547 goto error;
3548 }
3549
3434 if (!init_kmem_cache_nodes(s)) 3550 if (!init_kmem_cache_nodes(s))
3435 goto error; 3551 goto error;
3436 3552
@@ -3947,6 +4063,9 @@ void __init kmem_cache_init(void)
3947 setup_kmalloc_cache_index_table(); 4063 setup_kmalloc_cache_index_table();
3948 create_kmalloc_caches(0); 4064 create_kmalloc_caches(0);
3949 4065
4066 /* Setup random freelists for each cache */
4067 init_freelist_randomization();
4068
3950#ifdef CONFIG_SMP 4069#ifdef CONFIG_SMP
3951 register_cpu_notifier(&slab_notifier); 4070 register_cpu_notifier(&slab_notifier);
3952#endif 4071#endif