[3/5] lkdtm: add function for testing .rodata section
diff mbox

Message ID 1465336628-18219-4-git-send-email-keescook@chromium.org
State New
Headers show

Commit Message

Kees Cook June 7, 2016, 9:57 p.m. UTC
This adds a function that lives in the .rodata section. The section
flags are corrected using objcopy since there is no way with gcc to
declare section flags in an architecture-agnostic way.

Signed-off-by: Kees Cook <keescook@chromium.org>
---
 drivers/misc/Makefile       |  7 +++++++
 drivers/misc/lkdtm.h        |  6 ++++++
 drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
 drivers/misc/lkdtm_rodata.c | 10 ++++++++++
 4 files changed, 40 insertions(+), 7 deletions(-)
 create mode 100644 drivers/misc/lkdtm.h
 create mode 100644 drivers/misc/lkdtm_rodata.c

Comments

Laura Abbott June 8, 2016, 1:02 a.m. UTC | #1
On 06/07/2016 02:57 PM, Kees Cook wrote:
> This adds a function that lives in the .rodata section. The section
> flags are corrected using objcopy since there is no way with gcc to
> declare section flags in an architecture-agnostic way.
>

Permit me to be the bearer of bad architecture news once again. With
arm64 cross compiler (both Fedora 6.1.1 and Linaro 5.1)

   CC      drivers/misc/lkdtm_rodata.o
   OBJCOPY drivers/misc/lkdtm_rodata_objcopy.o
   LD      drivers/misc/lkdtm.o
drivers/misc/lkdtm_rodata_objcopy.o: file not recognized: File format not recognized
scripts/Makefile.build:423: recipe for target 'drivers/misc/lkdtm.o' failed
make[2]: *** [drivers/misc/lkdtm.o] Error 1
scripts/Makefile.build:440: recipe for target 'drivers/misc' failed
make[1]: *** [drivers/misc] Error 2
Makefile:985: recipe for target 'drivers' failed
make: *** [drivers] Error 2


As far as I can tell this is because arm64 defines OBJCOPYFLAGS and they get
propagated to objcopy

aarch64-linux-gnu-objcopy -O binary -R .note -R .note.gnu.build-id -R .comment
	-S --set-section-flags .text=alloc,readonly
	--rename-section .text=.rodata drivers/misc/lkdtm_rodata.o drivers/misc/lkdtm_rodata_objcopy.o

vs x86

objcopy  --set-section-flags .text=alloc,readonly --rename-section .text=.rodata
	drivers/misc/lkdtm_rodata.o drivers/misc/lkdtm_rodata_objcopy.o


specifically it's the -O binary that seems to break things, the same failure
happens on x86 as well with the the same commands. It works if I clear out
the OBJCOPYFLAGS variable first but I don't think that's the correct way to
fix this.

Thanks,
Laura

> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
>  drivers/misc/Makefile       |  7 +++++++
>  drivers/misc/lkdtm.h        |  6 ++++++
>  drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
>  drivers/misc/lkdtm_rodata.c | 10 ++++++++++
>  4 files changed, 40 insertions(+), 7 deletions(-)
>  create mode 100644 drivers/misc/lkdtm.h
>  create mode 100644 drivers/misc/lkdtm_rodata.c
>
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index c3cb6ad8cc37..b2d3d68dfa22 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -59,3 +59,10 @@ obj-$(CONFIG_CXL_BASE)		+= cxl/
>  obj-$(CONFIG_PANEL)             += panel.o
>
>  lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
> +lkdtm-$(CONFIG_LKDTM)		+= lkdtm_rodata_objcopy.o
> +
> +OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
> +			--set-section-flags .text=alloc,readonly \
> +			--rename-section .text=.rodata
> +$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o
> +	$(call if_changed,objcopy)
> diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h
> new file mode 100644
> index 000000000000..9531fa3be4c3
> --- /dev/null
> +++ b/drivers/misc/lkdtm.h
> @@ -0,0 +1,6 @@
> +#ifndef __LKDTM_H
> +#define __LKDTM_H
> +
> +void lkdtm_rodata_do_nothing(void);
> +
> +#endif
> diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c
> index 605050c60f10..187cd9b63e9a 100644
> --- a/drivers/misc/lkdtm_core.c
> +++ b/drivers/misc/lkdtm_core.c
> @@ -52,6 +52,8 @@
>  #include <linux/ide.h>
>  #endif
>
> +#include "lkdtm.h"
> +
>  /*
>   * Make sure our attempts to over run the kernel stack doesn't trigger
>   * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we
> @@ -103,6 +105,7 @@ enum ctype {
>  	CT_EXEC_STACK,
>  	CT_EXEC_KMALLOC,
>  	CT_EXEC_VMALLOC,
> +	CT_EXEC_RODATA,
>  	CT_EXEC_USERSPACE,
>  	CT_ACCESS_USERSPACE,
>  	CT_WRITE_RO,
> @@ -145,6 +148,7 @@ static char* cp_type[] = {
>  	"EXEC_STACK",
>  	"EXEC_KMALLOC",
>  	"EXEC_VMALLOC",
> +	"EXEC_RODATA",
>  	"EXEC_USERSPACE",
>  	"ACCESS_USERSPACE",
>  	"WRITE_RO",
> @@ -346,15 +350,18 @@ static noinline void corrupt_stack(void)
>  	memset((void *)data, 0, 64);
>  }
>
> -static void noinline execute_location(void *dst)
> +static noinline void execute_location(void *dst, bool write)
>  {
>  	void (*func)(void) = dst;
>
>  	pr_info("attempting ok execution at %p\n", do_nothing);
>  	do_nothing();
>
> -	memcpy(dst, do_nothing, EXEC_SIZE);
> -	flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
> +	if (write) {
> +		memcpy(dst, do_nothing, EXEC_SIZE);
> +		flush_icache_range((unsigned long)dst,
> +				   (unsigned long)dst + EXEC_SIZE);
> +	}
>  	pr_info("attempting bad execution at %p\n", func);
>  	func();
>  }
> @@ -551,25 +558,28 @@ static void lkdtm_do_action(enum ctype which)
>  		schedule();
>  		break;
>  	case CT_EXEC_DATA:
> -		execute_location(data_area);
> +		execute_location(data_area, true);
>  		break;
>  	case CT_EXEC_STACK: {
>  		u8 stack_area[EXEC_SIZE];
> -		execute_location(stack_area);
> +		execute_location(stack_area, true);
>  		break;
>  	}
>  	case CT_EXEC_KMALLOC: {
>  		u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL);
> -		execute_location(kmalloc_area);
> +		execute_location(kmalloc_area, true);
>  		kfree(kmalloc_area);
>  		break;
>  	}
>  	case CT_EXEC_VMALLOC: {
>  		u32 *vmalloc_area = vmalloc(EXEC_SIZE);
> -		execute_location(vmalloc_area);
> +		execute_location(vmalloc_area, true);
>  		vfree(vmalloc_area);
>  		break;
>  	}
> +	case CT_EXEC_RODATA:
> +		execute_location(lkdtm_rodata_do_nothing, false);
> +		break;
>  	case CT_EXEC_USERSPACE: {
>  		unsigned long user_addr;
>
> diff --git a/drivers/misc/lkdtm_rodata.c b/drivers/misc/lkdtm_rodata.c
> new file mode 100644
> index 000000000000..4d0d851f02b9
> --- /dev/null
> +++ b/drivers/misc/lkdtm_rodata.c
> @@ -0,0 +1,10 @@
> +/*
> + * This includes functions that are meant to live entirely in .rodata
> + * (via objcopy tricks), to validate the non-executability of .rodata.
> + */
> +#include <linux/kernel.h>
> +
> +void lkdtm_rodata_do_nothing(void)
> +{
> +	/* Does nothing. We just want an architecture agnostic "return". */
> +}
>
Kees Cook June 8, 2016, 3:46 p.m. UTC | #2
On Tue, Jun 7, 2016 at 6:02 PM, Laura Abbott <labbott@redhat.com> wrote:
> On 06/07/2016 02:57 PM, Kees Cook wrote:
>>
>> This adds a function that lives in the .rodata section. The section
>> flags are corrected using objcopy since there is no way with gcc to
>> declare section flags in an architecture-agnostic way.
>>
>
> Permit me to be the bearer of bad architecture news once again. With
> arm64 cross compiler (both Fedora 6.1.1 and Linaro 5.1)
>
>   CC      drivers/misc/lkdtm_rodata.o
>   OBJCOPY drivers/misc/lkdtm_rodata_objcopy.o
>   LD      drivers/misc/lkdtm.o
> drivers/misc/lkdtm_rodata_objcopy.o: file not recognized: File format not
> recognized
> scripts/Makefile.build:423: recipe for target 'drivers/misc/lkdtm.o' failed
> make[2]: *** [drivers/misc/lkdtm.o] Error 1
> scripts/Makefile.build:440: recipe for target 'drivers/misc' failed
> make[1]: *** [drivers/misc] Error 2
> Makefile:985: recipe for target 'drivers' failed
> make: *** [drivers] Error 2
>
>
> As far as I can tell this is because arm64 defines OBJCOPYFLAGS and they get
> propagated to objcopy
>
> aarch64-linux-gnu-objcopy -O binary -R .note -R .note.gnu.build-id -R
> .comment
>         -S --set-section-flags .text=alloc,readonly
>         --rename-section .text=.rodata drivers/misc/lkdtm_rodata.o
> drivers/misc/lkdtm_rodata_objcopy.o
>
> vs x86
>
> objcopy  --set-section-flags .text=alloc,readonly --rename-section
> .text=.rodata
>         drivers/misc/lkdtm_rodata.o drivers/misc/lkdtm_rodata_objcopy.o
>
>
> specifically it's the -O binary that seems to break things, the same failure
> happens on x86 as well with the the same commands. It works if I clear out
> the OBJCOPYFLAGS variable first but I don't think that's the correct way to
> fix this.
>
> Thanks,
> Laura
>
>
>> Signed-off-by: Kees Cook <keescook@chromium.org>
>> ---
>>  drivers/misc/Makefile       |  7 +++++++
>>  drivers/misc/lkdtm.h        |  6 ++++++
>>  drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
>>  drivers/misc/lkdtm_rodata.c | 10 ++++++++++
>>  4 files changed, 40 insertions(+), 7 deletions(-)
>>  create mode 100644 drivers/misc/lkdtm.h
>>  create mode 100644 drivers/misc/lkdtm_rodata.c
>>
>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>> index c3cb6ad8cc37..b2d3d68dfa22 100644
>> --- a/drivers/misc/Makefile
>> +++ b/drivers/misc/Makefile
>> @@ -59,3 +59,10 @@ obj-$(CONFIG_CXL_BASE)               += cxl/
>>  obj-$(CONFIG_PANEL)             += panel.o
>>
>>  lkdtm-$(CONFIG_LKDTM)          += lkdtm_core.o
>> +lkdtm-$(CONFIG_LKDTM)          += lkdtm_rodata_objcopy.o
>> +
>> +OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
>> +                       --set-section-flags .text=alloc,readonly \
>> +                       --rename-section .text=.rodata
>> +$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o
>> +       $(call if_changed,objcopy)

