linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] ELF interpretor info: align and add random padding
@ 2019-06-13 11:26 Yann Droneaud
  2019-06-13 11:26 ` [PATCH 1/3] binfmt/elf: use functions for stack manipulation Yann Droneaud
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Yann Droneaud @ 2019-06-13 11:26 UTC (permalink / raw)
  To: linux-kernel, kernel-hardening
  Cc: Andrew Morton, Kees Cook, Alexey Dobriyan, Yann Droneaud

Hi,

The following patches are mostly focused on ensuring AT_RANDOM array is
aligned on 16bytes boundary, and while being located at a pseudo-random
offset on stack (at most 256 bytes).

This patchset also insert a random sized (at most 15 bytes) padding between
AT_RANDOM and AT_PLATFORM and/or AT_BASE_PLATFORM.

It also insert a random sized padding (at most 256 bytes) between those
data and the arrays passed to userspace (argv[] + environ[] + auxv[])
as defined by ABI.

Adding random padding around AT_RANDOM, AT_PLATFORM, AT_BASE_PLATEFORM
should be viewed as an exercise of cargo-cult security as I'm not aware
of any attack that can be prevented with this mechanism in place.

Regards.

Yann Droneaud (3):
  binfmt/elf: use functions for stack manipulation
  binfmt/elf: align AT_RANDOM array
  binfmt/elf: randomize padding between ELF interp info

 fs/binfmt_elf.c | 110 +++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 86 insertions(+), 24 deletions(-)

-- 
2.21.0


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

* [PATCH 1/3] binfmt/elf: use functions for stack manipulation
  2019-06-13 11:26 [PATCH 0/3] ELF interpretor info: align and add random padding Yann Droneaud
@ 2019-06-13 11:26 ` Yann Droneaud
  2019-06-13 11:26 ` [PATCH 2/3] binfmt/elf: align AT_RANDOM array Yann Droneaud
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Yann Droneaud @ 2019-06-13 11:26 UTC (permalink / raw)
  To: linux-kernel, kernel-hardening
  Cc: Andrew Morton, Kees Cook, Alexey Dobriyan, Yann Droneaud

As a preliminary step to AT_RANDOM alignment and randomization,
replaces STACK_ macros by elf_stack_ inline functions to make them
easier to reuse.

STACK_ROUND() needed a pointer to elf_addr_t, while STACK_ADD() and
STACK_ALLOC() don't. In the new functions, the current stack pointer
is an obvious input/output parameter of unsigned long type.

STACK_ADD() returned an unsigned long, while STACK_ROUND() returned
a pointer to elf_addr_t. elf_stack_add_items() and elf_stack_align()
return both void.

STACK_ROUND() was used to reserve space on stack (like STACK_ADD())
and to align the resulting stack pointer on 16 bytes boundary. The
macro is replaced by elf_stack_add_items() followed by elf_stack_align().

Link: https://lore.kernel.org/lkml/cover.1560423331.git.ydroneaud@opteya.com
Signed-off-by: Yann Droneaud <ydroneaud@opteya.com>
---
 fs/binfmt_elf.c | 68 ++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 50 insertions(+), 18 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 8264b468f283..87f0c8a21350 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -136,21 +136,52 @@ static int padzero(unsigned long elf_bss)
 	return 0;
 }
 
-/* Let's use some macros to make this stack manipulation a little clearer */
+/* Let's use some functions to make this stack manipulation clearer */
+static inline void elf_stack_add_items(unsigned long *pp, size_t items)
+{
+	elf_addr_t *sp = (elf_addr_t *)*pp;
+
+#ifdef CONFIG_STACK_GROWSUP
+	sp += items;
+#else
+	sp -= items;
+#endif
+
+	*pp = (unsigned long)sp;
+}
+
+static inline void elf_stack_align(unsigned long *pp)
+{
+	unsigned long p = *pp;
+
+#ifdef CONFIG_STACK_GROWSUP
+	p += 15;
+#endif
+
+	p &= ~15UL;
+
+	*pp = p;
+}
+
+static inline elf_addr_t __user *elf_stack_alloc(unsigned long *pp,
+						 size_t len)
+{
+	unsigned long p = *pp;
+	elf_addr_t __user *sp;
+
 #ifdef CONFIG_STACK_GROWSUP
-#define STACK_ADD(sp, items) ((elf_addr_t __user *)(sp) + (items))
-#define STACK_ROUND(sp, items) \
-	((15 + (unsigned long) ((sp) + (items))) &~ 15UL)
-#define STACK_ALLOC(sp, len) ({ \
-	elf_addr_t __user *old_sp = (elf_addr_t __user *)sp; sp += len; \
-	old_sp; })
+	sp = (elf_addr_t __user *)p;
+	p += len;
 #else
-#define STACK_ADD(sp, items) ((elf_addr_t __user *)(sp) - (items))
-#define STACK_ROUND(sp, items) \
-	(((unsigned long) (sp - items)) &~ 15UL)
-#define STACK_ALLOC(sp, len) ({ sp -= len ; sp; })
+	p -= len;
+	sp = (elf_addr_t __user *)p;
 #endif
 
+	*pp = p;
+
+	return sp;
+}
+
 #ifndef ELF_BASE_PLATFORM
 /*
  * AT_BASE_PLATFORM indicates the "real" hardware/microarchitecture.
@@ -198,7 +229,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	if (k_platform) {
 		size_t len = strlen(k_platform) + 1;
 
-		u_platform = (elf_addr_t __user *)STACK_ALLOC(p, len);
+		u_platform = elf_stack_alloc(&p, len);
 		if (__copy_to_user(u_platform, k_platform, len))
 			return -EFAULT;
 	}
@@ -211,7 +242,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	if (k_base_platform) {
 		size_t len = strlen(k_base_platform) + 1;
 
-		u_base_platform = (elf_addr_t __user *)STACK_ALLOC(p, len);
+		u_base_platform = elf_stack_alloc(&p, len);
 		if (__copy_to_user(u_base_platform, k_base_platform, len))
 			return -EFAULT;
 	}
@@ -220,8 +251,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	 * Generate 16 random bytes for userspace PRNG seeding.
 	 */
 	get_random_bytes(k_rand_bytes, sizeof(k_rand_bytes));
-	u_rand_bytes = (elf_addr_t __user *)
-		       STACK_ALLOC(p, sizeof(k_rand_bytes));
+	u_rand_bytes = elf_stack_alloc(&p, sizeof(k_rand_bytes));
 	if (__copy_to_user(u_rand_bytes, k_rand_bytes, sizeof(k_rand_bytes)))
 		return -EFAULT;
 
@@ -280,11 +310,13 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 
 	/* And advance past the AT_NULL entry.  */
 	ei_index += 2;
-
-	sp = STACK_ADD(p, ei_index);
+	elf_stack_add_items(&p, ei_index);
 
 	items = (argc + 1) + (envc + 1) + 1;
-	bprm->p = STACK_ROUND(sp, items);
+	elf_stack_add_items(&p, items);
+	elf_stack_align(&p);
+
+	bprm->p = p;
 
 	/* Point sp at the lowest address on the stack */
 #ifdef CONFIG_STACK_GROWSUP
-- 
2.21.0


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

* [PATCH 2/3] binfmt/elf: align AT_RANDOM array
  2019-06-13 11:26 [PATCH 0/3] ELF interpretor info: align and add random padding Yann Droneaud
  2019-06-13 11:26 ` [PATCH 1/3] binfmt/elf: use functions for stack manipulation Yann Droneaud
