Skip to content

Commit 2f896d5

Browse files
labbottctmarinas
authored andcommitted
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 <[email protected]> Reviewed-by: Mark Rutland <[email protected]> Tested-by: Kees Cook <[email protected]> Tested-by: Mark Rutland <[email protected]> Signed-off-by: Laura Abbott <[email protected]> Signed-off-by: Catalin Marinas <[email protected]>
1 parent 6083fe7 commit 2f896d5

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

arch/arm64/include/asm/fixmap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ enum fixed_addresses {
4949

5050
FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
5151
FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,
52+
FIX_TEXT_POKE0,
5253
__end_of_fixed_addresses
5354
};
5455

arch/arm64/kernel/insn.c

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,19 @@
1717
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
*/
1919
#include <linux/bitops.h>
20+
#include <linux/bug.h>
2021
#include <linux/compiler.h>
2122
#include <linux/kernel.h>
23+
#include <linux/mm.h>
2224
#include <linux/smp.h>
25+
#include <linux/spinlock.h>
2326
#include <linux/stop_machine.h>
27+
#include <linux/types.h>
2428
#include <linux/uaccess.h>
2529

2630
#include <asm/cacheflush.h>
2731
#include <asm/debug-monitors.h>
32+
#include <asm/fixmap.h>
2833
#include <asm/insn.h>
2934

3035
#define AARCH64_INSN_SF_BIT BIT(31)
@@ -72,6 +77,29 @@ bool __kprobes aarch64_insn_is_nop(u32 insn)
7277
}
7378
}
7479

80+
static DEFINE_SPINLOCK(patch_lock);
81+
82+
static 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+
99+
static void __kprobes patch_unmap(int fixmap)
100+
{
101+
clear_fixmap(fixmap);
102+
}
75103
/*
76104
* In ARMv8-A, A64 instructions have a fixed length of 32 bits and are always
77105
* little-endian.
@@ -88,10 +116,27 @@ int __kprobes aarch64_insn_read(void *addr, u32 *insnp)
88116
return ret;
89117
}
90118

119+
static 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+
91136
int __kprobes aarch64_insn_write(void *addr, u32 insn)
92137
{
93138
insn = cpu_to_le32(insn);
94-
return probe_kernel_write(addr, &insn, AARCH64_INSN_SIZE);
139+
return __aarch64_insn_write(addr, insn);
95140
}
96141

97142
static bool __kprobes __aarch64_insn_hotpatch_safe(u32 insn)

0 commit comments

Comments
 (0)