kernel-hardening.lists.openwall.com archive mirror
 help / color / mirror / Atom feed
* [kernel-hardening] [PATCH] mm: SLAB freelist randomization
@ 2016-04-15 17:25 Thomas Garnier
  2016-04-15 22:00 ` [kernel-hardening] " Andrew Morton
  0 siblings, 1 reply; 8+ messages in thread
From: Thomas Garnier @ 2016-04-15 17:25 UTC (permalink / raw)
  To: Christoph Lameter, Pekka Enberg, David Rientjes, Joonsoo Kim,
	Andrew Morton, Kees Cook
  Cc: gthelen, labbott, kernel-hardening, linux-kernel, linux-mm,
	Thomas Garnier

Provide an optional config (CONFIG_FREELIST_RANDOM) to randomize the
SLAB freelist. 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. This security feature reduces the predictability of the
kernel SLAB allocator against heap overflows rendering attacks much less
stable.

For example this attack against SLUB (also applicable against SLAB)
would be affected:
https://jon.oberheide.org/blog/2010/09/10/linux-kernel-can-slub-overflow/

Also, since v4.6 the freelist was moved at the end of the SLAB. It means
a controllable heap is opened to new attacks not yet publicly discussed.
A kernel heap overflow can be transformed to multiple use-after-free.
This feature makes this type of attack harder too.

The config option name is not specific to the SLAB as this approach will
be extended to other allocators like SLUB.

Performance results highlighted no major changes:

Netperf average on 10 runs:

threads,base,change
16,576943.10,585905.90 (101.55%)
32,564082.00,569741.20 (101.00%)
48,558334.30,561851.20 (100.63%)
64,552025.20,556448.30 (100.80%)
80,552294.40,551743.10 (99.90%)
96,552435.30,547529.20 (99.11%)
112,551320.60,550183.20 (99.79%)
128,549138.30,550542.70 (100.26%)
144,549344.50,544529.10 (99.12%)
160,550360.80,539929.30 (98.10%)

slab_test 1 run on boot. After is faster except for odd result on size
2048.

Before:

Single thread testing
=====================
1. Kmalloc: Repeatedly allocate then free test
10000 times kmalloc(8) -> 137 cycles kfree -> 126 cycles
10000 times kmalloc(16) -> 118 cycles kfree -> 119 cycles
10000 times kmalloc(32) -> 112 cycles kfree -> 119 cycles
10000 times kmalloc(64) -> 126 cycles kfree -> 123 cycles
10000 times kmalloc(128) -> 135 cycles kfree -> 131 cycles
10000 times kmalloc(256) -> 165 cycles kfree -> 104 cycles
10000 times kmalloc(512) -> 174 cycles kfree -> 126 cycles
10000 times kmalloc(1024) -> 242 cycles kfree -> 160 cycles
10000 times kmalloc(2048) -> 478 cycles kfree -> 239 cycles
10000 times kmalloc(4096) -> 747 cycles kfree -> 364 cycles
10000 times kmalloc(8192) -> 774 cycles kfree -> 404 cycles
10000 times kmalloc(16384) -> 849 cycles kfree -> 430 cycles
2. Kmalloc: alloc/free test
10000 times kmalloc(8)/kfree -> 118 cycles
10000 times kmalloc(16)/kfree -> 118 cycles
10000 times kmalloc(32)/kfree -> 118 cycles
10000 times kmalloc(64)/kfree -> 121 cycles
10000 times kmalloc(128)/kfree -> 118 cycles
10000 times kmalloc(256)/kfree -> 115 cycles
10000 times kmalloc(512)/kfree -> 115 cycles
10000 times kmalloc(1024)/kfree -> 115 cycles
10000 times kmalloc(2048)/kfree -> 115 cycles
10000 times kmalloc(4096)/kfree -> 115 cycles
10000 times kmalloc(8192)/kfree -> 115 cycles
10000 times kmalloc(16384)/kfree -> 115 cycles

After:

Single thread testing
=====================
1. Kmalloc: Repeatedly allocate then free test
10000 times kmalloc(8) -> 99 cycles kfree -> 84 cycles
10000 times kmalloc(16) -> 88 cycles kfree -> 83 cycles
10000 times kmalloc(32) -> 90 cycles kfree -> 81 cycles
10000 times kmalloc(64) -> 107 cycles kfree -> 97 cycles
10000 times kmalloc(128) -> 134 cycles kfree -> 89 cycles
10000 times kmalloc(256) -> 145 cycles kfree -> 97 cycles
10000 times kmalloc(512) -> 177 cycles kfree -> 116 cycles
10000 times kmalloc(1024) -> 223 cycles kfree -> 151 cycles
10000 times kmalloc(2048) -> 1429 cycles kfree -> 221 cycles
10000 times kmalloc(4096) -> 720 cycles kfree -> 348 cycles
10000 times kmalloc(8192) -> 788 cycles kfree -> 393 cycles
10000 times kmalloc(16384) -> 867 cycles kfree -> 433 cycles
2. Kmalloc: alloc/free test
10000 times kmalloc(8)/kfree -> 115 cycles
10000 times kmalloc(16)/kfree -> 115 cycles
10000 times kmalloc(32)/kfree -> 115 cycles
10000 times kmalloc(64)/kfree -> 120 cycles
10000 times kmalloc(128)/kfree -> 127 cycles
10000 times kmalloc(256)/kfree -> 119 cycles
10000 times kmalloc(512)/kfree -> 112 cycles
10000 times kmalloc(1024)/kfree -> 112 cycles
10000 times kmalloc(2048)/kfree -> 112 cycles
10000 times kmalloc(4096)/kfree -> 112 cycles
10000 times kmalloc(8192)/kfree -> 112 cycles
10000 times kmalloc(16384)/kfree -> 112 cycles

Signed-off-by: Thomas Garnier <thgarnie@google.com>
---
Based on next-20160414
---
 init/Kconfig |   9 ++++
 mm/slab.c    | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 166 insertions(+), 1 deletion(-)

diff --git a/init/Kconfig b/init/Kconfig
index 0dfd09d..ee35418 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1742,6 +1742,15 @@ config SLOB
 
 endchoice
 
+config FREELIST_RANDOM
+	default n
+	depends on SLAB
+	bool "SLAB freelist randomization"
+	help
+	  Randomizes the freelist order used on creating new SLABs. This
+	  security feature reduces the predictability of the kernel slab
+	  allocator against heap overflows.
+
 config SLUB_CPU_PARTIAL
 	default y
 	depends on SLUB && SMP
diff --git a/mm/slab.c b/mm/slab.c
index b70aabf..5d8bde2 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -1229,6 +1229,61 @@ static void __init set_up_node(struct kmem_cache *cachep, int index)
 	}
 }
 
+#ifdef CONFIG_FREELIST_RANDOM
+/*
+ * Master lists are pre-computed random lists
+ * Lists of different sizes are used to optimize performance on different
+ * SLAB object sizes per pages.
+ */
+static freelist_idx_t master_list_2[2];
+static freelist_idx_t master_list_4[4];
+static freelist_idx_t master_list_8[8];
+static freelist_idx_t master_list_16[16];
+static freelist_idx_t master_list_32[32];
+static freelist_idx_t master_list_64[64];
+static freelist_idx_t master_list_128[128];
+static freelist_idx_t master_list_256[256];
+static struct m_list {
+	size_t count;
+	freelist_idx_t *list;
+} master_lists[] = {
+	{ ARRAY_SIZE(master_list_2), master_list_2 },
+	{ ARRAY_SIZE(master_list_4), master_list_4 },
+	{ ARRAY_SIZE(master_list_8), master_list_8 },
+	{ ARRAY_SIZE(master_list_16), master_list_16 },
+	{ ARRAY_SIZE(master_list_32), master_list_32 },
+	{ ARRAY_SIZE(master_list_64), master_list_64 },
+	{ ARRAY_SIZE(master_list_128), master_list_128 },
+	{ ARRAY_SIZE(master_list_256), master_list_256 },
+};
+
+static void __init freelist_random_init(void)
+{
+	unsigned int seed;
+	size_t z, i, rand;
+	struct rnd_state slab_rand;
+
+	get_random_bytes_arch(&seed, sizeof(seed));
+	prandom_seed_state(&slab_rand, seed);
+
+	for (z = 0; z < ARRAY_SIZE(master_lists); z++) {
+		for (i = 0; i < master_lists[z].count; i++)
+			master_lists[z].list[i] = i;
+
+		/* Fisher-Yates shuffle */
+		for (i = master_lists[z].count - 1; i > 0; i--) {
+			rand = prandom_u32_state(&slab_rand);
+			rand %= (i + 1);
+			swap(master_lists[z].list[i],
+				master_lists[z].list[rand]);
+		}
+	}
+}
+#else
+static inline void __init freelist_random_init(void) { }
+#endif /* CONFIG_FREELIST_RANDOM */
+
+
 /*
  * Initialisation.  Called after the page allocator have been initialised and
  * before smp_init().
@@ -1255,6 +1310,8 @@ void __init kmem_cache_init(void)
 	if (!slab_max_order_set && totalram_pages > (32 << 20) >> PAGE_SHIFT)
 		slab_max_order = SLAB_MAX_ORDER_HI;
 
+	freelist_random_init();
+
 	/* Bootstrap is tricky, because several objects are allocated
 	 * from caches that do not exist yet:
 	 * 1) initialize the kmem_cache cache: it contains the struct
@@ -2442,6 +2499,101 @@ static void cache_init_objs_debug(struct kmem_cache *cachep, struct page *page)
 #endif
 }
 
+#ifdef CONFIG_FREELIST_RANDOM
+enum master_type {
+	match,
+	less,
+	more
+};
+
+struct random_mng {
+	unsigned int padding;
+	unsigned int pos;
+	unsigned int count;
+	struct m_list master_list;
+	unsigned int master_count;
+	enum master_type type;
+};
+
+static void random_mng_initialize(struct random_mng *mng, unsigned int count)
+{
+	unsigned int idx;
+	const unsigned int last_idx = ARRAY_SIZE(master_lists) - 1;
+
+	memset(mng, 0, sizeof(*mng));
+	mng->count = count;
+	mng->pos = 0;
+	/* count is >= 2 */
+	idx = ilog2(count) - 1;
+	if (idx >= last_idx)
+		idx = last_idx;
+	else if (roundup_pow_of_two(idx + 1) != count)
+		idx++;
+	mng->master_list = master_lists[idx];
+	if (mng->master_list.count == mng->count)
+		mng->type = match;
+	else if (mng->master_list.count > mng->count)
+		mng->type = more;
+	else
+		mng->type = less;
+}
+
+static freelist_idx_t get_next_entry(struct random_mng *mng)
+{
+	if (mng->type == less && mng->pos == mng->master_list.count) {
+		mng->padding += mng->pos;
+		mng->pos = 0;
+	}
+	BUG_ON(mng->pos >= mng->master_list.count);
+	return mng->master_list.list[mng->pos++];
+}
+
+static freelist_idx_t next_random_slot(struct random_mng *mng)
+{
+	freelist_idx_t cur, entry;
+
+	entry = get_next_entry(mng);
+
+	if (mng->type != match) {
+		while ((entry + mng->padding) >= mng->count)
+			entry = get_next_entry(mng);
+		cur = entry + mng->padding;
+		BUG_ON(cur >= mng->count);
+	} else {
+		cur = entry;
+	}
+
+	return cur;
+}
+
+static void shuffle_freelist(struct kmem_cache *cachep, struct page *page,
+			     unsigned int count)
+{
+	unsigned int i;
+	struct random_mng mng;
+
+	if (count < 2) {
+		for (i = 0; i < count; i++)
+			set_free_obj(page, i, i);
+		return;
+	}
+
+	/* Last chunk is used already in this case */
+	if (OBJFREELIST_SLAB(cachep))
+		count--;
+
+	random_mng_initialize(&mng, count);
+	for (i = 0; i < count; i++)
+		set_free_obj(page, i, next_random_slot(&mng));
+
+	if (OBJFREELIST_SLAB(cachep))
+		set_free_obj(page, i, i);
+}
+#else
+static inline void shuffle_freelist(struct kmem_cache *cachep,
+				    struct page *page, unsigned int count) { }
+#endif /* CONFIG_FREELIST_RANDOM */
+
 static void cache_init_objs(struct kmem_cache *cachep,
 			    struct page *page)
 {
@@ -2464,8 +2616,12 @@ static void cache_init_objs(struct kmem_cache *cachep,
 			kasan_poison_object_data(cachep, objp);
 		}
 
-		set_free_obj(page, i, i);
+		/* If enabled, initialization is done in shuffle_freelist */
+		if (!config_enabled(CONFIG_FREELIST_RANDOM))
+			set_free_obj(page, i, i);
 	}
+
+	shuffle_freelist(cachep, page, cachep->num);
 }
 
 static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags)
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [kernel-hardening] Re: [PATCH] mm: SLAB freelist randomization
  2016-04-15 17:25 [kernel-hardening] [PATCH] mm: SLAB freelist randomization Thomas Garnier
@ 2016-04-15 22:00 ` Andrew Morton
  2016-04-15 22:26   ` Joe Perches
  0 siblings, 1 reply; 8+ messages in thread
From: Andrew Morton @ 2016-04-15 22:00 UTC (permalink / raw)
  To: Thomas Garnier
  Cc: Christoph Lameter, Pekka Enberg, David Rientjes, Joonsoo Kim,
	Kees Cook, gthelen, labbott, kernel-hardening, linux-kernel,
	linux-mm

On Fri, 15 Apr 2016 10:25:59 -0700 Thomas Garnier <thgarnie@google.com> wrote:

> Provide an optional config (CONFIG_FREELIST_RANDOM) to randomize the
> SLAB freelist. 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. This security feature reduces the predictability of the
> kernel SLAB allocator against heap overflows rendering attacks much less
> stable.
> 
> For example this attack against SLUB (also applicable against SLAB)
> would be affected:
> https://jon.oberheide.org/blog/2010/09/10/linux-kernel-can-slub-overflow/
> 
> Also, since v4.6 the freelist was moved at the end of the SLAB. It means
> a controllable heap is opened to new attacks not yet publicly discussed.
> A kernel heap overflow can be transformed to multiple use-after-free.
> This feature makes this type of attack harder too.
> 
> The config option name is not specific to the SLAB as this approach will
> be extended to other allocators like SLUB.
> 
> Performance results highlighted no major changes:
>
> ...
>
> --- a/mm/slab.c
> +++ b/mm/slab.c
> @@ -1229,6 +1229,61 @@ static void __init set_up_node(struct kmem_cache *cachep, int index)
>  	}
>  }
>  
> +#ifdef CONFIG_FREELIST_RANDOM
> +/*
> + * Master lists are pre-computed random lists
> + * Lists of different sizes are used to optimize performance on different
> + * SLAB object sizes per pages.

"object sizes per pages" doesn't make sense.  "object-per-page counts"?
"object sizes"?

> + */
> +static freelist_idx_t master_list_2[2];
> +static freelist_idx_t master_list_4[4];
> +static freelist_idx_t master_list_8[8];
> +static freelist_idx_t master_list_16[16];
> +static freelist_idx_t master_list_32[32];
> +static freelist_idx_t master_list_64[64];
> +static freelist_idx_t master_list_128[128];
> +static freelist_idx_t master_list_256[256];
> +static struct m_list {
> +	size_t count;
> +	freelist_idx_t *list;
> +} master_lists[] = {
> +	{ ARRAY_SIZE(master_list_2), master_list_2 },
> +	{ ARRAY_SIZE(master_list_4), master_list_4 },
> +	{ ARRAY_SIZE(master_list_8), master_list_8 },
> +	{ ARRAY_SIZE(master_list_16), master_list_16 },
> +	{ ARRAY_SIZE(master_list_32), master_list_32 },
> +	{ ARRAY_SIZE(master_list_64), master_list_64 },
> +	{ ARRAY_SIZE(master_list_128), master_list_128 },
> +	{ ARRAY_SIZE(master_list_256), master_list_256 },
> +};
> +
> +static void __init freelist_random_init(void)
> +{
> +	unsigned int seed;
> +	size_t z, i, rand;
> +	struct rnd_state slab_rand;
> +
> +	get_random_bytes_arch(&seed, sizeof(seed));

Using get_random_bytes_arch() seems a rather poor decision.  There are
the caveats described at the get_random_bytes_arch() definition site,
and the minor issue that get_random_bytes_arch() only actually works on
x86 and powerpc!

This is run-once __init code, so rather than adding the kernel's very
first get_random_bytes_arch() call site(!), why not stick with good old
get_random_bytes()?

If there's something I'm missing, please at least place a very good
comment here explaining the reasoning.

> +	prandom_seed_state(&slab_rand, seed);
> +
> +	for (z = 0; z < ARRAY_SIZE(master_lists); z++) {
> +		for (i = 0; i < master_lists[z].count; i++)
> +			master_lists[z].list[i] = i;
> +
> +		/* Fisher-Yates shuffle */
> +		for (i = master_lists[z].count - 1; i > 0; i--) {
> +			rand = prandom_u32_state(&slab_rand);
> +			rand %= (i + 1);
> +			swap(master_lists[z].list[i],
> +				master_lists[z].list[rand]);
> +		}
> +	}
> +}
> +#else
> +static inline void __init freelist_random_init(void) { }
> +#endif /* CONFIG_FREELIST_RANDOM */
> +
> +
>  /*
>   * Initialisation.  Called after the page allocator have been initialised and
>   * before smp_init().
>
> ...
>
> @@ -2442,6 +2499,101 @@ static void cache_init_objs_debug(struct kmem_cache *cachep, struct page *page)
>  #endif
>  }
>  
> +#ifdef CONFIG_FREELIST_RANDOM
> +enum master_type {
> +	match,
> +	less,
> +	more
> +};
> +
> +struct random_mng {

I can't work out what "mng" means in this code.

> +	unsigned int padding;
> +	unsigned int pos;
> +	unsigned int count;
> +	struct m_list master_list;
> +	unsigned int master_count;
> +	enum master_type type;
> +};

It would be useful to document the above struct.  Skilfully documenting
the data structures is key to making the code understandable.

> +static void random_mng_initialize(struct random_mng *mng, unsigned int count)
> +{
> +	unsigned int idx;
> +	const unsigned int last_idx = ARRAY_SIZE(master_lists) - 1;
> +
> +	memset(mng, 0, sizeof(*mng));
> +	mng->count = count;
> +	mng->pos = 0;
> +	/* count is >= 2 */
> +	idx = ilog2(count) - 1;

slab.c should now include log2.h.

> +	if (idx >= last_idx)
> +		idx = last_idx;
> +	else if (roundup_pow_of_two(idx + 1) != count)
> +		idx++;
> +	mng->master_list = master_lists[idx];
> +	if (mng->master_list.count == mng->count)
> +		mng->type = match;
> +	else if (mng->master_list.count > mng->count)
> +		mng->type = more;
> +	else
> +		mng->type = less;
> +}
> +
> +static freelist_idx_t get_next_entry(struct random_mng *mng)
> +{
> +	if (mng->type == less && mng->pos == mng->master_list.count) {
> +		mng->padding += mng->pos;
> +		mng->pos = 0;
> +	}
> +	BUG_ON(mng->pos >= mng->master_list.count);
> +	return mng->master_list.list[mng->pos++];
> +}
> +
> +static freelist_idx_t next_random_slot(struct random_mng *mng)
> +{
> +	freelist_idx_t cur, entry;
> +
> +	entry = get_next_entry(mng);
> +
> +	if (mng->type != match) {
> +		while ((entry + mng->padding) >= mng->count)
> +			entry = get_next_entry(mng);
> +		cur = entry + mng->padding;
> +		BUG_ON(cur >= mng->count);
> +	} else {
> +		cur = entry;
> +	}
> +
> +	return cur;
> +}
> +
> +static void shuffle_freelist(struct kmem_cache *cachep, struct page *page,
> +			     unsigned int count)
> +{
> +	unsigned int i;
> +	struct random_mng mng;
> +
> +	if (count < 2) {
> +		for (i = 0; i < count; i++)
> +			set_free_obj(page, i, i);
> +		return;
> +	}
> +
> +	/* Last chunk is used already in this case */
> +	if (OBJFREELIST_SLAB(cachep))
> +		count--;
> +
> +	random_mng_initialize(&mng, count);
> +	for (i = 0; i < count; i++)
> +		set_free_obj(page, i, next_random_slot(&mng));
> +
> +	if (OBJFREELIST_SLAB(cachep))
> +		set_free_obj(page, i, i);
> +}

Sorry, but the code is really too light on comments.  Each of the above
functions would benefit from a description of what they do and, most
importantly, why they do it.

Please address these things and let's wait for the slab maintainers
(and perhaps Kees?) to weigh in.

> +#else
> +static inline void shuffle_freelist(struct kmem_cache *cachep,
> +				    struct page *page, unsigned int count) { }
> +#endif /* CONFIG_FREELIST_RANDOM */
> +
>  static void cache_init_objs(struct kmem_cache *cachep,
>  			    struct page *page)
>  {
> @@ -2464,8 +2616,12 @@ static void cache_init_objs(struct kmem_cache *cachep,
>  			kasan_poison_object_data(cachep, objp);
>  		}
>  
> -		set_free_obj(page, i, i);
> +		/* If enabled, initialization is done in shuffle_freelist */
> +		if (!config_enabled(CONFIG_FREELIST_RANDOM))
> +			set_free_obj(page, i, i);
>  	}
> +
> +	shuffle_freelist(cachep, page, cachep->num);
>  }
>  
>  static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags)

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [kernel-hardening] Re: [PATCH] mm: SLAB freelist randomization
  2016-04-15 22:00 ` [kernel-hardening] " Andrew Morton