@ 2019-06-13 11:26 ` Yann Droneaud
  2019-06-13 11:26 ` [PATCH 3/3] binfmt/elf: randomize padding between ELF interp info Yann Droneaud
  2019-06-13 13:39 ` [PATCH 4/3] binfmt/elf: don't expose prandom_u32() state Yann Droneaud
  3 siblings, 0 replies; 5+ messages in thread
From: Yann Droneaud @ 2019-06-13 11:26 UTC (permalink / raw)
  To: linux-kernel, kernel-hardening
  Cc: Andrew Morton, Kees Cook, Alexey Dobriyan, Yann Droneaud

Because AT_RANDOM array is put on stack after AT_PLATFORM and
AT_BASE_PLATFORM strings, it's aligned on a 1 byte boundary:

on x86:

  $ for i in 1 2 3 ; do LD_SHOW_AUXV=1 ./bin32 | grep AT_RANDOM ; done
  AT_RANDOM:            0xffb91ffb
  AT_RANDOM:            0xffcd60eb
  AT_RANDOM:            0xffd7d99b

on x86_64:

  $ for i in 1 2 3 ; do LD_SHOW_AUXV=1 ./bin64 | grep AT_RANDOM ; done
  AT_RANDOM:            0x7ffe3ce7d559
  AT_RANDOM:            0x7ffeb9ecd2b9
  AT_RANDOM:            0x7ffd37cebd49

Unlike AT_ENTROPY1 and AT_ENTROPY2, AT_ENTROPY, or AT_RANDOM1 and
AT_RANDOM2 proposals[1][2][3] which would be 32bits or 64bits integers,
kernel never promise AT_RANDOM array to be aligned[4].

But having a 16bytes array not aligned might not be optimal. Userspace
could benefit from an aligned array.

As the AT_PLATFORM and AT_BASE_PLATFORM are asciiz C strings, they don't
need to be aligned, thus they can be put after AT_RANDOM array.

As AT_RANDOM array is first put on stack, it's alignment should match
the one set by arch_align_stack().

[1] https://lore.kernel.org/lkml/4675C678.3080807@gentoo.org/
[2] https://lore.kernel.org/lkml/467948F5.3010709@gentoo.org/
[3] https://sourceware.org/ml/libc-alpha/2008-10/msg00013.html
[1] https://lore.kernel.org/lkml/20081003001616.GN10632@outflux.net/

Link: https://lore.kernel.org/lkml/cover.1560423331.git.ydroneaud@opteya.com
Signed-off-by: Yann Droneaud <ydroneaud@opteya.com>
---
 fs/binfmt_elf.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 87f0c8a21350..cfcc01fff4ae 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -219,6 +219,14 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 
 	p = arch_align_stack(p);
 
+	/*
+	 * Generate 16 random bytes for userspace PRNG seeding.
+	 */
+	get_random_bytes(k_rand_bytes, sizeof(k_rand_bytes));
+	u_rand_bytes = elf_stack_alloc(&p, sizeof(k_rand_bytes));
+	if (__copy_to_user(u_rand_bytes, k_rand_bytes, sizeof(k_rand_bytes)))
+		return -EFAULT;
+
 	/*
 	 * If this architecture has a platform capability string, copy it
 	 * to userspace.  In some cases (Sparc), this info is impossible
@@ -247,14 +255,6 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 			return -EFAULT;
 	}
 
-	/*
-	 * Generate 16 random bytes for userspace PRNG seeding.
-	 */
-	get_random_bytes(k_rand_bytes, sizeof(k_rand_bytes));
-	u_rand_bytes = elf_stack_alloc(&p, sizeof(k_rand_bytes));
-	if (__copy_to_user(u_rand_bytes, k_rand_bytes, sizeof(k_rand_bytes)))
-		return -EFAULT;
-
 	/* Create the ELF interpreter info */
 	elf_info = (elf_addr_t *)current->mm->saved_auxv;
 	/* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */
-- 
2.21.0


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

* [PATCH 3/3] binfmt/elf: randomize padding between ELF interp info
  2019-06-13 11:26 [PATCH 0/3] ELF interpretor info: align and add random padding Yann Droneaud
  2019-06-13 11:26 ` [PATCH 1/3] binfmt/elf: use functions for stack manipulation Yann Droneaud
  2019-06-13 11:26 ` [PATCH 2/3] binfmt/elf: align AT_RANDOM array Yann Droneaud
@ 2019-06-13 11:26 ` Yann Droneaud
  2019-06-13 13:39 ` [PATCH 4/3] binfmt/elf: don't expose prandom_u32() state Yann Droneaud
  3 siblings, 0 replies; 5+ messages in thread
From: Yann Droneaud @ 2019-06-13 11:26 UTC (permalink / raw)
  To: linux-kernel, kernel-hardening
  Cc: Andrew Morton, Kees Cook, Alexey Dobriyan, Yann Droneaud,
	Elena Reshetova

As AT_RANDOM is on top of the stack, retrieving AT_RANDOM value
through getauxval() could help, if needed, an attacker accesses
interesting locations in program stack, if offset from top of the
stack is fixed/known beforehand.

As a pure cargo-cult feature, inspired by "x86/entry/64: randomize
kernel stack offset upon syscall" [1], this patch adds small
random offsets between the top of the stack, AT_RANDOM array,
AT_PLAFORM strings, AT_BASE_PLATFORM strings, and the auxiliary
vector (aka. ELF interpretor info)

It introduces 16 possible different locations for AT_RANDOM array,
16 possible different locations for AT_PLATFORM, same for AT_BASE_PLATFORM,
and 16 more for the auxiliary vector.

Thus, at most 544 bytes (256 + 16 + 16 + 256) can be wasted in
padding.

This patch also enforces an 16bytes aligned AT_RANDOM array as
elf_stack_align() is used, regardless of arch_align_stack().

It should be noted, per ABI, it's not possible to insert random
padding between auxiliary vector and environment variables, nor
between environment variables and program arguments, nor before
programs arguments. (It should be possible to shuffle values
within auxillay and environment variables, if someone want to
scare userspace).

[1] https://www.openwall.com/lists/kernel-hardening/2019/03/29/3

Link: https://lore.kernel.org/lkml/cover.1560423331.git.ydroneaud@opteya.com
Cc: Elena Reshetova <elena.reshetova@intel.com>
Signed-off-by: Yann Droneaud <ydroneaud@opteya.com>
---
 fs/binfmt_elf.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index cfcc01fff4ae..c84ef81f0639 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -182,6 +182,26 @@ static inline elf_addr_t __user *elf_stack_alloc(unsigned long *pp,
 	return sp;
 }
 
+static inline void elf_stack_randomize(unsigned long *pp, size_t range)
+{
+	u32 offset;
+	unsigned long p;
+
+	if (!(current->flags & PF_RANDOMIZE))
+		return;
+
+	offset = prandom_u32() % range;
+	p = *pp;
+
+#ifdef CONFIG_STACK_GROWSUP
+	p += offset;
+#else
+	p -= offset;
+#endif
+
+	*pp = p;
+}
+
 #ifndef ELF_BASE_PLATFORM
 /*
  * AT_BASE_PLATFORM indicates the "real" hardware/microarchitecture.
@@ -219,6 +239,9 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 
 	p = arch_align_stack(p);
 
+	elf_stack_randomize(&p, 256);
+	elf_stack_align(&p);
+
 	/*
 	 * Generate 16 random bytes for userspace PRNG seeding.
 	 */
@@ -237,6 +260,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	if (k_platform) {
 		size_t len = strlen(k_platform) + 1;
 
+		elf_stack_randomize(&p, 16);
+
 		u_platform = elf_stack_alloc(&p, len);
 		if (__copy_to_user(u_platform, k_platform, len))
 			return -EFAULT;
@@ -250,11 +275,16 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	if (k_base_platform) {
 		size_t len = strlen(k_base_platform) + 1;
 
+		elf_stack_randomize(&p, 16);
+
 		u_base_platform = elf_stack_alloc(&p, len);
 		if (__copy_to_user(u_base_platform, k_base_platform, len))
 			return -EFAULT;
 	}
 
+	elf_stack_randomize(&p, 256);
+	elf_stack_align(&p);
+
 	/* Create the ELF interpreter info */
 	elf_info = (elf_addr_t *)current->mm->saved_auxv;
 	/* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */
-- 
2.21.0


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

* [PATCH 4/3] binfmt/elf: don't expose prandom_u32() state
  2019-06-13 11:26 [PATCH 0/3] ELF interpretor info: align and add random padding Yann Droneaud
                   ` (2 preceding siblings ...)
  2019-06-13 11:26 ` [PATCH 3/3] binfmt/elf: randomize padding between ELF interp info Yann Droneaud
@ 2019-06-13 13:39 ` Yann Droneaud
  3 siblings, 0 replies; 5+ messages in thread
