Skip to content

Commit de78a9c

Browse files
chleroympe
authored andcommitted
powerpc: Add a framework for Kernel Userspace Access Protection
This patch implements a framework for Kernel Userspace Access Protection. Then subarches will have the possibility to provide their own implementation by providing setup_kuap() and allow/prevent_user_access(). Some platforms will need to know the area accessed and whether it is accessed from read, write or both. Therefore source, destination and size and handed over to the two functions. mpe: Rename to allow/prevent rather than unlock/lock, and add read/write wrappers. Drop the 32-bit code for now until we have an implementation for it. Add kuap to pt_regs for 64-bit as well as 32-bit. Don't split strings, use pr_crit_ratelimited(). Signed-off-by: Christophe Leroy <[email protected]> Signed-off-by: Russell Currey <[email protected]> Signed-off-by: Michael Ellerman <[email protected]>
1 parent 0fb1c25 commit de78a9c

File tree

10 files changed

+121
-15
lines changed

10 files changed

+121
-15
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2839,7 +2839,7 @@
28392839
noexec=on: enable non-executable mappings (default)
28402840
noexec=off: disable non-executable mappings
28412841

2842-
nosmap [X86]
2842+
nosmap [X86,PPC]
28432843
Disable SMAP (Supervisor Mode Access Prevention)
28442844
even if it is supported by processor.
28452845

arch/powerpc/include/asm/futex.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
3535
{
3636
int oldval = 0, ret;
3737

38+
allow_write_to_user(uaddr, sizeof(*uaddr));
3839
pagefault_disable();
3940

4041
switch (op) {
@@ -62,6 +63,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
6263
if (!ret)
6364
*oval = oldval;
6465

66+
prevent_write_to_user(uaddr, sizeof(*uaddr));
6567
return ret;
6668
}
6769

@@ -75,6 +77,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
7577
if (!access_ok(uaddr, sizeof(u32)))
7678
return -EFAULT;
7779

80+
allow_write_to_user(uaddr, sizeof(*uaddr));
7881
__asm__ __volatile__ (
7982
PPC_ATOMIC_ENTRY_BARRIER
8083
"1: lwarx %1,0,%3 # futex_atomic_cmpxchg_inatomic\n\
@@ -95,6 +98,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
9598
: "cc", "memory");
9699

97100
*uval = prev;
101+
prevent_write_to_user(uaddr, sizeof(*uaddr));
98102
return ret;
99103
}
100104

arch/powerpc/include/asm/kup.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#ifndef __ASSEMBLY__
66

7+
#include <asm/pgtable.h>
8+
79
void setup_kup(void);
810

911
#ifdef CONFIG_PPC_KUEP
@@ -12,6 +14,36 @@ void setup_kuep(bool disabled);
1214
static inline void setup_kuep(bool disabled) { }
1315
#endif /* CONFIG_PPC_KUEP */
1416

17+
#ifdef CONFIG_PPC_KUAP
18+
void setup_kuap(bool disabled);
19+
#else
20+
static inline void setup_kuap(bool disabled) { }
21+
static inline void allow_user_access(void __user *to, const void __user *from,
22+
unsigned long size) { }
23+
static inline void prevent_user_access(void __user *to, const void __user *from,
24+
unsigned long size) { }
25+
#endif /* CONFIG_PPC_KUAP */
26+
27+
static inline void allow_read_from_user(const void __user *from, unsigned long size)
28+
{
29+
allow_user_access(NULL, from, size);
30+
}
31+
32+
static inline void allow_write_to_user(void __user *to, unsigned long size)
33+
{
34+
allow_user_access(to, NULL, size);
35+
}
36+
37+
static inline void prevent_read_from_user(const void __user *from, unsigned long size)
38+
{
39+
prevent_user_access(NULL, from, size);
40+
}
41+
42+
static inline void prevent_write_to_user(void __user *to, unsigned long size)
43+
{
44+
prevent_user_access(to, NULL, size);
45+
}
46+
1547
#endif /* !__ASSEMBLY__ */
1648

1749
#endif /* _ASM_POWERPC_KUP_H_ */

arch/powerpc/include/asm/ptrace.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,17 @@ struct pt_regs
5252
};
5353
};
5454

55+
union {
56+
struct {
5557
#ifdef CONFIG_PPC64
56-
unsigned long ppr;
57-
unsigned long __pad; /* Maintain 16 byte interrupt stack alignment */
58+
unsigned long ppr;
59+
#endif
60+
#ifdef CONFIG_PPC_KUAP
61+
unsigned long kuap;
5862
#endif
63+
};
64+
unsigned long __pad[2]; /* Maintain 16 byte interrupt stack alignment */
65+
};
5966
};
6067
#endif
6168

arch/powerpc/include/asm/uaccess.h

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <asm/processor.h>
77
#include <asm/page.h>
88
#include <asm/extable.h>
9+
#include <asm/kup.h>
910