@ 2016-04-15 22:26   ` Joe Perches
  2016-04-15 22:47     ` Thomas Garnier
  0 siblings, 1 reply; 8+ messages in thread
From: Joe Perches @ 2016-04-15 22:26 UTC (permalink / raw)
  To: Andrew Morton, Thomas Garnier
  Cc: Christoph Lameter, Pekka Enberg, David Rientjes, Joonsoo Kim,
	Kees Cook, gthelen, labbott, kernel-hardening, linux-kernel,
	linux-mm

On Fri, 2016-04-15 at 15:00 -0700, Andrew Morton wrote:
> On Fri, 15 Apr 2016 10:25:59 -0700 Thomas Garnier <thgarnie@google.com> wrote:
> > Provide an optional config (CONFIG_FREELIST_RANDOM) to randomize the
> > SLAB freelist. 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. This security feature reduces the predictability of the
> > kernel SLAB allocator against heap overflows rendering attacks much less
> > stable.

trivia:

> > @@ -1229,6 +1229,61 @@ static void __init set_up_node(struct kmem_cache *cachep, int index)
[]
> > + */
> > +static freelist_idx_t master_list_2[2];
> > +static freelist_idx_t master_list_4[4];
> > +static freelist_idx_t master_list_8[8];
> > +static freelist_idx_t master_list_16[16];
> > +static freelist_idx_t master_list_32[32];
> > +static freelist_idx_t master_list_64[64];
> > +static freelist_idx_t master_list_128[128];
> > +static freelist_idx_t master_list_256[256];
> > +static struct m_list {
> > +	size_t count;
> > +	freelist_idx_t *list;
> > +} master_lists[] = {
> > +	{ ARRAY_SIZE(master_list_2), master_list_2 },
> > +	{ ARRAY_SIZE(master_list_4), master_list_4 },
> > +	{ ARRAY_SIZE(master_list_8), master_list_8 },
> > +	{ ARRAY_SIZE(master_list_16), master_list_16 },
> > +	{ ARRAY_SIZE(master_list_32), master_list_32 },
> > +	{ ARRAY_SIZE(master_list_64), master_list_64 },
> > +	{ ARRAY_SIZE(master_list_128), master_list_128 },
> > +	{ ARRAY_SIZE(master_list_256), master_list_256 },
> > +};