From: Yann Droneaud @ 2019-06-13 13:39 UTC (permalink / raw)
  To: linux-kernel, kernel-hardening
  Cc: Andrew Morton, Kees Cook, Alexey Dobriyan, Yann Droneaud

Using prandom_u32() to get random offsets might expose fraction
of its internal state to userspace;

To prevent leaking prandom_u32() state, get_random_u32() could be
used instead, but with greater cost.

But it would be a big waste to call get_random_u32() to retrieve
only 4bits to 8bits at a time.

Instead this patch makes use of get_random_u64() to seed once a
local PRNG.

The local PRNG can be used safely to produces the random offsets,
exposing its internal state won't harm.

Link: https://lore.kernel.org/lkml/cover.1560423331.git.ydroneaud@opteya.com
Signed-off-by: Yann Droneaud <ydroneaud@opteya.com>
---
 fs/binfmt_elf.c | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index c84ef81f0639..9aaca1f671d1 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -182,7 +182,8 @@ static inline elf_addr_t __user *elf_stack_alloc(unsigned long *pp,
 	return sp;
 }
 
-static inline void elf_stack_randomize(unsigned long *pp, size_t range)
+static inline void elf_stack_randomize(unsigned long *pp,
+				       struct rnd_state *state, size_t range)
 {
 	u32 offset;
 	unsigned long p;
@@ -190,7 +191,7 @@ static inline void elf_stack_randomize(unsigned long *pp, size_t range)
 	if (!(current->flags & PF_RANDOMIZE))
 		return;
 
-	offset = prandom_u32() % range;
+	offset = prandom_u32_state(state) % range;
 	p = *pp;
 
 #ifdef CONFIG_STACK_GROWSUP
@@ -202,6 +203,15 @@ static inline void elf_stack_randomize(unsigned long *pp, size_t range)
 	*pp = p;
 }
 
