2019-04-18 07:48:35

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH] x86_64: Disabling read-implies-exec when the stack is executable

On Wed, May 11, 2016 at 5:45 AM Hector Marco-Gisbert <[email protected]> wrote:
>
> The READ_IMPLIES_EXEC personality was removed in 2005 for 64-bit processes,
> (commit a3cc2546a54361b86b73557df5b85c4fc3fc27c3 form history.git).
>
> But it's still possible to have all readable areas with EXEC permissions by
> setting the stack as executable in 64-bit ELF executables (also in 32-bit).
>
> This is because the macro elf_read_implies_exec() does not distinguish
> between 32 and 64-bit executables: when the stack is executable then the
> read-implies-exec personality is set (enabled) to the process.
>
> We think that this is not a desirable behaviour, maybe read-implies-exec
> could be used via personality but not by setting the stack as executable in
> the ELF.
>
> For x86_64 processes, is there any reason to disable read-implies-exec
> personality and at the same time enable it when the stack is executable ?
>
> With this patch it's no longer possible to enable the read-implies-exec on
> the process by setting the stack as executable in the PT_GNU_STACK on
> x86_64 executables.
>
> Regarding 32-bits processes, is there any reason to enable
> read-implies-exec by setting the stack as executable instead of using the
> personality on X86_32 or when emulating IA32 on X86_64 ?
>
> If not, I could re-send the patch which removes this possibility.
>
>
> Signed-off-by: Hector Marco-Gisbert <[email protected]>
> Acked-by: Ismael Ripoll Ripoll <[email protected]>
> ---
> arch/x86/include/asm/elf.h | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
> index 15340e3..87fd15e 100644
> --- a/arch/x86/include/asm/elf.h
> +++ b/arch/x86/include/asm/elf.h
> @@ -271,8 +271,8 @@ extern int force_personality32;
> * An executable for which elf_read_implies_exec() returns TRUE will
> * have the READ_IMPLIES_EXEC personality flag set automatically.
> */
> -#define elf_read_implies_exec(ex, executable_stack) \
> - (executable_stack != EXSTACK_DISABLE_X)
> +#define elf_read_implies_exec(ex, executable_stack) \
> + (mmap_is_ia32() ? (executable_stack != EXSTACK_DISABLE_X) : 0)
>
> struct task_struct;
>
> --
> 1.9.1
>

*thread necromancy*

I'd still like to see this get landed. READ_IMPLIES_EXEC is way too
powerful (it impacts, for example, mmap() regions of device driver
memory, forcing drivers to not be able to disallow VM_EXEC[1]).

The only case it could break is on an AMD K8 (Athlon only, I assume?),
which seems unlikely to have a modern kernel run on it. If there is
still concern, then we could just test against the NX CPU feature:

diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index 69c0f892e310..367cd36259a4 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -280,10 +280,12 @@ extern u32 elf_hwcap2;

/*
* An executable for which elf_read_implies_exec() returns TRUE will
- * have the READ_IMPLIES_EXEC personality flag set automatically.
+ * have the READ_IMPLIES_EXEC personality flag set automatically when
+ * a CPU did not support NX or is using a 32-bit memory layout.
*/
-#define elf_read_implies_exec(ex, executable_stack) \
- (executable_stack != EXSTACK_DISABLE_X)
+#define elf_read_implies_exec(ex, executable_stack) \
+ (mmap_is_ia32() || !(__supported_pte_mask & _PAGE_NX) ? \
+ (executable_stack != EXSTACK_DISABLE_X) : 0)

struct task_struct;


Additionally, I think architectures that always had NX (arm64) should
entirely remove their elf_read_implies_exec() macro (defaulting to the
removal of the stack-marking-based READ_IMPLIES_EXEC enabling):

diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index 6adc1a90e7e6..87c2dd468eea 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -107,8 +107,6 @@
*/
#define elf_check_arch(x) ((x)->e_machine == EM_AARCH64)

-#define elf_read_implies_exec(ex,stk) (stk != EXSTACK_DISABLE_X)
-
#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE PAGE_SIZE


Thoughts?

-Kees

[1] https://lkml.kernel.org/r/[email protected]

--
Kees Cook