Uhhhh... How is arm64 injecting those extra flags? OBJCOPYFLAGS is
being set with := here?

In related news I need to figure out how to get my cross-compiler
builds more well scripted...

-Kees
Laura Abbott June 8, 2016, 4:15 p.m. UTC | #3
On 06/08/2016 08:46 AM, Kees Cook wrote:
> On Tue, Jun 7, 2016 at 6:02 PM, Laura Abbott <labbott@redhat.com> wrote:
>> On 06/07/2016 02:57 PM, Kees Cook wrote:
>>>
>>> This adds a function that lives in the .rodata section. The section
>>> flags are corrected using objcopy since there is no way with gcc to
>>> declare section flags in an architecture-agnostic way.
>>>
>>
>> Permit me to be the bearer of bad architecture news once again. With
>> arm64 cross compiler (both Fedora 6.1.1 and Linaro 5.1)
>>
>>   CC      drivers/misc/lkdtm_rodata.o
>>   OBJCOPY drivers/misc/lkdtm_rodata_objcopy.o
>>   LD      drivers/misc/lkdtm.o
>> drivers/misc/lkdtm_rodata_objcopy.o: file not recognized: File format not
>> recognized
>> scripts/Makefile.build:423: recipe for target 'drivers/misc/lkdtm.o' failed
>> make[2]: *** [drivers/misc/lkdtm.o] Error 1
>> scripts/Makefile.build:440: recipe for target 'drivers/misc' failed
>> make[1]: *** [drivers/misc] Error 2
>> Makefile:985: recipe for target 'drivers' failed
>> make: *** [drivers] Error 2
>>
>>
>> As far as I can tell this is because arm64 defines OBJCOPYFLAGS and they get
>> propagated to objcopy
>>
>> aarch64-linux-gnu-objcopy -O binary -R .note -R .note.gnu.build-id -R
>> .comment
>>         -S --set-section-flags .text=alloc,readonly
>>         --rename-section .text=.rodata drivers/misc/lkdtm_rodata.o
>> drivers/misc/lkdtm_rodata_objcopy.o
>>
>> vs x86
>>
>> objcopy  --set-section-flags .text=alloc,readonly --rename-section
>> .text=.rodata
>>         drivers/misc/lkdtm_rodata.o drivers/misc/lkdtm_rodata_objcopy.o
>>
>>
>> specifically it's the -O binary that seems to break things, the same failure
>> happens on x86 as well with the the same commands. It works if I clear out
>> the OBJCOPYFLAGS variable first but I don't think that's the correct way to
>> fix this.
>>
>> Thanks,
>> Laura
>>
>>
>>> Signed-off-by: Kees Cook <keescook@chromium.org>
>>> ---
>>>  drivers/misc/Makefile       |  7 +++++++
>>>  drivers/misc/lkdtm.h        |  6 ++++++
>>>  drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
>>>  drivers/misc/lkdtm_rodata.c | 10 ++++++++++
>>>  4 files changed, 40 insertions(+), 7 deletions(-)
>>>  create mode 100644 drivers/misc/lkdtm.h
>>>  create mode 100644 drivers/misc/lkdtm_rodata.c
>>>
>>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>>> index c3cb6ad8cc37..b2d3d68dfa22 100644
>>> --- a/drivers/misc/Makefile
>>> +++ b/drivers/misc/Makefile
>>> @@ -59,3 +59,10 @@ obj-$(CONFIG_CXL_BASE)               += cxl/
>>>  obj-$(CONFIG_PANEL)             += panel.o
>>>
>>>  lkdtm-$(CONFIG_LKDTM)          += lkdtm_core.o
>>> +lkdtm-$(CONFIG_LKDTM)          += lkdtm_rodata_objcopy.o
>>> +
>>> +OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
>>> +                       --set-section-flags .text=alloc,readonly \
>>> +                       --rename-section .text=.rodata
>>> +$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o
>>> +       $(call if_changed,objcopy)
>
> Uhhhh... How is arm64 injecting those extra flags? OBJCOPYFLAGS is
> being set with := here?
>