1011
/*
1112
* The fs value determines whether argument validity checking should be
@@ -140,13 +141,15 @@ extern long __put_user_bad(void);
140141
#define __put_user_size(x, ptr, size, retval) \
141142
do { \
142143
retval = 0; \
144+
allow_write_to_user(ptr, size); \
143145
switch (size) { \
144146
case 1: __put_user_asm(x, ptr, retval, "stb"); break; \
145147
case 2: __put_user_asm(x, ptr, retval, "sth"); break; \
146148
case 4: __put_user_asm(x, ptr, retval, "stw"); break; \
147149
case 8: __put_user_asm2(x, ptr, retval); break; \
148150
default: __put_user_bad(); \
149151
} \
152+
prevent_write_to_user(ptr, size); \
150153
} while (0)
151154

152155
#define __put_user_nocheck(x, ptr, size) \
@@ -239,13 +242,15 @@ do { \
239242
__chk_user_ptr(ptr); \
240243
if (size > sizeof(x)) \
241244
(x) = __get_user_bad(); \
245+
allow_read_from_user(ptr, size); \
242246
switch (size) { \
243247
case 1: __get_user_asm(x, ptr, retval, "lbz"); break; \
244248
case 2: __get_user_asm(x, ptr, retval, "lhz"); break; \
245249
case 4: __get_user_asm(x, ptr, retval, "lwz"); break; \
246250
case 8: __get_user_asm2(x, ptr, retval); break; \
247251
default: (x) = __get_user_bad(); \
248252
} \
253+
prevent_read_from_user(ptr, size); \
249254
} while (0)
250255

251256
/*
@@ -305,15 +310,21 @@ extern unsigned long __copy_tofrom_user(void __user *to,
305310
static inline unsigned long
306311
raw_copy_in_user(void __user *to, const void __user *from, unsigned long n)
307312
{
308-
return __copy_tofrom_user(to, from, n);
313+
unsigned long ret;
314+
315+
allow_user_access(to, from, n);
316+
ret = __copy_tofrom_user(to, from, n);
317+
prevent_user_access(to, from, n);
318+
return ret;
309319
}
310320
#endif /* __powerpc64__ */
311321

