[PATCH 2/2] arm: use fixmap for text patching when text is RO

Kees Cook keescook at chromium.org
Mon Apr 7 11:04:06 PDT 2014


On Mon, Apr 7, 2014 at 6:57 AM, Jon Medhurst (Tixy) <tixy at linaro.org> 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 at rab.in>
>> ---
>>  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



More information about the linux-arm-kernel mailing list