static const struct m_list?

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [kernel-hardening] Re: [PATCH] mm: SLAB freelist randomization
  2016-04-15 22:26   ` Joe Perches
@ 2016-04-15 22:47     ` Thomas Garnier
  2016-04-18 15:59       ` Thomas Garnier
  0 siblings, 1 reply; 8+ messages in thread
From: Thomas Garnier @ 2016-04-15 22:47 UTC (permalink / raw)
  To: Joe Perches
  Cc: Andrew Morton, Christoph Lameter, Pekka Enberg, David Rientjes,
	Joonsoo Kim, Kees Cook, Greg Thelen, Laura Abbott,
	kernel-hardening, LKML, Linux-MM

Thanks for the comments. I will address them in a v2 early next week.

If anyone has other comments, please let me know.

Thomas

On Fri, Apr 15, 2016 at 3:26 PM, Joe Perches <joe@perches.com> wrote:
> On Fri, 2016-04-15 at 15:00 -0700, Andrew Morton wrote:
>> On Fri, 15 Apr 2016 10:25:59 -0700 Thomas Garnier <thgarnie@google.com> wrote:
>> > Provide an optional config (CONFIG_FREELIST_RANDOM) to randomize the
>> > SLAB freelist. 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. This security feature reduces the predictability of the
>> > kernel SLAB allocator against heap overflows rendering attacks much less
>> > stable.
>
> trivia:
>
>> > @@ -1229,6 +1229,61 @@ static void __init set_up_node(struct kmem_cache *cachep, int index)
> []
>> > + */
>> > +static freelist_idx_t master_list_2[2];
>> > +static freelist_idx_t master_list_4[4];
>> > +static freelist_idx_t master_list_8[8];
>> > +static freelist_idx_t master_list_16[16];
>> > +static freelist_idx_t master_list_32[32];
>> > +static freelist_idx_t master_list_64[64];
>> > +static freelist_idx_t master_list_128[128];
>> > +static freelist_idx_t master_list_256[256];
>> > +static struct m_list {
>> > +   size_t count;
>> > +   freelist_idx_t *list;
>> > +} master_lists[] = {
>> > +   { ARRAY_SIZE(master_list_2), master_list_2 },
>> > +   { ARRAY_SIZE(master_list_4), master_list_4 },
>> > +   { ARRAY_SIZE(master_list_8), master_list_8 },
>> > +   { ARRAY_SIZE(master_list_16), master_list_16 },
>> > +   { ARRAY_SIZE(master_list_32), master_list_32 },
>> > +   { ARRAY_SIZE(master_list_64), master_list_64 },
>> > +   { ARRAY_SIZE(master_list_128), master_list_128 },
>> > +   { ARRAY_SIZE(master_list_256), master_list_256 },
>> > +};
>
> static const struct m_list?
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [kernel-hardening] Re: [PATCH] mm: SLAB freelist randomization
  2016-04-15 22:47     ` Thomas Garnier
