Message ID | e2a42afbce47b364bf790b4cf8edf76235e48d53.1583741997.git.zong.li@sifive.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Support strict kernel memory permissions for security | expand |
Hi Zong, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on v5.6-rc5] [also build test WARNING on next-20200306] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Zong-Li/Support-strict-kernel-memory-permissions-for-security/20200309-172554 base: 2c523b344dfa65a3738e7039832044aa133c75fb config: riscv-allnoconfig (attached as .config) compiler: riscv64-linux-gcc (GCC) 7.5.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree GCC_VERSION=7.5.0 make.cross ARCH=riscv If you fix the issue, kindly add following tag Reported-by: kbuild test robot <lkp@intel.com> All warnings (new ones prefixed by >>): In file included from include/linux/spinlock.h:318:0, from arch/riscv/kernel/patch.c:6: arch/riscv/kernel/patch.c: In function 'riscv_insn_write': arch/riscv/kernel/patch.c:63:25: error: 'patch_lock' undeclared (first use in this function); did you mean 'patch_map'? raw_spin_lock_irqsave(&patch_lock, flags); ^ include/linux/spinlock_api_up.h:28:32: note: in definition of macro '___LOCK' do { __acquire(lock); (void)(lock); } while (0) ^~~~ >> include/linux/spinlock_api_up.h:40:31: note: in expansion of macro '__LOCK' do { local_irq_save(flags); __LOCK(lock); } while (0) ^~~~~~ >> include/linux/spinlock_api_up.h:68:45: note: in expansion of macro '__LOCK_IRQSAVE' #define _raw_spin_lock_irqsave(lock, flags) __LOCK_IRQSAVE(lock, flags) ^~~~~~~~~~~~~~ >> include/linux/spinlock.h:272:3: note: in expansion of macro '_raw_spin_lock_irqsave' _raw_spin_lock_irqsave(lock, flags); \ ^~~~~~~~~~~~~~~~~~~~~~ >> arch/riscv/kernel/patch.c:63:2: note: in expansion of macro 'raw_spin_lock_irqsave' raw_spin_lock_irqsave(&patch_lock, flags); ^~~~~~~~~~~~~~~~~~~~~ arch/riscv/kernel/patch.c:63:25: note: each undeclared identifier is reported only once for each function it appears in raw_spin_lock_irqsave(&patch_lock, flags); ^ include/linux/spinlock_api_up.h:28:32: note: in definition of macro '___LOCK' do { __acquire(lock); (void)(lock); } while (0) ^~~~ >> include/linux/spinlock_api_up.h:40:31: note: in expansion of macro '__LOCK' do { local_irq_save(flags); __LOCK(lock); } while (0) ^~~~~~ >> include/linux/spinlock_api_up.h:68:45: note: in expansion of macro '__LOCK_IRQSAVE' #define _raw_spin_lock_irqsave(lock, flags) __LOCK_IRQSAVE(lock, flags) ^~~~~~~~~~~~~~ >> include/linux/spinlock.h:272:3: note: in expansion of macro '_raw_spin_lock_irqsave' _raw_spin_lock_irqsave(lock, flags); \ ^~~~~~~~~~~~~~~~~~~~~~ >> arch/riscv/kernel/patch.c:63:2: note: in expansion of macro 'raw_spin_lock_irqsave' raw_spin_lock_irqsave(&patch_lock, flags); ^~~~~~~~~~~~~~~~~~~~~ arch/riscv/kernel/patch.c:66:25: error: 'FIX_TEXT_POKE1' undeclared (first use in this function) patch_map(addr + len, FIX_TEXT_POKE1); ^~~~~~~~~~~~~~ arch/riscv/kernel/patch.c:68:26: error: 'FIX_TEXT_POKE0' undeclared (first use in this function); did you mean 'FIX_TEXT_POKE1'? waddr = patch_map(addr, FIX_TEXT_POKE0); ^~~~~~~~~~~~~~ FIX_TEXT_POKE1 -- In file included from include/linux/spinlock.h:318:0, from arch/riscv//kernel/patch.c:6: arch/riscv//kernel/patch.c: In function 'riscv_insn_write': arch/riscv//kernel/patch.c:63:25: error: 'patch_lock' undeclared (first use in this function); did you mean 'patch_map'? raw_spin_lock_irqsave(&patch_lock, flags); ^ include/linux/spinlock_api_up.h:28:32: note: in definition of macro '___LOCK' do { __acquire(lock); (void)(lock); } while (0) ^~~~ >> include/linux/spinlock_api_up.h:40:31: note: in expansion of macro '__LOCK' do { local_irq_save(flags); __LOCK(lock); } while (0) ^~~~~~ >> include/linux/spinlock_api_up.h:68:45: note: in expansion of macro '__LOCK_IRQSAVE' #define _raw_spin_lock_irqsave(lock, flags) __LOCK_IRQSAVE(lock, flags) ^~~~~~~~~~~~~~ >> include/linux/spinlock.h:272:3: note: in expansion of macro '_raw_spin_lock_irqsave' _raw_spin_lock_irqsave(lock, flags); \ ^~~~~~~~~~~~~~~~~~~~~~ arch/riscv//kernel/patch.c:63:2: note: in expansion of macro 'raw_spin_lock_irqsave' raw_spin_lock_irqsave(&patch_lock, flags); ^~~~~~~~~~~~~~~~~~~~~ arch/riscv//kernel/patch.c:63:25: note: each undeclared identifier is reported only once for each function it appears in raw_spin_lock_irqsave(&patch_lock, flags); ^ include/linux/spinlock_api_up.h:28:32: note: in definition of macro '___LOCK' do { __acquire(lock); (void)(lock); } while (0) ^~~~ >> include/linux/spinlock_api_up.h:40:31: note: in expansion of macro '__LOCK' do { local_irq_save(flags); __LOCK(lock); } while (0) ^~~~~~ >> include/linux/spinlock_api_up.h:68:45: note: in expansion of macro '__LOCK_IRQSAVE' #define _raw_spin_lock_irqsave(lock, flags) __LOCK_IRQSAVE(lock, flags) ^~~~~~~~~~~~~~ >> include/linux/spinlock.h:272:3: note: in expansion of macro '_raw_spin_lock_irqsave' _raw_spin_lock_irqsave(lock, flags); \ ^~~~~~~~~~~~~~~~~~~~~~ arch/riscv//kernel/patch.c:63:2: note: in expansion of macro 'raw_spin_lock_irqsave' raw_spin_lock_irqsave(&patch_lock, flags); ^~~~~~~~~~~~~~~~~~~~~ arch/riscv//kernel/patch.c:66:25: error: 'FIX_TEXT_POKE1' undeclared (first use in this function) patch_map(addr + len, FIX_TEXT_POKE1); ^~~~~~~~~~~~~~ arch/riscv//kernel/patch.c:68:26: error: 'FIX_TEXT_POKE0' undeclared (first use in this function); did you mean 'FIX_TEXT_POKE1'? waddr = patch_map(addr, FIX_TEXT_POKE0); ^~~~~~~~~~~~~~ FIX_TEXT_POKE1 vim +/_raw_spin_lock_irqsave +272 include/linux/spinlock.h b8e6ec865fd1d8 Linus Torvalds 2006-11-26 268 c2f21ce2e31286 Thomas Gleixner 2009-12-02 269 #define raw_spin_lock_irqsave(lock, flags) \ 3f307891ce0e7b Steven Rostedt 2008-07-25 270 do { \ 3f307891ce0e7b Steven Rostedt 2008-07-25 271 typecheck(unsigned long, flags); \ 9c1721aa4994f6 Thomas Gleixner 2009-12-03 @272 _raw_spin_lock_irqsave(lock, flags); \ 3f307891ce0e7b Steven Rostedt 2008-07-25 273 } while (0) ef12f10994281e Thomas Gleixner 2009-11-07 274 :::::: The code at line 272 was first introduced by commit :::::: 9c1721aa4994f6625decbd915241f3a94ee2fe67 locking: Cleanup the name space completely :::::: TO: Thomas Gleixner <tglx@linutronix.de> :::::: CC: Thomas Gleixner <tglx@linutronix.de> --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi Zong, Thank you for the patch! Yet something to improve: [auto build test ERROR on v5.6-rc5] [also build test ERROR on next-20200306] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Zong-Li/Support-strict-kernel-memory-permissions-for-security/20200309-172554 base: 2c523b344dfa65a3738e7039832044aa133c75fb config: riscv-nommu_virt_defconfig (attached as .config) compiler: riscv64-linux-gcc (GCC) 7.5.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree GCC_VERSION=7.5.0 make.cross ARCH=riscv If you fix the issue, kindly add following tag Reported-by: kbuild test robot <lkp@intel.com> All errors (new ones prefixed by >>): In file included from arch/riscv//kernel/patch.c:6:0: arch/riscv//kernel/patch.c: In function 'riscv_insn_write': >> arch/riscv//kernel/patch.c:63:25: error: 'patch_lock' undeclared (first use in this function); did you mean 'patch_map'? raw_spin_lock_irqsave(&patch_lock, flags); ^ include/linux/spinlock.h:250:34: note: in definition of macro 'raw_spin_lock_irqsave' flags = _raw_spin_lock_irqsave(lock); \ ^~~~ arch/riscv//kernel/patch.c:63:25: note: each undeclared identifier is reported only once for each function it appears in raw_spin_lock_irqsave(&patch_lock, flags); ^ include/linux/spinlock.h:250:34: note: in definition of macro 'raw_spin_lock_irqsave' flags = _raw_spin_lock_irqsave(lock); \ ^~~~ >> arch/riscv//kernel/patch.c:66:25: error: 'FIX_TEXT_POKE1' undeclared (first use in this function) patch_map(addr + len, FIX_TEXT_POKE1); ^~~~~~~~~~~~~~ >> arch/riscv//kernel/patch.c:68:26: error: 'FIX_TEXT_POKE0' undeclared (first use in this function); did you mean 'FIX_TEXT_POKE1'? waddr = patch_map(addr, FIX_TEXT_POKE0); ^~~~~~~~~~~~~~ FIX_TEXT_POKE1 vim +63 arch/riscv//kernel/patch.c 55 56 static int __kprobes riscv_insn_write(void *addr, const void *insn, size_t len) 57 { 58 void *waddr = addr; 59 bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE; 60 unsigned long flags = 0; 61 int ret; 62 > 63 raw_spin_lock_irqsave(&patch_lock, flags); 64 65 if (across_pages) > 66 patch_map(addr + len, FIX_TEXT_POKE1); 67 > 68 waddr = patch_map(addr, FIX_TEXT_POKE0); 69 70 ret = probe_kernel_write(waddr, insn, len); 71 72 patch_unmap(FIX_TEXT_POKE0); 73 74 if (across_pages) 75 patch_unmap(FIX_TEXT_POKE1); 76 77 raw_spin_unlock_irqrestore(&patch_lock, flags); 78 79 return ret; 80 } 81 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff --git a/arch/riscv/include/asm/fixmap.h b/arch/riscv/include/asm/fixmap.h index 42d2c42f3cc9..2368d49eb4ef 100644 --- a/arch/riscv/include/asm/fixmap.h +++ b/arch/riscv/include/asm/fixmap.h @@ -27,6 +27,8 @@ enum fixed_addresses { FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1, FIX_PTE, FIX_PMD, + FIX_TEXT_POKE1, + FIX_TEXT_POKE0, FIX_EARLYCON_MEM_BASE, __end_of_fixed_addresses }; diff --git a/arch/riscv/include/asm/patch.h b/arch/riscv/include/asm/patch.h new file mode 100644 index 000000000000..b5918a6e0615 --- /dev/null +++ b/arch/riscv/include/asm/patch.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 SiFive + */ + +#ifndef _ASM_RISCV_PATCH_H +#define _ASM_RISCV_PATCH_H + +int riscv_patch_text_nosync(void *addr, const void *insns, size_t len); +int riscv_patch_text(void *addr, u32 insn); + +#endif /* _ASM_RISCV_PATCH_H */ diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index f40205cb9a22..d189bd3d8501 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -4,7 +4,8 @@ # ifdef CONFIG_FTRACE -CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_patch.o = -pg endif extra-y += head.o @@ -26,6 +27,7 @@ obj-y += traps.o obj-y += riscv_ksyms.o obj-y += stacktrace.o obj-y += cacheinfo.o +obj-y += patch.o obj-$(CONFIG_MMU) += vdso.o vdso/ obj-$(CONFIG_RISCV_M_MODE) += clint.o diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c new file mode 100644 index 000000000000..1c61a8595839 --- /dev/null +++ b/arch/riscv/kernel/patch.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 SiFive + */ + +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/uaccess.h> +#include <linux/stop_machine.h> +#include <asm/kprobes.h> +#include <asm/cacheflush.h> +#include <asm/fixmap.h> + +struct riscv_insn_patch { + void *addr; + u32 insn; + atomic_t cpu_count; +}; + +#ifdef CONFIG_MMU +static DEFINE_RAW_SPINLOCK(patch_lock); + +static void __kprobes *patch_map(void *addr, int fixmap) +{ + uintptr_t uintaddr = (uintptr_t) addr; + struct page *page; + + if (core_kernel_text(uintaddr)) + page = phys_to_page(__pa_symbol(addr)); + else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) + page = vmalloc_to_page(addr); + else + return addr; + + BUG_ON(!page); + + return (void *)set_fixmap_offset(fixmap, page_to_phys(page) + + (uintaddr & ~PAGE_MASK)); +} + +static void __kprobes patch_unmap(int fixmap) +{ + clear_fixmap(fixmap); +} +#else +static void __kprobes *patch_map(void *addr, int fixmap) +{ + return addr; +} + +static void __kprobes patch_unmap(int fixmap) +{ +} +#endif /* CONFIG_MMU */ + +static int __kprobes riscv_insn_write(void *addr, const void *insn, size_t len) +{ + void *waddr = addr; + bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE; + unsigned long flags = 0; + int ret; + + raw_spin_lock_irqsave(&patch_lock, flags); + + if (across_pages) + patch_map(addr + len, FIX_TEXT_POKE1); + + waddr = patch_map(addr, FIX_TEXT_POKE0); + + ret = probe_kernel_write(waddr, insn, len); + + patch_unmap(FIX_TEXT_POKE0); + + if (across_pages) + patch_unmap(FIX_TEXT_POKE1); + + raw_spin_unlock_irqrestore(&patch_lock, flags); + + return ret; +} + +int __kprobes riscv_patch_text_nosync(void *addr, const void *insns, size_t len) +{ + u32 *tp = addr; + int ret; + + ret = riscv_insn_write(tp, insns, len); + + if (!ret) + flush_icache_range((uintptr_t) tp, (uintptr_t) tp + len); + + return ret; +} + +static int __kprobes riscv_patch_text_cb(void *data) +{ + struct riscv_insn_patch *patch = data; + int ret = 0; + + if (atomic_inc_return(&patch->cpu_count) == 1) { + ret = + riscv_patch_text_nosync(patch->addr, &patch->insn, + GET_INSN_LENGTH(patch->insn)); + atomic_inc(&patch->cpu_count); + } else { + while (atomic_read(&patch->cpu_count) <= num_online_cpus()) + cpu_relax(); + smp_mb(); + } + + return ret; +} + +int __kprobes riscv_patch_text(void *addr, u32 insn) +{ + struct riscv_insn_patch patch = { + .addr = addr, + .insn = insn, + .cpu_count = ATOMIC_INIT(0), + }; + + return stop_machine_cpuslocked(riscv_patch_text_cb, + &patch, cpu_online_mask); +}
On strict kernel memory permission, we couldn't patch code without writable permission. Preserve two holes in fixmap area, so we can map the kernel code temporarily to fixmap area, then patch the instructions. We need two pages here because we support the compressed instruction, so the instruction might be align to 2 bytes. When patching the 32-bit length instruction which is 2 bytes alignment, it will across two pages. Introduce two interfaces to patch kernel code: riscv_patch_text_nosync: - patch code without synchronization, it's caller's responsibility to synchronize all CPUs if needed. riscv_patch_text: - patch code and always synchronize with stop_machine() Signed-off-by: Zong Li <zong.li@sifive.com> --- arch/riscv/include/asm/fixmap.h | 2 + arch/riscv/include/asm/patch.h | 12 ++++ arch/riscv/kernel/Makefile | 4 +- arch/riscv/kernel/patch.c | 124 ++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/include/asm/patch.h create mode 100644 arch/riscv/kernel/patch.c