Looks like intended behavior from scripts/Makefile.lib:

# Objcopy
# ---------------------------------------------------------------------------

quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@

OBJCOPYFLAGS_$@ is separate from OBJCOPYFLAGS which makes sense for the
original intentions although not what we want here. Some Makefile
wizardry is probably needed.

> In related news I need to figure out how to get my cross-compiler
> builds more well scripted...
>
> -Kees
>
Kees Cook June 8, 2016, 9:26 p.m. UTC | #4
On Wed, Jun 8, 2016 at 8:46 AM, Kees Cook <keescook@chromium.org> wrote:
> On Tue, Jun 7, 2016 at 6:02 PM, Laura Abbott <labbott@redhat.com> wrote:
>> On 06/07/2016 02:57 PM, Kees Cook wrote:
>>>
>>> This adds a function that lives in the .rodata section. The section
>>> flags are corrected using objcopy since there is no way with gcc to
>>> declare section flags in an architecture-agnostic way.
>>>
>>
>> Permit me to be the bearer of bad architecture news once again. With
>> arm64 cross compiler (both Fedora 6.1.1 and Linaro 5.1)
>>
>>   CC      drivers/misc/lkdtm_rodata.o
>>   OBJCOPY drivers/misc/lkdtm_rodata_objcopy.o
>>   LD      drivers/misc/lkdtm.o
>> drivers/misc/lkdtm_rodata_objcopy.o: file not recognized: File format not
>> recognized
>> scripts/Makefile.build:423: recipe for target 'drivers/misc/lkdtm.o' failed
>> make[2]: *** [drivers/misc/lkdtm.o] Error 1
>> scripts/Makefile.build:440: recipe for target 'drivers/misc' failed
>> make[1]: *** [drivers/misc] Error 2
>> Makefile:985: recipe for target 'drivers' failed
>> make: *** [drivers] Error 2
>>
>>
>> As far as I can tell this is because arm64 defines OBJCOPYFLAGS and they get
>> propagated to objcopy
>>
>> aarch64-linux-gnu-objcopy -O binary -R .note -R .note.gnu.build-id -R
>> .comment
>>         -S --set-section-flags .text=alloc,readonly
>>         --rename-section .text=.rodata drivers/misc/lkdtm_rodata.o
>> drivers/misc/lkdtm_rodata_objcopy.o
>>
>> vs x86
>>
>> objcopy  --set-section-flags .text=alloc,readonly --rename-section
>> .text=.rodata
>>         drivers/misc/lkdtm_rodata.o drivers/misc/lkdtm_rodata_objcopy.o
>>
>>
>> specifically it's the -O binary that seems to break things, the same failure
>> happens on x86 as well with the the same commands. It works if I clear out
>> the OBJCOPYFLAGS variable first but I don't think that's the correct way to
>> fix this.
>>
>> Thanks,
>> Laura
>>
>>
>>> Signed-off-by: Kees Cook <keescook@chromium.org>
>>> ---
>>>  drivers/misc/Makefile       |  7 +++++++
>>>  drivers/misc/lkdtm.h        |  6 ++++++
>>>  drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
>>>  drivers/misc/lkdtm_rodata.c | 10 ++++++++++
>>>  4 files changed, 40 insertions(+), 7 deletions(-)
>>>  create mode 100644 drivers/misc/lkdtm.h
>>>  create mode 100644 drivers/misc/lkdtm_rodata.c
>>>
>>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>>> index c3cb6ad8cc37..b2d3d68dfa22 100644
>>> --- a/drivers/misc/Makefile
>>> +++ b/drivers/misc/Makefile
>>> @@ -59,3 +59,10 @@ obj-$(CONFIG_CXL_BASE)               += cxl/
>>>  obj-$(CONFIG_PANEL)             += panel.o
>>>
>>>  lkdtm-$(CONFIG_LKDTM)          += lkdtm_core.o
>>> +lkdtm-$(CONFIG_LKDTM)          += lkdtm_rodata_objcopy.o
>>> +