2019-04-18 08:18:43

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH] x86_64: Disabling read-implies-exec when the stack is executable

On Thu, 18 Apr 2019, Kees Cook wrote:
> On Wed, May 11, 2016 at 5:45 AM Hector Marco-Gisbert <[email protected]> wrote:
> *thread necromancy*
>
> I'd still like to see this get landed. READ_IMPLIES_EXEC is way too
> powerful (it impacts, for example, mmap() regions of device driver
> memory, forcing drivers to not be able to disallow VM_EXEC[1]).
>
> The only case it could break is on an AMD K8 (Athlon only, I assume?),
> which seems unlikely to have a modern kernel run on it. If there is
> still concern, then we could just test against the NX CPU feature:
>
> diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
> index 69c0f892e310..367cd36259a4 100644
> --- a/arch/x86/include/asm/elf.h
> +++ b/arch/x86/include/asm/elf.h
> @@ -280,10 +280,12 @@ extern u32 elf_hwcap2;
>
> /*
> * An executable for which elf_read_implies_exec() returns TRUE will
> - * have the READ_IMPLIES_EXEC personality flag set automatically.
> + * have the READ_IMPLIES_EXEC personality flag set automatically when
> + * a CPU did not support NX or is using a 32-bit memory layout.
> */
> -#define elf_read_implies_exec(ex, executable_stack) \
> - (executable_stack != EXSTACK_DISABLE_X)
> +#define elf_read_implies_exec(ex, executable_stack) \
> + (mmap_is_ia32() || !(__supported_pte_mask & _PAGE_NX) ? \

What's special about ia32? All what matters is whether PAGE_NX is supported
or not. That has nothing to do with 32/64bit unless I'm missing something
(as usual).

Thanks,

tglx

2019-04-18 14:14:02

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH] x86_64: Disabling read-implies-exec when the stack is executable

On Thu, Apr 18, 2019 at 3:17 AM Thomas Gleixner <[email protected]> wrote:
>
> On Thu, 18 Apr 2019, Kees Cook wrote:
> > On Wed, May 11, 2016 at 5:45 AM Hector Marco-Gisbert <[email protected]> wrote:
> > *thread necromancy*
> >
> > I'd still like to see this get landed. READ_IMPLIES_EXEC is way too
> > powerful (it impacts, for example, mmap() regions of device driver
> > memory, forcing drivers to not be able to disallow VM_EXEC[1]).
> >
> > The only case it could break is on an AMD K8 (Athlon only, I assume?),
> > which seems unlikely to have a modern kernel run on it. If there is
> > still concern, then we could just test against the NX CPU feature:
> >
> > diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
> > index 69c0f892e310..367cd36259a4 100644
> > --- a/arch/x86/include/asm/elf.h
> > +++ b/arch/x86/include/asm/elf.h
> > @@ -280,10 +280,12 @@ extern u32 elf_hwcap2;
> >
> > /*
> > * An executable for which elf_read_implies_exec() returns TRUE will
> > - * have the READ_IMPLIES_EXEC personality flag set automatically.
> > + * have the READ_IMPLIES_EXEC personality flag set automatically when
> > + * a CPU did not support NX or is using a 32-bit memory layout.
> > */
> > -#define elf_read_implies_exec(ex, executable_stack) \
> > - (executable_stack != EXSTACK_DISABLE_X)
> > +#define elf_read_implies_exec(ex, executable_stack) \
> > + (mmap_is_ia32() || !(__supported_pte_mask & _PAGE_NX) ? \
>
> What's special about ia32? All what matters is whether PAGE_NX is supported
> or not. That has nothing to do with 32/64bit unless I'm missing something
> (as usual).

I think the reasoning was that older toolchains from ia32 wouldn't
provide any permissions marking at all, and this was a signal that it
might need executable heaps as well as stack. So, since ia32 gained NX
along the way, lacking the gnu-stack marking was a reasonable
indicator that the ELF needed this crazy setting to effectively
disable NX for the ELF.

But yes, just looking at the NX feature should cover this.

One thing I'd also like to adjust is the logic about the gnu-stack
existing and requesting an executable stack should apply only to the
stack, and not trigger READ_IMPLIES_EXEC. i.e. if the ELF has
gnu-stack marking of any kind, it should never flag READ_IMPLIES_EXEC.
It would, of course, continue to control the stack perms.

And if this is wrong ... well, we'll find out how and we can more
correctly document this. So how about:

#define elf_read_implies_exec(ex, executable_stack) \
(!(__supported_pte_mask & _PAGE_NX) && \
(executable_stack == EXSTACK_DEFAULT))


--
Kees Cook

2019-04-18 14:16:07

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH] x86_64: Disabling read-implies-exec when the stack is executable