@ 2016-04-18 15:59       ` Thomas Garnier
  2016-04-18 19:36         ` Laura Abbott
  0 siblings, 1 reply; 8+ messages in thread
From: Thomas Garnier @ 2016-04-18 15:59 UTC (permalink / raw)
  To: Joe Perches
  Cc: Andrew Morton, Christoph Lameter, Pekka Enberg, David Rientjes,
	Joonsoo Kim, Kees Cook, Greg Thelen, Laura Abbott,
	kernel-hardening, LKML, Linux-MM

I will send the next version today. Note that I get_random_bytes_arch
is used because at that stage we have 0 bits of entropy. It seemed
like a better idea to use the arch version that will fallback on
get_random_bytes sub API in the worse case.

On Fri, Apr 15, 2016 at 3:47 PM, Thomas Garnier <thgarnie@google.com> wrote:
> Thanks for the comments. I will address them in a v2 early next week.
>
> If anyone has other comments, please let me know.
>
> Thomas
>
> On Fri, Apr 15, 2016 at 3:26 PM, Joe Perches <joe@perches.com> wrote:
>> On Fri, 2016-04-15 at 15:00 -0700, Andrew Morton wrote:
>>> On Fri, 15 Apr 2016 10:25:59 -0700 Thomas Garnier <thgarnie@google.com> wrote:
>>> > Provide an optional config (CONFIG_FREELIST_RANDOM) to randomize the
>>> > SLAB freelist. 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. This security feature reduces the predictability of the
>>> > kernel SLAB allocator against heap overflows rendering attacks much less
>>> > stable.
>>
>> trivia:
>>
>>> > @@ -1229,6 +1229,61 @@ static void __init set_up_node(struct kmem_cache *cachep, int index)
>> []
>>> > + */
>>> > +static freelist_idx_t master_list_2[2];
>>> > +static freelist_idx_t master_list_4[4];
>>> > +static freelist_idx_t master_list_8[8];
>>> > +static freelist_idx_t master_list_16[16];
>>> > +static freelist_idx_t master_list_32[32];
>>> > +static freelist_idx_t master_list_64[64];
>>> > +static freelist_idx_t master_list_128[128];
>>> > +static freelist_idx_t master_list_256[256];
>>> > +static struct m_list {
>>> > +   size_t count;
>>> > +   freelist_idx_t *list;
>>> > +} master_lists[] = {
>>> > +   { ARRAY_SIZE(master_list_2), master_list_2 },
>>> > +   { ARRAY_SIZE(master_list_4), master_list_4 },
>>> > +   { ARRAY_SIZE(master_list_8), master_list_8 },
>>> > +   { ARRAY_SIZE(master_list_16), master_list_16 },
>>> > +   { ARRAY_SIZE(master_list_32), master_list_32 },
>>> > +   { ARRAY_SIZE(master_list_64), master_list_64 },
>>> > +   { ARRAY_SIZE(master_list_128), master_list_128 },
>>> > +   { ARRAY_SIZE(master_list_256), master_list_256 },
>>> > +};
>>
>> static const struct m_list?
>>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [kernel-hardening] Re: [PATCH] mm: SLAB freelist randomization
  2016-04-18 15:59       ` Thomas Garnier
@ 2016-04-18 19:36         ` Laura Abbott
  2016-04-18 19:52           ` Thomas Garnier
  0 siblings, 1 reply; 8+ messages in thread
From: Laura Abbott @ 2016-04-18 19:36 UTC (permalink / raw)
  To: Thomas Garnier, Joe Perches
  Cc: Andrew Morton, Christoph Lameter, Pekka Enberg, David Rientjes,
	Joonsoo Kim, Kees Cook, Greg Thelen, Laura Abbott,
	kernel-hardening, LKML, Linux-MM

On 04/18/2016 08:59 AM, Thomas Garnier wrote:
> I will send the next version today. Note that I get_random_bytes_arch
> is used because at that stage we have 0 bits of entropy. It seemed
> like a better idea to use the arch version that will fallback on
> get_random_bytes sub API in the worse case.
>

This is unfortunate for ARM/ARM64. Those platforms don't have a standard
method for getting random numbers so until additional entropy is added
get_random_bytes will always return the same seed and indeed I always
see the same shuffle on a quick test of arm64. For KASLR, the workaround
was to require the bootloader to pass in entropy. It might be good to
either document this or require this only be used with CONFIG_ARCH_RANDOM.


> On Fri, Apr 15, 2016 at 3:47 PM, Thomas Garnier <thgarnie@google.com> wrote:
>> Thanks for the comments. I will address them in a v2 early next week.
>>
>> If anyone has other comments, please let me know.
>>
>> Thomas
>>
>> On Fri, Apr 15, 2016 at 3:26 PM, Joe Perches <joe@perches.com> wrote:
>>> On Fri, 2016-04-15 at 15:00 -0700, Andrew Morton wrote:
>>>> On Fri, 15 Apr 2016 10:25:59 -0700 Thomas Garnier <thgarnie@google.com> wrote:
>>>>> Provide an optional config (CONFIG_FREELIST_RANDOM) to randomize the
>>>>> SLAB freelist. 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. This security feature reduces the predictability of the
>>>>> kernel SLAB allocator against heap overflows rendering attacks much less
>>>>> stable.
>>>
>>> trivia:
>>>
>>>>> @@ -1229,6 +1229,61 @@ static void __init set_up_node(struct kmem_cache *cachep, int index)
>>> []
>>>>> + */
>>>>> +static freelist_idx_t master_list_2[2];
>>>>> +static freelist_idx_t master_list_4[4];
>>>>> +static freelist_idx_t master_list_8[8];
>>>>> +static freelist_idx_t master_list_16[16];
>>>>> +static freelist_idx_t master_list_32[32];
>>>>> +static freelist_idx_t master_list_64[64];
>>>>> +static freelist_idx_t master_list_128[128];
>>>>> +static freelist_idx_t master_list_256[256];
>>>>> +static struct m_list {
>>>>> +   size_t count;
>>>>> +   freelist_idx_t *list;
>>>>> +} master_lists[] = {
>>>>> +   { ARRAY_SIZE(master_list_2), master_list_2 },
>>>>> +   { ARRAY_SIZE(master_list_4), master_list_4 },
>>>>> +   { ARRAY_SIZE(master_list_8), master_list_8 },
>>>>> +   { ARRAY_SIZE(master_list_16), master_list_16 },
>>>>> +   { ARRAY_SIZE(master_list_32), master_list_32 },
>>>>> +   { ARRAY_SIZE(master_list_64), master_list_64 },
>>>>> +   { ARRAY_SIZE(master_list_128), master_list_128 },
>>>>> +   { ARRAY_SIZE(master_list_256), master_list_256 },
>>>>> +};
>>>
>>> static const struct m_list?
>>>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [kernel-hardening] Re: [PATCH] mm: SLAB freelist randomization
  2016-04-18 19:36         ` Laura Abbott