+static inline void elf_stack_randomize_seed(struct rnd_state *state)
+{
+	if (!(current->flags & PF_RANDOMIZE))
+		return;
+
+	prandom_seed_state(state,
+			   get_random_u64());
+}
+
 #ifndef ELF_BASE_PLATFORM
 /*
  * AT_BASE_PLATFORM indicates the "real" hardware/microarchitecture.
@@ -230,6 +240,9 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	int ei_index = 0;
 	const struct cred *cred = current_cred();
 	struct vm_area_struct *vma;
+	struct rnd_state state;
+
+	elf_stack_randomize_seed(&state);
 
 	/*
 	 * In some cases (e.g. Hyper-Threading), we want to avoid L1
@@ -239,7 +252,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 
 	p = arch_align_stack(p);
 
-	elf_stack_randomize(&p, 256);
+	elf_stack_randomize(&p, &state, 256);
 	elf_stack_align(&p);
 
 	/*
@@ -260,7 +273,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	if (k_platform) {
 		size_t len = strlen(k_platform) + 1;
 
-		elf_stack_randomize(&p, 16);
+		elf_stack_randomize(&p, &state, 16);
 
 		u_platform = elf_stack_alloc(&p, len);
 		if (__copy_to_user(u_platform, k_platform, len))
@@ -275,14 +288,14 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 	if (k_base_platform) {
 		size_t len = strlen(k_base_platform) + 1;
 
-		elf_stack_randomize(&p, 16);
+		elf_stack_randomize(&p, &state, 16);
 
 		u_base_platform = elf_stack_alloc(&p, len);
 		if (__copy_to_user(u_base_platform, k_base_platform, len))
 			return -EFAULT;
 	}
 
-	elf_stack_randomize(&p, 256);
+	elf_stack_randomize(&p, &state, 256);
 	elf_stack_align(&p);
 
 	/* Create the ELF interpreter info */
-- 
2.21.0


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

end of thread, other threads:[~2019-06-13 15:29 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-13 11:26 [PATCH 0/3] ELF interpretor info: align and add random padding Yann Droneaud
2019-06-13 11:26 ` [PATCH 1/3] binfmt/elf: use functions for stack manipulation Yann Droneaud
2019-06-13 11:26 ` [PATCH 2/3] binfmt/elf: align AT_RANDOM array Yann Droneaud
2019-06-13 11:26 ` [PATCH 3/3] binfmt/elf: randomize padding between ELF interp info Yann Droneaud
2019-06-13 13:39 ` [PATCH 4/3] binfmt/elf: don't expose prandom_u32() state Yann Droneaud

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).