Re: [PATCH 2/2] arm: use fixmap for text patching when text is RO
From: Kees Cook
Date: Mon Apr 07 2014 - 14:04:19 EST
On Mon, Apr 7, 2014 at 6:57 AM, Jon Medhurst (Tixy) <tixy@xxxxxxxxxx> wrote:
> On Fri, 2014-04-04 at 23:27 +0200, Rabin Vincent wrote:
>> Use fixmaps for text patching when the kernel text is read-only,
>> inspired by x86. This makes jump labels and kprobes work with the
>> currently available CONFIG_DEBUG_SET_MODULE_RONX and the upcoming
>> CONFIG_DEBUG_RODATA options.
>>
>> Signed-off-by: Rabin Vincent <rabin@xxxxxx>
>> ---
>> arch/arm/include/asm/fixmap.h | 3 ++
>> arch/arm/kernel/jump_label.c | 2 +-
>> arch/arm/kernel/patch.c | 66 ++++++++++++++++++++++++++++++++++++++-----
>> arch/arm/kernel/patch.h | 12 +++++++-
>> 4 files changed, 74 insertions(+), 9 deletions(-)
>>
>> diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h
>> index 55ed076..79c1719 100644
>> --- a/arch/arm/include/asm/fixmap.h
>> +++ b/arch/arm/include/asm/fixmap.h
>> @@ -18,6 +18,9 @@
>> #define FIXADDR_TOP (FIXADDR_END - PAGE_SIZE)
>>
>> enum fixed_addresses {
>> + FIX_TEXT_POKE0,
>> + FIX_TEXT_POKE1,
>> +
>> FIX_KMAP_BEGIN,
>> FIX_KMAP_END = (FIXADDR_TOP - FIXADDR_START) >> PAGE_SHIFT,
>> __end_of_fixed_addresses
>> diff --git a/arch/arm/kernel/jump_label.c b/arch/arm/kernel/jump_label.c
>> index 4ce4f78..afeeb9e 100644
>> --- a/arch/arm/kernel/jump_label.c
>> +++ b/arch/arm/kernel/jump_label.c
>> @@ -19,7 +19,7 @@ static void __arch_jump_label_transform(struct jump_entry *entry,
>> insn = arm_gen_nop();
>>
>> if (is_static)
>> - __patch_text(addr, insn);
>> + __patch_text_early(addr, insn);
>> else
>> patch_text(addr, insn);
>> }
>> diff --git a/arch/arm/kernel/patch.c b/arch/arm/kernel/patch.c
>> index 07314af..761c5b6 100644
>> --- a/arch/arm/kernel/patch.c
>> +++ b/arch/arm/kernel/patch.c
>> @@ -1,8 +1,11 @@
>> #include <linux/kernel.h>
>> +#include <linux/spinlock.h>
>> #include <linux/kprobes.h>
>> +#include <linux/mm.h>
>> #include <linux/stop_machine.h>
>>
>> #include <asm/cacheflush.h>
>> +#include <asm/fixmap.h>
>> #include <asm/smp_plat.h>
>> #include <asm/opcodes.h>
>>
>> @@ -13,21 +16,67 @@ struct patch {
>> unsigned int insn;
>> };
>>
>> -void __kprobes __patch_text(void *addr, unsigned int insn)
>> +static DEFINE_SPINLOCK(patch_lock);
>> +
>> +static void __kprobes *patch_map(void *addr, int fixmap, unsigned long *flags)
>> +{
>> + unsigned int uintaddr = (uintptr_t) addr;
>> + bool module = !core_kernel_text(uintaddr);
>> + struct page *page;
>> +
>> + if (module && IS_ENABLED(CONFIG_DEBUG_SET_MODULE_RONX)) {
>> + page = vmalloc_to_page(addr);
>> + } else if (!module && IS_ENABLED(CONFIG_ARM_KERNMEM_PERMS)) {
I think this should probably be CONFIG_DEBUG_RODATA now that I've
moved RO exclusively into that config.
>> + page = virt_to_page(addr);
>> + } else {
>> + return addr;
>> + }
>
> I can't help but think that it'd be more maintainable to just always use
> fixmap, though that would obviously have some performance impact (not
> sure that particularly matters for text patching) and it would expose
> possible fixmap bugginess to more kernels (see my next comment...)
>
>> +
>> + if (flags)
>> + spin_lock_irqsave(&patch_lock, *flags);
>> +
>> + set_fixmap(fixmap, page_to_phys(page));
>> +
>> + return (void *) (__fix_to_virt(fixmap) + (uintaddr & ~PAGE_MASK));
>
> How does fixmap cope with cache colouring? Looking at the implementation
> it looks like it doesn't and so fixmap use on ARM is possibly buggy.
>
> For the text patching case where we know there are no writeable mappings
> [1] this should be OK if we used set_fixmap_nocache here, so long as we
> also invalidated the dcache later for the proper virtual address.
>
> [1] Can we know there are no writeable mappings though, the ftrace code
> modifying patches from Kees Cook have there own way of modifying text
> code permissions.
I think performance becomes an issue for massive updates like ftrace
seems to do, so I got the sense it was good to have the "bulk" way to
do it, and then this tiny poking way to do it.
-Kees
--
Kees Cook
Chrome OS Security
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/