@ 2016-04-18 19:52           ` Thomas Garnier
  2016-04-25 21:04             ` Andrew Morton
  0 siblings, 1 reply; 8+ messages in thread
From: Thomas Garnier @ 2016-04-18 19:52 UTC (permalink / raw)
  To: Laura Abbott
  Cc: Joe Perches, Andrew Morton, Christoph Lameter, Pekka Enberg,
	David Rientjes, Joonsoo Kim, Kees Cook, Greg Thelen,
	Laura Abbott, kernel-hardening, LKML, Linux-MM

I agree, if we had a generic way to pass entropy across boots on all
architecture that would be amazing. I will let the SLAB maintainers to
decide on requiring CONFIG_ARCH_RANDOM or documenting it.

On Mon, Apr 18, 2016 at 12:36 PM, Laura Abbott <labbott@redhat.com> wrote:
> On 04/18/2016 08:59 AM, Thomas Garnier wrote:
>>
>> I will send the next version today. Note that I get_random_bytes_arch
>> is used because at that stage we have 0 bits of entropy. It seemed
>> like a better idea to use the arch version that will fallback on
>> get_random_bytes sub API in the worse case.
>>
>
> This is unfortunate for ARM/ARM64. Those platforms don't have a standard
> method for getting random numbers so until additional entropy is added
> get_random_bytes will always return the same seed and indeed I always
> see the same shuffle on a quick test of arm64. For KASLR, the workaround
> was to require the bootloader to pass in entropy. It might be good to
> either document this or require this only be used with CONFIG_ARCH_RANDOM.
>
>
>
>> On Fri, Apr 15, 2016 at 3:47 PM, Thomas Garnier <thgarnie@google.com>
>> wrote:
>>>
>>> Thanks for the comments. I will address them in a v2 early next week.
>>>
>>> If anyone has other comments, please let me know.
>>>
>>> Thomas
>>>
>>> On Fri, Apr 15, 2016 at 3:26 PM, Joe Perches <joe@perches.com> wrote:
>>>>
>>>> On Fri, 2016-04-15 at 15:00 -0700, Andrew Morton wrote:
>>>>>
>>>>> On Fri, 15 Apr 2016 10:25:59 -0700 Thomas Garnier <thgarnie@google.com>
>>>>> wrote:
>>>>>>
>>>>>> Provide an optional config (CONFIG_FREELIST_RANDOM) to randomize the
>>>>>> SLAB freelist. 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. This security feature reduces the predictability of
>>>>>> the
>>>>>> kernel SLAB allocator against heap overflows rendering attacks much
>>>>>> less
>>>>>> stable.
>>>>
>>>>
>>>> trivia:
>>>>
>>>>>> @@ -1229,6 +1229,61 @@ static void __init set_up_node(struct
>>>>>> kmem_cache *cachep, int index)
>>>>
>>>> []
>>>>>>
>>>>>> + */
>>>>>> +static freelist_idx_t master_list_2[2];
>>>>>> +static freelist_idx_t master_list_4[4];
>>>>>> +static freelist_idx_t master_list_8[8];
>>>>>> +static freelist_idx_t master_list_16[16];
>>>>>> +static freelist_idx_t master_list_32[32];
>>>>>> +static freelist_idx_t master_list_64[64];
>>>>>> +static freelist_idx_t master_list_128[128];
>>>>>> +static freelist_idx_t master_list_256[256];
>>>>>> +static struct m_list {
>>>>>> +   size_t count;
>>>>>> +   freelist_idx_t *list;
>>>>>> +} master_lists[] = {
>>>>>> +   { ARRAY_SIZE(master_list_2), master_list_2 },
>>>>>> +   { ARRAY_SIZE(master_list_4), master_list_4 },
>>>>>> +   { ARRAY_SIZE(master_list_8), master_list_8 },
>>>>>> +   { ARRAY_SIZE(master_list_16), master_list_16 },
>>>>>> +   { ARRAY_SIZE(master_list_32), master_list_32 },
>>>>>> +   { ARRAY_SIZE(master_list_64), master_list_64 },
>>>>>> +   { ARRAY_SIZE(master_list_128), master_list_128 },
>>>>>> +   { ARRAY_SIZE(master_list_256), master_list_256 },
>>>>>> +};
>>>>
>>>>
>>>> static const struct m_list?
>>>>
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [kernel-hardening] Re: [PATCH] mm: SLAB freelist randomization
  2016-04-18 19:52           ` Thomas Garnier
@ 2016-04-25 21:04             ` Andrew Morton
  0 siblings, 0 replies; 8+ messages in thread
