Skip to content

Commit 6efbea7

Browse files
committed
Merge tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
Pull arm64 fixes from Will Deacon: - Disable software tag-based KASAN when compiling with GCC, as functions are incorrectly instrumented leading to a crash early during boot - Fix pkey configuration for kernel threads when POE is enabled - Fix invalid memory accesses in uprobes when targetting load-literal instructions * tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: kasan: Disable Software Tag-Based KASAN with GCC Documentation/protection-keys: add AArch64 to documentation arm64: set POR_EL0 for kernel threads arm64: probes: Fix uprobes for big-endian kernels arm64: probes: Fix simulate_ldr*_literal() arm64: probes: Remove broken LDR (literal) uprobe support
2 parents c16e5c9 + 7aed6a2 commit 6efbea7

File tree

7 files changed

+61
-33
lines changed

7 files changed

+61
-33
lines changed

Documentation/core-api/protection-keys.rst

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ Pkeys Userspace (PKU) is a feature which can be found on:
1212
* Intel server CPUs, Skylake and later
1313
* Intel client CPUs, Tiger Lake (11th Gen Core) and later
1414
* Future AMD CPUs
15+
* arm64 CPUs implementing the Permission Overlay Extension (FEAT_S1POE)
1516

17+
x86_64
18+
======
1619
Pkeys work by dedicating 4 previously Reserved bits in each page table entry to
1720
a "protection key", giving 16 possible keys.
1821

@@ -28,6 +31,22 @@ register. The feature is only available in 64-bit mode, even though there is
2831
theoretically space in the PAE PTEs. These permissions are enforced on data
2932
access only and have no effect on instruction fetches.
3033

34+
arm64
35+
=====
36+
37+
Pkeys use 3 bits in each page table entry, to encode a "protection key index",
38+
giving 8 possible keys.
39+
40+
Protections for each key are defined with a per-CPU user-writable system
41+
register (POR_EL0). This is a 64-bit register encoding read, write and execute
42+
overlay permissions for each protection key index.
43+
44+
Being a CPU register, POR_EL0 is inherently thread-local, potentially giving
45+
each thread a different set of protections from every other thread.
46+
47+
Unlike x86_64, the protection key permissions also apply to instruction
48+
fetches.
49+
3150
Syscalls
3251
========
3352

@@ -38,11 +57,10 @@ There are 3 system calls which directly interact with pkeys::
3857
int pkey_mprotect(unsigned long start, size_t len,
3958
unsigned long prot, int pkey);
4059

41-
Before a pkey can be used, it must first be allocated with
42-
pkey_alloc(). An application calls the WRPKRU instruction
43-
directly in order to change access permissions to memory covered
44-
with a key. In this example WRPKRU is wrapped by a C function
45-
called pkey_set().
60+
Before a pkey can be used, it must first be allocated with pkey_alloc(). An
61+
application writes to the architecture specific CPU register directly in order
62+
to change access permissions to memory covered with a key. In this example
63+
this is wrapped by a C function called pkey_set().
4664
::
4765

4866
int real_prot = PROT_READ|PROT_WRITE;
@@ -64,9 +82,9 @@ is no longer in use::
6482
munmap(ptr, PAGE_SIZE);
6583
pkey_free(pkey);
6684

67-
.. note:: pkey_set() is a wrapper for the RDPKRU and WRPKRU instructions.
68-
An example implementation can be found in
69-
tools/testing/selftests/x86/protection_keys.c.
85+
.. note:: pkey_set() is a wrapper around writing to the CPU register.
86+
Example implementations can be found in
87+
tools/testing/selftests/mm/pkey-{arm64,powerpc,x86}.h
7088

7189
Behavior
7290
========
@@ -96,3 +114,7 @@ with a read()::
96114
The kernel will send a SIGSEGV in both cases, but si_code will be set
97115
to SEGV_PKERR when violating protection keys versus SEGV_ACCERR when
98116
the plain mprotect() permissions are violated.
117+
118+
Note that kernel accesses from a kthread (such as io_uring) will use a default
119+
value for the protection key register and so will not be consistent with
120+
userspace's value of the register or mprotect().

arch/arm64/include/asm/uprobes.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010
#include <asm/insn.h>
1111
#include <asm/probes.h>
1212

13-
#define MAX_UINSN_BYTES AARCH64_INSN_SIZE
14-
1513
#define UPROBE_SWBP_INSN cpu_to_le32(BRK64_OPCODE_UPROBES)
1614
#define UPROBE_SWBP_INSN_SIZE AARCH64_INSN_SIZE
17-
#define UPROBE_XOL_SLOT_BYTES MAX_UINSN_BYTES
15+
#define UPROBE_XOL_SLOT_BYTES AARCH64_INSN_SIZE
1816

1917
typedef __le32 uprobe_opcode_t;
2018

