Skip to content

Commit 36ad44a

Browse files
committed
Merge: s390/kfence: fix page fault reporting
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/2429 Bugzilla: https://bugzilla.redhat.com/2148793 Upstream Status: Linus.git This is back ported from upstream, no conflict. ``` commit d9c2cf6 Author: Heiko Carstens <hca@linux.ibm.com> Date: Mon Feb 13 19:38:58 2023 +0100 s390/kfence: fix page fault reporting Baoquan He reported lots of KFENCE reports when /proc/kcore is read, e.g. with crash or even simpler with dd: BUG: KFENCE: invalid read in copy_from_kernel_nofault+0x5e/0x120 Invalid read at 0x00000000f4f5149f: copy_from_kernel_nofault+0x5e/0x120 read_kcore+0x6b2/0x870 proc_reg_read+0x9a/0xf0 vfs_read+0x94/0x270 ksys_read+0x70/0x100 __do_syscall+0x1d0/0x200 system_call+0x82/0xb0 The reason for this is that read_kcore() simply reads memory that might have been unmapped by KFENCE with copy_from_kernel_nofault(). Any fault due to pages being unmapped by KFENCE would be handled gracefully by the fault handler (exception table fixup). However the s390 fault handler first reports the fault, and only afterwards would perform the exception table fixup. Most architectures have this in reversed order, which also avoids the false positive KFENCE reports when an unmapped page is accessed. Therefore change the s390 fault handler so it handles exception table fixups before KFENCE page faults are reported. Reported-by: Baoquan He <bhe@redhat.com> Tested-by: Baoquan He <bhe@redhat.com> Acked-by: Alexander Potapenko <glider@google.com> Link: https://lore.kernel.org/r/20230213183858.1473681-1-hca@linux.ibm.com Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Baoquan He <bhe@redhat.com> ``` Approved-by: Donald Dutile <ddutile@redhat.com> Approved-by: Rafael Aquini <aquini@redhat.com> Approved-by: Tao Liu <ltao@redhat.com> Approved-by: Lianbo Jiang <lijiang@redhat.com> Approved-by: Waiman Long <longman@redhat.com> Signed-off-by: Jan Stancek <jstancek@redhat.com>
2 parents d809498 + 1cc5a1a commit 36ad44a

File tree

1 file changed

+35
-14
lines changed

1 file changed

+35
-14
lines changed

arch/s390/mm/fault.c

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ static enum fault_type get_fault_type(struct pt_regs *regs)
9696
return KERNEL_FAULT;
9797
}
9898

99+
static unsigned long get_fault_address(struct pt_regs *regs)
100+
{
101+
unsigned long trans_exc_code = regs->int_parm_long;
102+
103+
return trans_exc_code & __FAIL_ADDR_MASK;
104+
}
105+
106+
static bool fault_is_write(struct pt_regs *regs)
107+
{
108+
unsigned long trans_exc_code = regs->int_parm_long;
109+
110+
return (trans_exc_code & store_indication) == 0x400;
111+
}
112+
99113
static int bad_address(void *p)
100114
{
101115
unsigned long dummy;
@@ -228,15 +242,26 @@ static noinline void do_sigsegv(struct pt_regs *regs, int si_code)
228242
(void __user *)(regs->int_parm_long & __FAIL_ADDR_MASK));
229243
}
230244

231-
static noinline void do_no_context(struct pt_regs *regs)
245+
static noinline void do_no_context(struct pt_regs *regs, vm_fault_t fault)
232246
{
247+
enum fault_type fault_type;
248+
unsigned long address;
249+
bool is_write;
250+
233251
if (fixup_exception(regs))
234252
return;
253+
fault_type = get_fault_type(regs);
254+
if ((fault_type == KERNEL_FAULT) && (fault == VM_FAULT_BADCONTEXT)) {
255+
address = get_fault_address(regs);
256+
is_write = fault_is_write(regs);
257+
if (kfence_handle_page_fault(address, is_write, regs))
258+
return;
259+
}
235260
/*
236261
* Oops. The kernel tried to access some bad page. We'll have to
237262
* terminate things with extreme prejudice.
238263
*/
239-
if (get_fault_type(regs) == KERNEL_FAULT)
264+
if (fault_type == KERNEL_FAULT)
240265
printk(KERN_ALERT "Unable to handle kernel pointer dereference"
241266
" in virtual kernel address space\n");
242267
else
@@ -255,7 +280,7 @@ static noinline void do_low_address(struct pt_regs *regs)
255280
die (regs, "Low-address protection");
256281
}
257282

258-
do_no_context(regs);
283+
do_no_context(regs, VM_FAULT_BADACCESS);
259284
}
260285

261286
static noinline void do_sigbus(struct pt_regs *regs)
@@ -286,28 +311,28 @@ static noinline void do_fault_error(struct pt_regs *regs, vm_fault_t fault)
286311
fallthrough;
287312
case VM_FAULT_BADCONTEXT:
288313
case VM_FAULT_PFAULT:
289-
do_no_context(regs);
314+
do_no_context(regs, fault);
290315
break;
291316
case VM_FAULT_SIGNAL:
292317
if (!user_mode(regs))
293-
do_no_context(regs);
318+
do_no_context(regs, fault);
294319
break;
295320
default: /* fault & VM_FAULT_ERROR */
296321
if (fault & VM_FAULT_OOM) {
297322
if (!user_mode(regs))
298-
do_no_context(regs);
323+
do_no_context(regs, fault);
299324
else
300325
pagefault_out_of_memory();
301326
} else if (fault & VM_FAULT_SIGSEGV) {
302327
/* Kernel mode? Handle exceptions or die */
303328
if (!user_mode(regs))
304-
do_no_context(regs);
329+
do_no_context(regs, fault);
305330
else
306331
do_sigsegv(regs, SEGV_MAPERR);
307332
} else if (fault & VM_FAULT_SIGBUS) {
308333
/* Kernel mode? Handle exceptions or die */
309334
if (!user_mode(regs))
310-
do_no_context(regs);
335+
do_no_context(regs, fault);
311336
else
312337
do_sigbus(regs);
313338
} else
@@ -334,7 +359,6 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access)
334359
struct mm_struct *mm;
335360
struct vm_area_struct *vma;
336361
enum fault_type type;
337-
unsigned long trans_exc_code;
338362
unsigned long address;
339363
unsigned int flags;
340364
vm_fault_t fault;
@@ -351,9 +375,8 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access)
351375
return 0;
352376

353377
mm = tsk->mm;
354-
trans_exc_code = regs->int_parm_long;
355-
address = trans_exc_code & __FAIL_ADDR_MASK;
356-
is_write = (trans_exc_code & store_indication) == 0x400;
378+
address = get_fault_address(regs);
379+
is_write = fault_is_write(regs);
357380

358381
/*
359382
* Verify that the fault happened in user space, that
@@ -364,8 +387,6 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access)
364387
type = get_fault_type(regs);
365388
switch (type) {
366389
case KERNEL_FAULT:
367-
if (kfence_handle_page_fault(address, is_write, regs))
368-
return 0;
369390
goto out;
370391
case USER_FAULT:
371392
case GMAP_FAULT:

0 commit comments

Comments
 (0)