If I add:

OBJCOPYFLAGS :=

here, it seems to fix things...

>>> +OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
>>> +                       --set-section-flags .text=alloc,readonly \
>>> +                       --rename-section .text=.rodata
>>> +$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o
>>> +       $(call if_changed,objcopy)
>
> Uhhhh... How is arm64 injecting those extra flags? OBJCOPYFLAGS is
> being set with := here?
>
> In related news I need to figure out how to get my cross-compiler
> builds more well scripted...
>
> -Kees
>
> --
> Kees Cook
> Chrome OS & Brillo Security
Laura Abbott June 10, 2016, 1:14 a.m. UTC | #5
On 06/08/2016 02:26 PM, Kees Cook wrote:
> On Wed, Jun 8, 2016 at 8:46 AM, Kees Cook <keescook@chromium.org> wrote:
>> On Tue, Jun 7, 2016 at 6:02 PM, Laura Abbott <labbott@redhat.com> wrote:
>>> On 06/07/2016 02:57 PM, Kees Cook wrote:
>>>>
>>>> This adds a function that lives in the .rodata section. The section
>>>> flags are corrected using objcopy since there is no way with gcc to
>>>> declare section flags in an architecture-agnostic way.
>>>>
>>>
>>> Permit me to be the bearer of bad architecture news once again. With
>>> arm64 cross compiler (both Fedora 6.1.1 and Linaro 5.1)
>>>
>>>   CC      drivers/misc/lkdtm_rodata.o
>>>   OBJCOPY drivers/misc/lkdtm_rodata_objcopy.o
>>>   LD      drivers/misc/lkdtm.o
>>> drivers/misc/lkdtm_rodata_objcopy.o: file not recognized: File format not
>>> recognized
>>> scripts/Makefile.build:423: recipe for target 'drivers/misc/lkdtm.o' failed
>>> make[2]: *** [drivers/misc/lkdtm.o] Error 1
>>> scripts/Makefile.build:440: recipe for target 'drivers/misc' failed
>>> make[1]: *** [drivers/misc] Error 2
>>> Makefile:985: recipe for target 'drivers' failed
>>> make: *** [drivers] Error 2
>>>
>>>
>>> As far as I can tell this is because arm64 defines OBJCOPYFLAGS and they get
>>> propagated to objcopy
>>>
>>> aarch64-linux-gnu-objcopy -O binary -R .note -R .note.gnu.build-id -R
>>> .comment
>>>         -S --set-section-flags .text=alloc,readonly
>>>         --rename-section .text=.rodata drivers/misc/lkdtm_rodata.o
>>> drivers/misc/lkdtm_rodata_objcopy.o
>>>
>>> vs x86
>>>
>>> objcopy  --set-section-flags .text=alloc,readonly --rename-section
>>> .text=.rodata
>>>         drivers/misc/lkdtm_rodata.o drivers/misc/lkdtm_rodata_objcopy.o
>>>
>>>
>>> specifically it's the -O binary that seems to break things, the same failure
>>> happens on x86 as well with the the same commands. It works if I clear out
>>> the OBJCOPYFLAGS variable first but I don't think that's the correct way to
>>> fix this.
>>>
>>> Thanks,
>>> Laura
>>>
>>>
>>>> Signed-off-by: Kees Cook <keescook@chromium.org>
>>>> ---
>>>>  drivers/misc/Makefile       |  7 +++++++
>>>>  drivers/misc/lkdtm.h        |  6 ++++++
>>>>  drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
>>>>  drivers/misc/lkdtm_rodata.c | 10 ++++++++++
>>>>  4 files changed, 40 insertions(+), 7 deletions(-)
>>>>  create mode 100644 drivers/misc/lkdtm.h
>>>>  create mode 100644 drivers/misc/lkdtm_rodata.c
>>>>
>>>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>>>> index c3cb6ad8cc37..b2d3d68dfa22 100644
>>>> --- a/drivers/misc/Makefile
>>>> +++ b/drivers/misc/Makefile
>>>> @@ -59,3 +59,10 @@ obj-$(CONFIG_CXL_BASE)               += cxl/
>>>>  obj-$(CONFIG_PANEL)             += panel.o
>>>>
>>>>  lkdtm-$(CONFIG_LKDTM)          += lkdtm_core.o
>>>> +lkdtm-$(CONFIG_LKDTM)          += lkdtm_rodata_objcopy.o
>>>> +
>
> If I add:
>
> OBJCOPYFLAGS :=
>
> here, it seems to fix things...
>

I can confirm that this works on arm64

