Commit bc8fbc5f authored by Marco Elver's avatar Marco Elver Committed by Linus Torvalds
Browse files

kfence: add test suite

Add KFENCE test suite, testing various error detection scenarios. Makes
use of KUnit for test organization. Since KFENCE's interface to obtain
error reports is via the console, the test verifies that KFENCE outputs
expected reports to the console.

[elver@google.com: fix typo in test]
  Link: https://lkml.kernel.org/r/X9lHQExmHGvETxY4@elver.google.com
[elver@google.com: show access type in report]
  Link: https://lkml.kernel.org/r/20210111091544.3287013-2-elver@google.com

Link: https://lkml.kernel.org/r/20201103175841.3495947-9-elver@google.com


Signed-off-by: default avatarAlexander Potapenko <glider@google.com>
Signed-off-by: default avatarMarco Elver <elver@google.com>
Reviewed-by: default avatarDmitry Vyukov <dvyukov@google.com>
Co-developed-by: default avatarAlexander Potapenko <glider@google.com>
Reviewed-by: default avatarJann Horn <jannh@google.com>
Cc: Andrey Konovalov <andreyknvl@google.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Christopher Lameter <cl@linux.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Hillf Danton <hdanton@sina.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Joern Engel <joern@purestorage.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Paul E. McKenney <paulmck@kernel.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: SeongJae Park <sjpark@amazon.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 10efe55f
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -65,9 +65,9 @@ Error reports
A typical out-of-bounds access looks like this::

    ==================================================================
    BUG: KFENCE: out-of-bounds in test_out_of_bounds_read+0xa3/0x22b
    BUG: KFENCE: out-of-bounds read in test_out_of_bounds_read+0xa3/0x22b

    Out-of-bounds access at 0xffffffffb672efff (1B left of kfence-#17):
    Out-of-bounds read at 0xffffffffb672efff (1B left of kfence-#17):
     test_out_of_bounds_read+0xa3/0x22b
     kunit_try_run_case+0x51/0x85
     kunit_generic_run_threadfn_adapter+0x16/0x30
@@ -94,9 +94,9 @@ its origin. Note that, real kernel addresses are only shown for
Use-after-free accesses are reported as::

    ==================================================================
    BUG: KFENCE: use-after-free in test_use_after_free_read+0xb3/0x143
    BUG: KFENCE: use-after-free read in test_use_after_free_read+0xb3/0x143

    Use-after-free access at 0xffffffffb673dfe0 (in kfence-#24):
    Use-after-free read at 0xffffffffb673dfe0 (in kfence-#24):
     test_use_after_free_read+0xb3/0x143
     kunit_try_run_case+0x51/0x85
     kunit_generic_run_threadfn_adapter+0x16/0x30
@@ -193,9 +193,9 @@ where it was not possible to determine an associated object, e.g. if adjacent
object pages had not yet been allocated::

    ==================================================================
    BUG: KFENCE: invalid access in test_invalid_access+0x26/0xe0
    BUG: KFENCE: invalid read in test_invalid_access+0x26/0xe0

    Invalid access at 0xffffffffb670b00a:
    Invalid read at 0xffffffffb670b00a:
     test_invalid_access+0x26/0xe0
     kunit_try_run_case+0x51/0x85
     kunit_generic_run_threadfn_adapter+0x16/0x30
+1 −1
Original line number Diff line number Diff line
@@ -390,7 +390,7 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr,
	} else if (addr < PAGE_SIZE) {
		msg = "NULL pointer dereference";
	} else {
		if (kfence_handle_page_fault(addr, regs))
		if (kfence_handle_page_fault(addr, esr & ESR_ELx_WNR, regs))
			return;

		msg = "paging request";
+2 −1
Original line number Diff line number Diff line
@@ -682,7 +682,8 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code,
		efi_crash_gracefully_on_page_fault(address);

	/* Only not-present faults should be handled by KFENCE. */
	if (!(error_code & X86_PF_PROT) && kfence_handle_page_fault(address, regs))
	if (!(error_code & X86_PF_PROT) &&
	    kfence_handle_page_fault(address, error_code & X86_PF_WRITE, regs))
		return;

oops:
+7 −2
Original line number Diff line number Diff line
@@ -186,6 +186,7 @@ static __always_inline __must_check bool kfence_free(void *addr)
/**
 * kfence_handle_page_fault() - perform page fault handling for KFENCE pages
 * @addr: faulting address
 * @is_write: is access a write
 * @regs: current struct pt_regs (can be NULL, but shows full stack trace)
 *
 * Return:
@@ -197,7 +198,7 @@ static __always_inline __must_check bool kfence_free(void *addr)
 * cases KFENCE prints an error message and marks the offending page as
 * present, so that the kernel can proceed.
 */
bool __must_check kfence_handle_page_fault(unsigned long addr, struct pt_regs *regs);
bool __must_check kfence_handle_page_fault(unsigned long addr, bool is_write, struct pt_regs *regs);

#else /* CONFIG_KFENCE */

@@ -210,7 +211,11 @@ static inline size_t kfence_ksize(const void *addr) { return 0; }
static inline void *kfence_object_start(const void *addr) { return NULL; }
static inline void __kfence_free(void *addr) { }
static inline bool __must_check kfence_free(void *addr) { return false; }
static inline bool __must_check kfence_handle_page_fault(unsigned long addr, struct pt_regs *regs) { return false; }
static inline bool __must_check kfence_handle_page_fault(unsigned long addr, bool is_write,
							 struct pt_regs *regs)
{
	return false;
}

#endif

+13 −0
Original line number Diff line number Diff line
@@ -66,4 +66,17 @@ config KFENCE_STRESS_TEST_FAULTS

	  Only for KFENCE testing; set to 0 if you are not a KFENCE developer.

config KFENCE_KUNIT_TEST
	tristate "KFENCE integration test suite" if !KUNIT_ALL_TESTS
	default KUNIT_ALL_TESTS
	depends on TRACEPOINTS && KUNIT
	help
	  Test suite for KFENCE, testing various error detection scenarios with
	  various allocation types, and checking that reports are correctly
	  output to console.

	  Say Y here if you want the test to be built into the kernel and run
	  during boot; say M if you want the test to build as a module; say N
	  if you are unsure.

endif # KFENCE
Loading