aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaura Abbott <lauraa@codeaurora.org>2015-01-21 20:36:05 -0500
committerCatalin Marinas <catalin.marinas@arm.com>2015-01-22 06:50:56 -0500
commit2f896d5866107e2926dcdec34a7d40bc56dd2951 (patch)
tree45269256d0e928bcc51838ce63f2913abe252480
parent6083fe74b7bfffc2c7be8c711596608bda0cda6e (diff)
arm64: use fixmap for text patching
When kernel text is marked as read only, it cannot be modified directly. Use a fixmap to modify the text instead in a similar manner to x86 and arm. Reviewed-by: Kees Cook <keescook@chromium.org> Reviewed-by: Mark Rutland <mark.rutland@arm.com> Tested-by: Kees Cook <keescook@chromium.org> Tested-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Laura Abbott <lauraa@codeaurora.org> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
-rw-r--r--arch/arm64/include/asm/fixmap.h1
-rw-r--r--arch/arm64/kernel/insn.c47
2 files changed, 47 insertions, 1 deletions
diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h
index 9ef6eca905ca..defa0ff98250 100644
--- a/arch/arm64/include/asm/fixmap.h
+++ b/arch/arm64/include/asm/fixmap.h
@@ -49,6 +49,7 @@ enum fixed_addresses {
49 49
50 FIX_BTMAP_END = __end_of_permanent_fixed_addresses, 50 FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
51 FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1, 51 FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,
52 FIX_TEXT_POKE0,
52 __end_of_fixed_addresses 53 __end_of_fixed_addresses
53}; 54};
54 55
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 7e9327a0986d..27d4864577e5 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -17,14 +17,19 @@
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */ 18 */
19#include <linux/bitops.h> 19#include <linux/bitops.h>
20#include <linux/bug.h>
20#include <linux/compiler.h> 21#include <linux/compiler.h>
21#include <linux/kernel.h> 22#include <linux/kernel.h>
23#include <linux/mm.h>
22#include <linux/smp.h> 24#include <linux/smp.h>
25#include <linux/spinlock.h>
23#include <linux/stop_machine.h> 26#include <linux/stop_machine.h>
27#include <linux/types.h>
24#include <linux/uaccess.h> 28#include <linux/uaccess.h>
25 29
26#include <asm/cacheflush.h> 30#include <asm/cacheflush.h>
27#include <asm/debug-monitors.h> 31#include <asm/debug-monitors.h>
32#include <asm/fixmap.h>
28#include <asm/insn.h> 33#include <asm/insn.h>
29 34
30#define AARCH64_INSN_SF_BIT BIT(31) 35#define AARCH64_INSN_SF_BIT BIT(31)
@@ -72,6 +77,29 @@ bool __kprobes aarch64_insn_is_nop(u32 insn)
72 } 77 }
73} 78}
74 79
80static DEFINE_SPINLOCK(patch_lock);
81
82static void __kprobes *patch_map(void *addr, int fixmap)
83{
84 unsigned long uintaddr = (uintptr_t) addr;
85 bool module = !core_kernel_text(uintaddr);
86 struct page *page;
87
88 if (module && IS_ENABLED(CONFIG_DEBUG_SET_MODULE_RONX))
89 page = vmalloc_to_page(addr);
90 else
91 page = virt_to_page(addr);
92
93 BUG_ON(!page);
94 set_fixmap(fixmap, page_to_phys(page));
95
96 return (void *) (__fix_to_virt(fixmap) + (uintaddr & ~PAGE_MASK));
97}
98
99static void __kprobes patch_unmap(int fixmap)
100{
101 clear_fixmap(fixmap);
102}
75/* 103/*
76 * In ARMv8-A, A64 instructions have a fixed length of 32 bits and are always 104 * In ARMv8-A, A64 instructions have a fixed length of 32 bits and are always
77 * little-endian. 105 * little-endian.
@@ -88,10 +116,27 @@ int __kprobes aarch64_insn_read(void *addr, u32 *insnp)
88 return ret; 116 return ret;
89} 117}
90 118
119static int __kprobes __aarch64_insn_write(void *addr, u32 insn)
120{
121 void *waddr = addr;
122 unsigned long flags = 0;
123 int ret;
124
125 spin_lock_irqsave(&patch_lock, flags);
126 waddr = patch_map(addr, FIX_TEXT_POKE0);
127
128 ret = probe_kernel_write(waddr, &insn, AARCH64_INSN_SIZE);
129
130 patch_unmap(FIX_TEXT_POKE0);
131 spin_unlock_irqrestore(&patch_lock, flags);
132
133 return ret;
134}
135
91int __kprobes aarch64_insn_write(void *addr, u32 insn) 136int __kprobes aarch64_insn_write(void *addr, u32 insn)
92{ 137{
93 insn = cpu_to_le32(insn); 138 insn = cpu_to_le32(insn);
94 return probe_kernel_write(addr, &insn, AARCH64_INSN_SIZE); 139 return __aarch64_insn_write(addr, insn);
95} 140}
96 141
97static bool __kprobes __aarch64_insn_hotpatch_safe(u32 insn) 142static bool __kprobes __aarch64_insn_hotpatch_safe(u32 insn)