# echo EXEC_RODATA > /sys/kernel/debug/provoke-crash/DIRECT
[   21.725514] lkdtm: Performing direct entry EXEC_RODATA
[   21.725890] lkdtm: attempting ok execution at ffff0000084c0e08
[   21.726030] lkdtm: attempting bad execution at ffff000008880700
[   21.726401] Bad mode in Synchronous Abort handler detected on CPU2, code 0x8400000e -- IABT (current EL)
[   21.726847] CPU: 2 PID: 998 Comm: sh Not tainted 4.7.0-rc2+ #13

I wish the exception was clearer what the actual error was. I might propose a
patch to make it more obvious.

Thanks,
Laura
Michael Ellerman Aug. 1, 2016, 12:37 p.m. UTC | #6
Kees Cook <keescook@chromium.org> writes:

> This adds a function that lives in the .rodata section. The section
> flags are corrected using objcopy since there is no way with gcc to
> declare section flags in an architecture-agnostic way.
>
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
>  drivers/misc/Makefile       |  7 +++++++
>  drivers/misc/lkdtm.h        |  6 ++++++
>  drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
>  drivers/misc/lkdtm_rodata.c | 10 ++++++++++
>  4 files changed, 40 insertions(+), 7 deletions(-)
>  create mode 100644 drivers/misc/lkdtm.h
>  create mode 100644 drivers/misc/lkdtm_rodata.c

This is blowing up my linker :(

  scripts/link-vmlinux.sh: line 52: 36260 Segmentation fault      (core dumped) ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} -T ${lds} ${KBUILD_VMLINUX_INIT} --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}

Haven't had a chance to debug it further.

cheers
Kees Cook Aug. 1, 2016, 7:45 p.m. UTC | #7
On Mon, Aug 1, 2016 at 5:37 AM, Michael Ellerman <mpe@ellerman.id.au> wrote:
> Kees Cook <keescook@chromium.org> writes:
>
>> This adds a function that lives in the .rodata section. The section
>> flags are corrected using objcopy since there is no way with gcc to
>> declare section flags in an architecture-agnostic way.
>>
>> Signed-off-by: Kees Cook <keescook@chromium.org>
>> ---
>>  drivers/misc/Makefile       |  7 +++++++
>>  drivers/misc/lkdtm.h        |  6 ++++++
>>  drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
>>  drivers/misc/lkdtm_rodata.c | 10 ++++++++++
>>  4 files changed, 40 insertions(+), 7 deletions(-)
>>  create mode 100644 drivers/misc/lkdtm.h
>>  create mode 100644 drivers/misc/lkdtm_rodata.c
>
> This is blowing up my linker :(
>
>   scripts/link-vmlinux.sh: line 52: 36260 Segmentation fault      (core dumped) ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} -T ${lds} ${KBUILD_VMLINUX_INIT} --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
>
> Haven't had a chance to debug it further.

Argh. Do you want a quick fix for this now? I can add a PPC CONFIG
blacklist for the rodata check, maybe?

Also, what version of gcc? I'll see if I can reproduce this with a
cross compiler...

-Kees
Michael Ellerman Aug. 2, 2016, 3:12 a.m. UTC | #8
Kees Cook <keescook@chromium.org> writes:

> On Mon, Aug 1, 2016 at 5:37 AM, Michael Ellerman <mpe@ellerman.id.au> wrote:
>> Kees Cook <keescook@chromium.org> writes:
>>
>>> This adds a function that lives in the .rodata section. The section
>>> flags are corrected using objcopy since there is no way with gcc to
>>> declare section flags in an architecture-agnostic way.
>>>
>>> Signed-off-by: Kees Cook <keescook@chromium.org>
>>> ---
>>>  drivers/misc/Makefile       |  7 +++++++
>>>  drivers/misc/lkdtm.h        |  6 ++++++
>>>  drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
>>>  drivers/misc/lkdtm_rodata.c | 10 ++++++++++
>>>  4 files changed, 40 insertions(+), 7 deletions(-)
>>>  create mode 100644 drivers/misc/lkdtm.h
>>>  create mode 100644 drivers/misc/lkdtm_rodata.c
>>
>> This is blowing up my linker :(
>>
>>   scripts/link-vmlinux.sh: line 52: 36260 Segmentation fault      (core dumped) ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} -T ${lds} ${KBUILD_VMLINUX_INIT} --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
>>
>> Haven't had a chance to debug it further.
>
> Argh. Do you want a quick fix for this now? I can add a PPC CONFIG
> blacklist for the rodata check, maybe?

Nah that's OK, none of our defconfigs have it enabled so it's not a real
blocker. It also builds OK as a module - though I haven't tested the
result yet.

> Also, what version of gcc? I'll see if I can reproduce this with a
> cross compiler...

The original hit was with gcc-5.3 (which is actually a x86->ppc cross):

  http://kisskb.ellerman.id.au/kisskb/buildresult/12762730/

But I can also reproduce with 5.4, and 6.1.0.

Interestingly I *can't* reproduce with the Ubuntu x86->ppc cross
(5.4.0-6ubuntu1~16.04.1).