From: Andrew Morton @ 2016-04-25 21:04 UTC (permalink / raw)
  To: Thomas Garnier
  Cc: Laura Abbott, Joe Perches, Christoph Lameter, Pekka Enberg,
	David Rientjes, Joonsoo Kim, Kees Cook, Greg Thelen,
	Laura Abbott, kernel-hardening, LKML, Linux-MM

On Mon, 18 Apr 2016 12:52:30 -0700 Thomas Garnier <thgarnie@google.com> wrote:

> I agree, if we had a generic way to pass entropy across boots on all
> architecture that would be amazing. I will let the SLAB maintainers to
> decide on requiring CONFIG_ARCH_RANDOM or documenting it.

In our world, requiring that sort of attention from maintainers
requires a pretty active level of pinging, poking and harrassing ;)

I do think that if you stick with get_random_bytes_arch() then it need
a comment explaining why.

And I (still) don't think that get_random_bytes_arch() actually does
what you want - if CONFIG_ARCH_RANDOM isn't implemented then
get_random_bytes_arch() just fails.  IOW your statement "the arch
version that will fallback on get_random_bytes sub API in the worse
case" is a misconception?  There is no fallback.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2016-04-25 21:04 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-15 17:25 [kernel-hardening] [PATCH] mm: SLAB freelist randomization Thomas Garnier
2016-04-15 22:00 ` [kernel-hardening] " Andrew Morton
2016-04-15 22:26   ` Joe Perches
2016-04-15 22:47     ` Thomas Garnier
2016-04-18 15:59       ` Thomas Garnier
2016-04-18 19:36         ` Laura Abbott
2016-04-18 19:52           ` Thomas Garnier
2016-04-25 21:04             ` Andrew Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).