> On Apr 18, 2019, at 1:17 AM, Thomas Gleixner <[email protected]> wrote:
>
>> On Thu, 18 Apr 2019, Kees Cook wrote:
>> On Wed, May 11, 2016 at 5:45 AM Hector Marco-Gisbert <[email protected]> wrote:
>> *thread necromancy*
>>
>> I'd still like to see this get landed. READ_IMPLIES_EXEC is way too
>> powerful (it impacts, for example, mmap() regions of device driver
>> memory, forcing drivers to not be able to disallow VM_EXEC[1]).
>>
>> The only case it could break is on an AMD K8 (Athlon only, I assume?),
>> which seems unlikely to have a modern kernel run on it. If there is
>> still concern, then we could just test against the NX CPU feature:
>>
>> diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
>> index 69c0f892e310..367cd36259a4 100644
>> --- a/arch/x86/include/asm/elf.h
>> +++ b/arch/x86/include/asm/elf.h
>> @@ -280,10 +280,12 @@ extern u32 elf_hwcap2;
>>
>> /*
>> * An executable for which elf_read_implies_exec() returns TRUE will
>> - * have the READ_IMPLIES_EXEC personality flag set automatically.
>> + * have the READ_IMPLIES_EXEC personality flag set automatically when
>> + * a CPU did not support NX or is using a 32-bit memory layout.
>> */
>> -#define elf_read_implies_exec(ex, executable_stack) \
>> - (executable_stack != EXSTACK_DISABLE_X)
>> +#define elf_read_implies_exec(ex, executable_stack) \
>> + (mmap_is_ia32() || !(__supported_pte_mask & _PAGE_NX) ? \
>
> What's special about ia32? All what matters is whether PAGE_NX is supported
> or not. That has nothing to do with 32/64bit unless I'm missing something
> (as usual).
>
>

I have the opposite question: who cares if we have NX? On a CPU without NX, read implies exec, full stop. Why should nasty personality stuff matter at all? The personality stuff is about supporting old crufty binaries.

So: are there old 64-bit binaries that have their stacks marked RX that expect mmap to automatically return X memory? If so, then the patch is a problem. If not, then maybe the patch is okay.

All that being said, the comment in the patch seems to be highly misleading. If the patch is to be applied, the comment needs serious work.

2019-04-18 14:39:14

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH] x86_64: Disabling read-implies-exec when the stack is executable

On Thu, Apr 18, 2019 at 9:15 AM Andy Lutomirski <[email protected]> wrote:
> I have the opposite question: who cares if we have NX? On a CPU without NX, read implies exec, full stop. Why should nasty personality stuff matter at all? The personality stuff is about supporting old crufty binaries.
>
> So: are there old 64-bit binaries that have their stacks marked RX that expect mmap to automatically return X memory? If so, then the patch is a problem. If not, then maybe the patch is okay.

That's what I'm wondering too. (Though remember that ia32 PAE has NX,
so it's also 32-bit binaries.) The matrix I have in my head is:

CPU: | lacks NX | has NX |
ELF: | | |
--------------------------------------------------------|
missing GNU_STACK | doesn't matter | needs RIE |
GNU_STACK == RWX | doesn't matter | needs only stack X | *
GNU_STACK == RW | doesn't matter | needs stack NX |

(hopefully gmail doesn't mangle this whitespace)

The "*" line here is the question. The question is "when does
GNU_STACK == RWX also mean all mmaps must be X?" If it's only on ia32,
okay, fine we can adjust it, but why is it only an issue for ia32
toolchains? If it's a non-issue, then the above logic stands.

> All that being said, the comment in the patch seems to be highly misleading. If the patch is to be applied, the comment needs serious work.

Yes, absolutely. (I'd include the chart above, for example...)

--
Kees Cook