@@ -23,8 +21,8 @@ struct arch_uprobe_task {
2321

2422
struct arch_uprobe {
2523
union {
26-
u8 insn[MAX_UINSN_BYTES];
27-
u8 ixol[MAX_UINSN_BYTES];
24+
__le32 insn;
25+
__le32 ixol;
2826
};
2927
struct arch_probe_insn api;
3028
bool simulate;

arch/arm64/kernel/probes/decode-insn.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,6 @@ arm_probe_decode_insn(probe_opcode_t insn, struct arch_probe_insn *api)
9999
aarch64_insn_is_blr(insn) ||
100100
aarch64_insn_is_ret(insn)) {
101101
api->handler = simulate_br_blr_ret;
102-
} else if (aarch64_insn_is_ldr_lit(insn)) {
103-
api->handler = simulate_ldr_literal;
104-
} else if (aarch64_insn_is_ldrsw_lit(insn)) {
105-
api->handler = simulate_ldrsw_literal;
106102
} else {
107103
/*
108104
* Instruction cannot be stepped out-of-line and we don't
@@ -140,6 +136,17 @@ arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi)
140136
probe_opcode_t insn = le32_to_cpu(*addr);
141137
probe_opcode_t *scan_end = NULL;
142138
unsigned long size = 0, offset = 0;
139+
struct arch_probe_insn *api = &asi->api;
140+
141+
if (aarch64_insn_is_ldr_lit(insn)) {
142+
api->handler = simulate_ldr_literal;
143+
decoded = INSN_GOOD_NO_SLOT;
144+
} else if (aarch64_insn_is_ldrsw_lit(insn)) {
145+
api->handler = simulate_ldrsw_literal;
146+
decoded = INSN_GOOD_NO_SLOT;
147+
} else {
148+
decoded = arm_probe_decode_insn(insn, &asi->api);
149+
}
143150

144151
/*
145152
* If there's a symbol defined in front of and near enough to
@@ -157,7 +164,6 @@ arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi)
157164
else
158165
scan_end = addr - MAX_ATOMIC_CONTEXT_SIZE;
159166
}
160-
decoded = arm_probe_decode_insn(insn, &asi->api);
161167

162168
if (decoded != INSN_REJECTED && scan_end)
163169
if (is_probed_address_atomic(addr - 1, scan_end))

arch/arm64/kernel/probes/simulate-insn.c

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -171,32 +171,28 @@ simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
171171
void __kprobes
172172
simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
173173
{
174-
u64 *load_addr;
174+
unsigned long load_addr;
175175
int xn = opcode & 0x1f;
176-
int disp;
177176

178-
disp = ldr_displacement(opcode);
179-
load_addr = (u64 *) (addr + disp);
177+
load_addr = addr + ldr_displacement(opcode);
180178

181179
if (opcode & (1 << 30)) /* x0-x30 */
182-
set_x_reg(regs, xn, *load_addr);
180+
set_x_reg(regs, xn, READ_ONCE(*(u64 *)load_addr));
183181
else /* w0-w30 */
184-
set_w_reg(regs, xn, *load_addr);
182+
set_w_reg(regs, xn, READ_ONCE(*(u32 *)load_addr));
185183

186184
instruction_pointer_set(regs, instruction_pointer(regs) + 4);
187185
}
188186

189187
void __kprobes
190188
simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
191189
{
192-
s32 *load_addr;
190+
unsigned long load_addr;
193191
int xn = opcode & 0x1f;
194-
int disp;
195192

196-
disp = ldr_displacement(opcode);
197-
load_addr = (s32 *) (addr + disp);
193+
load_addr = addr + ldr_displacement(opcode);
198194

199-
set_x_reg(regs, xn, *load_addr);
195+
set_x_reg(regs, xn, READ_ONCE(*(s32 *)load_addr));
200196

201197
instruction_pointer_set(regs, instruction_pointer(regs) + 4);
202198
}

arch/arm64/kernel/probes/uprobes.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
4242
else if (!IS_ALIGNED(addr, AARCH64_INSN_SIZE))
4343
return -EINVAL;
4444

45-
insn = *(probe_opcode_t *)(&auprobe->insn[0]);
45+
insn = le32_to_cpu(auprobe->insn);
4646

4747
switch (arm_probe_decode_insn(insn, &auprobe->api)) {
4848
case INSN_REJECTED:
@@ -108,7 +108,7 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
108108
if (!auprobe->simulate)
109109
return false;
110110

111-
insn = *(probe_opcode_t *)(&auprobe->insn[0]);
111+
insn = le32_to_cpu(auprobe->insn);
112112
addr = instruction_pointer(regs);
113113

114114
if (auprobe->api.handler)

arch/arm64/kernel/process.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,9 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
412412

413413
p->thread.cpu_context.x19 = (unsigned long)args->fn;
414414
p->thread.cpu_context.x20 = (unsigned long)args->fn_arg;
415+
416+
if (system_supports_poe())
417+
p->thread.por_el0 = POR_EL0_INIT;
415418
}
416419
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
417420
p->thread.cpu_context.sp = (unsigned long)childregs;

lib/Kconfig.kasan

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ config ARCH_DISABLE_KASAN_INLINE
2222
config CC_HAS_KASAN_GENERIC
2323
def_bool $(cc-option, -fsanitize=kernel-address)
2424

25+
# GCC appears to ignore no_sanitize_address when -fsanitize=kernel-hwaddress
26+
# is passed. See https://bugzilla.kernel.org/show_bug.cgi?id=218854 (and
27+
# the linked LKML thread) for more details.
2528
config CC_HAS_KASAN_SW_TAGS
26-
def_bool $(cc-option, -fsanitize=kernel-hwaddress)
29+
def_bool !CC_IS_GCC && $(cc-option, -fsanitize=kernel-hwaddress)
2730

2831
# This option is only required for software KASAN modes.
2932
# Old GCC versions do not have proper support for no_sanitize_address.
@@ -98,7 +101,7 @@ config KASAN_SW_TAGS
98101
help
99102
Enables Software Tag-Based KASAN.
100103

101-
Requires GCC 11+ or Clang.
104+
Requires Clang.
102105

103106
Supported only on arm64 CPUs and relies on Top Byte Ignore.
104107

0 commit comments

Comments
 (0)