Those toolchains are all using binutils 2.26 AFAIK.

Going back to a really old toolchain (gcc 4.6.3/binutils 2.22) it does
build but I get these warnings:

 powerpc64-linux-ld: drivers/misc/built-in.o: .opd is not a regular array of opd entries
 powerpc64-linux-ld: drivers/built-in.o: .opd is not a regular array of opd entries
 powerpc64-linux-ld: drivers/built-in.o: .opd is not a regular array of opd entries
 powerpc64-linux-ld: drivers/built-in.o: .opd is not a regular array of opd entries
 powerpc64-linux-ld: drivers/built-in.o: .opd is not a regular array of opd entries


So probably don't worry about it and we'll try and work it out on our end.

cheers
Kees Cook Aug. 2, 2016, 7:12 p.m. UTC | #9
On Mon, Aug 1, 2016 at 8:12 PM, Michael Ellerman <mpe@ellerman.id.au> wrote:
> Kees Cook <keescook@chromium.org> writes:
>
>> On Mon, Aug 1, 2016 at 5:37 AM, Michael Ellerman <mpe@ellerman.id.au> wrote:
>>> Kees Cook <keescook@chromium.org> writes:
>>>
>>>> This adds a function that lives in the .rodata section. The section
>>>> flags are corrected using objcopy since there is no way with gcc to
>>>> declare section flags in an architecture-agnostic way.
>>>>
>>>> Signed-off-by: Kees Cook <keescook@chromium.org>
>>>> ---
>>>>  drivers/misc/Makefile       |  7 +++++++
>>>>  drivers/misc/lkdtm.h        |  6 ++++++
>>>>  drivers/misc/lkdtm_core.c   | 24 +++++++++++++++++-------
>>>>  drivers/misc/lkdtm_rodata.c | 10 ++++++++++
>>>>  4 files changed, 40 insertions(+), 7 deletions(-)
>>>>  create mode 100644 drivers/misc/lkdtm.h
>>>>  create mode 100644 drivers/misc/lkdtm_rodata.c
>>>
>>> This is blowing up my linker :(
>>>
>>>   scripts/link-vmlinux.sh: line 52: 36260 Segmentation fault      (core dumped) ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} -T ${lds} ${KBUILD_VMLINUX_INIT} --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
>>>
>>> Haven't had a chance to debug it further.
>>
>> Argh. Do you want a quick fix for this now? I can add a PPC CONFIG
>> blacklist for the rodata check, maybe?
>
> Nah that's OK, none of our defconfigs have it enabled so it's not a real
> blocker. It also builds OK as a module - though I haven't tested the
> result yet.

Okay, I'll leave it as is.

>> Also, what version of gcc? I'll see if I can reproduce this with a
>> cross compiler...
>
> The original hit was with gcc-5.3 (which is actually a x86->ppc cross):
>
>   http://kisskb.ellerman.id.au/kisskb/buildresult/12762730/
>
> But I can also reproduce with 5.4, and 6.1.0.
>
> Interestingly I *can't* reproduce with the Ubuntu x86->ppc cross
> (5.4.0-6ubuntu1~16.04.1).

Oh, weird. Well, that does explains my lack of hitting the problem,
though: that's the cross compiler I was using. :P

> Those toolchains are all using binutils 2.26 AFAIK.

I wonder if this is some gold vs bfd issue, or a specific bug that got
fixed in the Ubuntu tree but hasn't landed in 6.1 or 5.4 (??)

> Going back to a really old toolchain (gcc 4.6.3/binutils 2.22) it does
> build but I get these warnings:
>
>  powerpc64-linux-ld: drivers/misc/built-in.o: .opd is not a regular array of opd entries
>  powerpc64-linux-ld: drivers/built-in.o: .opd is not a regular array of opd entries

How strange. I wonder if there's some corner case of the objcopy that
is wrong...

> So probably don't worry about it and we'll try and work it out on our end.

Okay, sounds good.

-Kees
Michael Ellerman Aug. 3, 2016, 4:46 a.m. UTC | #10
Kees Cook <keescook@chromium.org> writes:
> On Mon, Aug 1, 2016 at 8:12 PM, Michael Ellerman <mpe@ellerman.id.au> wrote:
>> Kees Cook <keescook@chromium.org> writes:
>>> On Mon, Aug 1, 2016 at 5:37 AM, Michael Ellerman <mpe@ellerman.id.au> wrote:
>>>>
>>>>   scripts/link-vmlinux.sh: line 52: 36260 Segmentation fault      (core dumped) ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} -T ${lds} ${KBUILD_VMLINUX_INIT} --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
>>>>
>>>> Haven't had a chance to debug it further.
...
>> Interestingly I *can't* reproduce with the Ubuntu x86->ppc cross
>> (5.4.0-6ubuntu1~16.04.1).
>
> Oh, weird. Well, that does explains my lack of hitting the problem,
> though: that's the cross compiler I was using. :P