312322
static inline unsigned long raw_copy_from_user(void *to,
313323
const void __user *from, unsigned long n)
314324
{
325+
unsigned long ret;
315326
if (__builtin_constant_p(n) && (n <= 8)) {
316-
unsigned long ret = 1;
327+
ret = 1;
317328

318329
switch (n) {
319330
case 1:
@@ -338,14 +349,18 @@ static inline unsigned long raw_copy_from_user(void *to,
338349
}
339350

340351
barrier_nospec();
341-
return __copy_tofrom_user((__force void __user *)to, from, n);
352+
allow_read_from_user(from, n);
353+
ret = __copy_tofrom_user((__force void __user *)to, from, n);
354+
prevent_read_from_user(from, n);
355+
return ret;
342356
}
343357

344358
static inline unsigned long raw_copy_to_user(void __user *to,
345359
const void *from, unsigned long n)
346360
{
361+
unsigned long ret;
347362
if (__builtin_constant_p(n) && (n <= 8)) {
348-
unsigned long ret = 1;
363+
ret = 1;
349364

350365
switch (n) {
351366
case 1:
@@ -365,17 +380,24 @@ static inline unsigned long raw_copy_to_user(void __user *to,
365380
return 0;
366381
}
367382

368-
return __copy_tofrom_user(to, (__force const void __user *)from, n);
383+
allow_write_to_user(to, n);
384+
ret = __copy_tofrom_user(to, (__force const void __user *)from, n);
385+
prevent_write_to_user(to, n);
386+
return ret;
369387
}
370388

371389
extern unsigned long __clear_user(void __user *addr, unsigned long size);
372390

373391
static inline unsigned long clear_user(void __user *addr, unsigned long size)
374392
{
393+
unsigned long ret = size;
375394
might_fault();
376-
if (likely(access_ok(addr, size)))
377-
return __clear_user(addr, size);
378-
return size;
395+
if (likely(access_ok(addr, size))) {
396+
allow_write_to_user(addr, size);
397+
ret = __clear_user(addr, size);
398+
prevent_write_to_user(addr, size);
399+
}
400+
return ret;
379401
}
380402

381403
extern long strncpy_from_user(char *dst, const char __user *src, long count);

arch/powerpc/kernel/asm-offsets.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,10 @@ int main(void)
332332
STACK_PT_REGS_OFFSET(_PPR, ppr);
333333
#endif /* CONFIG_PPC64 */
334334

335+
#ifdef CONFIG_PPC_KUAP
336+
STACK_PT_REGS_OFFSET(STACK_REGS_KUAP, kuap);
337+
#endif
338+
335339
#if defined(CONFIG_PPC32)
336340
#if defined(CONFIG_BOOKE) || defined(CONFIG_40x)
337341
DEFINE(EXC_LVL_SIZE, STACK_EXC_LVL_FRAME_SIZE);

arch/powerpc/lib/checksum_wrappers.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ __wsum csum_and_copy_from_user(const void __user *src, void *dst,
2929
unsigned int csum;
3030

3131
might_sleep();
32+
allow_read_from_user(src, len);
3233

3334
*err_ptr = 0;
3435

@@ -60,6 +61,7 @@ __wsum csum_and_copy_from_user(const void __user *src, void *dst,
6061
}
6162

6263
out:
64+
prevent_read_from_user(src, len);
6365
return (__force __wsum)csum;
6466
}
6567
EXPORT_SYMBOL(csum_and_copy_from_user);
@@ -70,6 +72,7 @@ __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len,
7072
unsigned int csum;
7173

7274
might_sleep();
75+
allow_write_to_user(dst, len);
7376

7477
*err_ptr = 0;
7578

@@ -97,6 +100,7 @@ __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len,
97100
}
98101

99102
out:
103+
prevent_write_to_user(dst, len);
100104
return (__force __wsum)csum;
101105
}
102106
EXPORT_SYMBOL(csum_and_copy_to_user);

arch/powerpc/mm/fault.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,11 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr,
223223
}
224224

225225
/* Is this a bad kernel fault ? */
226-
static bool bad_kernel_fault(bool is_exec, unsigned long error_code,
226+
static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code,
227227
unsigned long address)
228228
{
229+
int is_exec = TRAP(regs) == 0x400;
230+
229231
/* NX faults set DSISR_PROTFAULT on the 8xx, DSISR_NOEXEC_OR_G on others */
230232
if (is_exec && (error_code & (DSISR_NOEXEC_OR_G | DSISR_KEYFAULT |
231233
DSISR_PROTFAULT))) {
@@ -234,7 +236,15 @@ static bool bad_kernel_fault(bool is_exec, unsigned long error_code,
234236
address,
235237
from_kuid(&init_user_ns, current_uid()));
236238
}
237-
return is_exec || (address >= TASK_SIZE);
239+
240+
if (!is_exec && address < TASK_SIZE && (error_code & DSISR_PROTFAULT) &&
241+
!search_exception_tables(regs->nip)) {
242+
pr_crit_ratelimited("Kernel attempted to access user page (%lx) - exploit attempt? (uid: %d)\n",
243+
address,
244+
from_kuid(&init_user_ns, current_uid()));
245+
}
246+
247+
return is_exec || (address >= TASK_SIZE) || !search_exception_tables(regs->nip);
238248
}
239249

240250
static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address,
@@ -454,9 +464,10 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
454464

455465
/*
456466
* The kernel should never take an execute fault nor should it
457-
* take a page fault to a kernel address.
467+
* take a page fault to a kernel address or a page fault to a user
468+
* address outside of dedicated places
458469
*/
459-
if (unlikely(!is_user && bad_kernel_fault(is_exec, error_code, address)))
470+
if (unlikely(!is_user && bad_kernel_fault(regs, error_code, address)))
460471
return SIGSEGV;
461472

462473
/*

arch/powerpc/mm/init-common.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <asm/kup.h>
2828

2929
static bool disable_kuep = !IS_ENABLED(CONFIG_PPC_KUEP);
30+
static bool disable_kuap = !IS_ENABLED(CONFIG_PPC_KUAP);
3031

3132
static int __init parse_nosmep(char *p)
3233
{
@@ -36,9 +37,18 @@ static int __init parse_nosmep(char *p)
3637
}
3738
early_param("nosmep", parse_nosmep);
3839

40+
static int __init parse_nosmap(char *p)
41+
{
42+
disable_kuap = true;
43+
pr_warn("Disabling Kernel Userspace Access Protection\n");
44+
return 0;
45+
}
46+
early_param("nosmap", parse_nosmap);
47+
3948
void __init setup_kup(void)
4049
{
4150
setup_kuep(disable_kuep);
51+
setup_kuap(disable_kuap);
4252
}
4353

4454
#define CTOR(shift) static void ctor_##shift(void *addr) \

arch/powerpc/platforms/Kconfig.cputype

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,18 @@ config PPC_KUEP
357357

358358
If you're unsure, say Y.
359359

360+
config PPC_HAVE_KUAP
361+
bool
362+
363+
config PPC_KUAP
364+
bool "Kernel Userspace Access Protection"
365+
depends on PPC_HAVE_KUAP
366+
default y
367+
help
368+
Enable support for Kernel Userspace Access Protection (KUAP)
369+
370+
If you're unsure, say Y.
371+
360372
config ARCH_ENABLE_HUGEPAGE_MIGRATION
361373
def_bool y
362374
depends on PPC_BOOK3S_64 && HUGETLB_PAGE && MIGRATION

0 commit comments

Comments
 (0)