Actually that was a false negative.

The trick is you have to have LKDTM=y *and* FUNCTION_TRACER=y.

It is a linker bug:

  https://sourceware.org/bugzilla/show_bug.cgi?id=20428

Which Alan has already fixed.

But we need to workaround existing linkers that are out there.

We can do that by marking lkdtm_rodata_do_nothing() notrace, which I
think makes sense for all arches actually.

So I'll send you a patch to do that.

cheers

Patch
diff mbox

diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c3cb6ad8cc37..b2d3d68dfa22 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -59,3 +59,10 @@  obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_PANEL)             += panel.o
 
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
+lkdtm-$(CONFIG_LKDTM)		+= lkdtm_rodata_objcopy.o
+
+OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
+			--set-section-flags .text=alloc,readonly \
+			--rename-section .text=.rodata
+$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o
+	$(call if_changed,objcopy)
diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h
new file mode 100644
index 000000000000..9531fa3be4c3
--- /dev/null
+++ b/drivers/misc/lkdtm.h
@@ -0,0 +1,6 @@ 
+#ifndef __LKDTM_H
+#define __LKDTM_H
+
+void lkdtm_rodata_do_nothing(void);
+
+#endif
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c
index 605050c60f10..187cd9b63e9a 100644
--- a/drivers/misc/lkdtm_core.c
+++ b/drivers/misc/lkdtm_core.c
@@ -52,6 +52,8 @@ 
 #include <linux/ide.h>
 #endif
 
+#include "lkdtm.h"
+
 /*
  * Make sure our attempts to over run the kernel stack doesn't trigger
  * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we
@@ -103,6 +105,7 @@  enum ctype {
 	CT_EXEC_STACK,
 	CT_EXEC_KMALLOC,
 	CT_EXEC_VMALLOC,
+	CT_EXEC_RODATA,
 	CT_EXEC_USERSPACE,
 	CT_ACCESS_USERSPACE,
 	CT_WRITE_RO,
@@ -145,6 +148,7 @@  static char* cp_type[] = {
 	"EXEC_STACK",
 	"EXEC_KMALLOC",
 	"EXEC_VMALLOC",
+	"EXEC_RODATA",
 	"EXEC_USERSPACE",
 	"ACCESS_USERSPACE",
 	"WRITE_RO",
@@ -346,15 +350,18 @@  static noinline void corrupt_stack(void)
 	memset((void *)data, 0, 64);
 }
 
-static void noinline execute_location(void *dst)
+static noinline void execute_location(void *dst, bool write)
 {
 	void (*func)(void) = dst;
 
 	pr_info("attempting ok execution at %p\n", do_nothing);
 	do_nothing();
 
-	memcpy(dst, do_nothing, EXEC_SIZE);
-	flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
+	if (write) {
+		memcpy(dst, do_nothing, EXEC_SIZE);
+		flush_icache_range((unsigned long)dst,
+				   (unsigned long)dst + EXEC_SIZE);
+	}
 	pr_info("attempting bad execution at %p\n", func);
 	func();
 }
@@ -551,25 +558,28 @@  static void lkdtm_do_action(enum ctype which)
 		schedule();
 		break;
 	case CT_EXEC_DATA:
-		execute_location(data_area);
+		execute_location(data_area, true);
 		break;
 	case CT_EXEC_STACK: {
 		u8 stack_area[EXEC_SIZE];
-		execute_location(stack_area);
+		execute_location(stack_area, true);
 		break;
 	}
 	case CT_EXEC_KMALLOC: {
 		u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL);
-		execute_location(kmalloc_area);
+		execute_location(kmalloc_area, true);
 		kfree(kmalloc_area);
 		break;
 	}
 	case CT_EXEC_VMALLOC: {
 		u32 *vmalloc_area = vmalloc(EXEC_SIZE);
-		execute_location(vmalloc_area);
+		execute_location(vmalloc_area, true);
 		vfree(vmalloc_area);
 		break;
 	}
+	case CT_EXEC_RODATA:
+		execute_location(lkdtm_rodata_do_nothing, false);
+		break;
 	case CT_EXEC_USERSPACE: {
 		unsigned long user_addr;
 
diff --git a/drivers/misc/lkdtm_rodata.c b/drivers/misc/lkdtm_rodata.c
new file mode 100644
index 000000000000..4d0d851f02b9
--- /dev/null
+++ b/drivers/misc/lkdtm_rodata.c
@@ -0,0 +1,10 @@ 
+/*
+ * This includes functions that are meant to live entirely in .rodata
+ * (via objcopy tricks), to validate the non-executability of .rodata.
+ */
+#include <linux/kernel.h>
+
+void lkdtm_rodata_do_nothing(void)
+{
+	/* Does nothing. We just want an architecture agnostic "return". */
+}