Commit f2fab612 authored by Nam Cao's avatar Nam Cao Committed by Paul Walmsley
Browse files

riscv: Add kprobes KUnit test

Add KUnit test for riscv kprobes, mostly for simulated instructions. The
test install kprobes into multiple sample functions, and check that these
functions still return the expected magic value.

This test can detect some kprobe bugs reported in the past (in Link:).

Link: https://lore.kernel.org/linux-riscv/20241119111056.2554419-1-namcao@linutronix.de/
Link: https://lore.kernel.org/stable/c7e463c0-8cad-4f4e-addd-195c06b7b6de@iscas.ac.cn/
Link: https://lore.kernel.org/linux-riscv/20230829182500.61875-1-namcaov@gmail.com/


Signed-off-by: default avatarNam Cao <namcao@linutronix.de>
Tested-by: default avatarAlexandre Ghiti <alexghiti@rivosinc.com>
Link: https://lore.kernel.org/r/20250513151631.3520793-1-namcao@linutronix.de


Signed-off-by: default avatarPaul Walmsley <pjw@kernel.org>
parent f811f585
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -30,6 +30,18 @@ config RISCV_MODULE_LINKING_KUNIT

         If unsure, say N.

config RISCV_KPROBES_KUNIT
       bool "KUnit test for riscv kprobes" if !KUNIT_ALL_TESTS
       depends on KUNIT
       depends on KPROBES
       default KUNIT_ALL_TESTS
       help
         Enable testing for riscv kprobes. Useful for riscv and/or kprobes
         development. The test verifies that kprobes do not change the behaviour
         of some sample functions.

         If unsure, say N.

endif # RUNTIME_TESTING_MENU

endmenu # "arch/riscv/kernel runtime Testing"
+1 −0
Original line number Diff line number Diff line
obj-$(CONFIG_RISCV_MODULE_LINKING_KUNIT)	+= module_test/
obj-$(CONFIG_RISCV_KPROBES_KUNIT)		+= kprobes/
+1 −0
Original line number Diff line number Diff line
obj-y += test-kprobes.o test-kprobes-asm.o
+229 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0+ */

#include <linux/linkage.h>
#include <asm/asm.h>
#include "test-kprobes.h"

SYM_FUNC_START(test_kprobes_add)
	li a1, KPROBE_TEST_MAGIC_UPPER
	li a2, KPROBE_TEST_MAGIC_LOWER
test_kprobes_add_addr1:
	add a1, a1, a2
test_kprobes_add_addr2:
	add a0, a1, x0
	ret
SYM_FUNC_END(test_kprobes_add)

SYM_FUNC_START(test_kprobes_jal)
	li a0, 0
	mv a1, ra
	.option push
	.option norvc
test_kprobes_jal_addr1:
	jal x0, 2f
	ret
	.option pop
1:	li a0, KPROBE_TEST_MAGIC_UPPER
	ret
	.option push
	.option norvc
test_kprobes_jal_addr2:
2:	jal 1b
	.option pop
	li a2, KPROBE_TEST_MAGIC_LOWER
	add a0, a0, a2
	jr a1
SYM_FUNC_END(test_kprobes_jal)

SYM_FUNC_START(test_kprobes_jalr)
	la a0, 1f
	mv a1, ra
	.option push
	.option norvc
test_kprobes_jalr_addr:
	jalr a0
	.option pop
	li t0, KPROBE_TEST_MAGIC_UPPER
	add a0, a0, t0
	jr a1
1:	li a0, KPROBE_TEST_MAGIC_LOWER
	ret
SYM_FUNC_END(test_kprobes_jalr)

SYM_FUNC_START(test_kprobes_auipc)
test_kprobes_auipc_addr:
	auipc a0, KPROBE_TEST_MAGIC_LOWER
	la a1, test_kprobes_auipc_addr
	sub a0, a0, a1
	srli a0, a0, 12
	li a1, KPROBE_TEST_MAGIC_UPPER
	add a0, a0, a1
	ret
SYM_FUNC_END(test_kprobes_auipc)

SYM_FUNC_START(test_kprobes_branch)
	.option push
	.option norvc
	li a0, 0
	li a1, 1
	li a2, 2
test_kprobes_branch_addr1:
	beqz a0, 1f
	ret
1:
test_kprobes_branch_addr2:
	beqz a1, 3f
test_kprobes_branch_addr3:
	bnez a0, 3f
test_kprobes_branch_addr4:
	bnez a2, 1f
	ret
1:
test_kprobes_branch_addr5:
	bge a1, a2, 3f
test_kprobes_branch_addr6:
	bge a2, a1, 2f
	ret
1:
	li t0, KPROBE_TEST_MAGIC_UPPER
	add a0, a0, t0
	ret
2:
test_kprobes_branch_addr7:
	blt a2, a1, 3f
	li a0, KPROBE_TEST_MAGIC_LOWER
test_kprobes_branch_addr8:
	blt a1, a2, 1b
3:
	li a0, 0
	ret
	.option pop
SYM_FUNC_END(test_kprobes_branch)

#ifdef CONFIG_RISCV_ISA_C

SYM_FUNC_START(test_kprobes_c_j)
	li a0, 0
test_kprobes_branch_c_j_addr1:
	c.j 2f
1:
	li a1, KPROBE_TEST_MAGIC_UPPER
	add a0, a0, a1
	ret
2:	li a0, KPROBE_TEST_MAGIC_LOWER
test_kprobes_branch_c_j_addr2:
	c.j 1b
SYM_FUNC_END(test_kprobes_c_j)

SYM_FUNC_START(test_kprobes_c_jr)
	la a0, 2f
test_kprobes_c_jr_addr1:
	c.jr a0
	ret
1:	li a1, KPROBE_TEST_MAGIC_LOWER
	add a0, a0, a1
	ret
2:
	li a0, KPROBE_TEST_MAGIC_UPPER
	la a1, 1b
test_kprobes_c_jr_addr2:
	c.jr a1
SYM_FUNC_END(test_kprobes_c_jr)

SYM_FUNC_START(test_kprobes_c_jalr)
	mv a1, ra
	la a0, 1f
test_kprobes_c_jalr_addr:
	c.jalr a0
	li a2, KPROBE_TEST_MAGIC_UPPER
	add a0, a0, a2
	jr a1
1:	li a0, KPROBE_TEST_MAGIC_LOWER
	ret
SYM_FUNC_END(test_kprobes_c_jalr)

SYM_FUNC_START(test_kprobes_c_beqz)
	li a0, 0
	li a1, 1
test_kprobes_c_beqz_addr1:
	c.beqz a0, 2f
	ret
1:	li a1, KPROBE_TEST_MAGIC_UPPER
	add a0, a0, a1
	ret
test_kprobes_c_beqz_addr2:
2:	c.beqz a1, 3f
	li a0, KPROBE_TEST_MAGIC_LOWER
	mv a1, x0
test_kprobes_c_beqz_addr3:
	c.beqz a1, 1b
3:	li a0, 0
	ret
SYM_FUNC_END(test_kprobes_c_beqz)

SYM_FUNC_START(test_kprobes_c_bnez)
	li a0, 0
	li a1, 1
test_kprobes_c_bnez_addr1:
	c.bnez a1, 2f
	ret
1:	li a1, KPROBE_TEST_MAGIC_UPPER
	add a0, a0, a1
	ret
test_kprobes_c_bnez_addr2:
2:	c.bnez a0, 3f
	li a0, KPROBE_TEST_MAGIC_LOWER
test_kprobes_c_bnez_addr3:
	c.bnez a0, 1b
3:	li a0, 0
	ret
SYM_FUNC_END(test_kprobes_c_bnez)

#endif /* CONFIG_RISCV_ISA_C */

SYM_DATA_START(test_kprobes_addresses)
	RISCV_PTR test_kprobes_add_addr1
	RISCV_PTR test_kprobes_add_addr2
	RISCV_PTR test_kprobes_jal_addr1
	RISCV_PTR test_kprobes_jal_addr2
	RISCV_PTR test_kprobes_jalr_addr
	RISCV_PTR test_kprobes_auipc_addr
	RISCV_PTR test_kprobes_branch_addr1
	RISCV_PTR test_kprobes_branch_addr2
	RISCV_PTR test_kprobes_branch_addr3
	RISCV_PTR test_kprobes_branch_addr4
	RISCV_PTR test_kprobes_branch_addr5
	RISCV_PTR test_kprobes_branch_addr6
	RISCV_PTR test_kprobes_branch_addr7
	RISCV_PTR test_kprobes_branch_addr8
#ifdef CONFIG_RISCV_ISA_C
	RISCV_PTR test_kprobes_branch_c_j_addr1
	RISCV_PTR test_kprobes_branch_c_j_addr2
	RISCV_PTR test_kprobes_c_jr_addr1
	RISCV_PTR test_kprobes_c_jr_addr2
	RISCV_PTR test_kprobes_c_jalr_addr
	RISCV_PTR test_kprobes_c_beqz_addr1
	RISCV_PTR test_kprobes_c_beqz_addr2
	RISCV_PTR test_kprobes_c_beqz_addr3
	RISCV_PTR test_kprobes_c_bnez_addr1
	RISCV_PTR test_kprobes_c_bnez_addr2
	RISCV_PTR test_kprobes_c_bnez_addr3
#endif /* CONFIG_RISCV_ISA_C */
	RISCV_PTR 0
SYM_DATA_END(test_kprobes_addresses)

SYM_DATA_START(test_kprobes_functions)
	RISCV_PTR test_kprobes_add
	RISCV_PTR test_kprobes_jal
	RISCV_PTR test_kprobes_jalr
	RISCV_PTR test_kprobes_auipc
	RISCV_PTR test_kprobes_branch
#ifdef CONFIG_RISCV_ISA_C
	RISCV_PTR test_kprobes_c_j
	RISCV_PTR test_kprobes_c_jr
	RISCV_PTR test_kprobes_c_jalr
	RISCV_PTR test_kprobes_c_beqz
	RISCV_PTR test_kprobes_c_bnez
#endif /* CONFIG_RISCV_ISA_C */
	RISCV_PTR 0
SYM_DATA_END(test_kprobes_functions)
+56 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0+

#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <kunit/test.h>
#include "test-kprobes.h"

static int kprobe_dummy_handler(struct kprobe *kp, struct pt_regs *regs)
{
	return 0;
}

static void test_kprobe_riscv(struct kunit *test)
{
	unsigned int num_kprobe = 0;
	long (*func)(void);
	struct kprobe *kp;
	int i;

	while (test_kprobes_addresses[num_kprobe])
		num_kprobe++;

	kp = kcalloc(num_kprobe, sizeof(*kp), GFP_KERNEL);
	KUNIT_EXPECT_TRUE(test, kp);
	if (!kp)
		return;

	for (i = 0; i < num_kprobe; ++i) {
		kp[i].addr = test_kprobes_addresses[i];
		kp[i].pre_handler = kprobe_dummy_handler;
		KUNIT_EXPECT_EQ(test, 0, register_kprobe(&kp[i]));
	}

	for (i = 0;; ++i) {
		func = test_kprobes_functions[i];
		if (!func)
			break;
		KUNIT_EXPECT_EQ_MSG(test, KPROBE_TEST_MAGIC, func(), "function %d broken", i);
	}

	for (i = 0; i < num_kprobe; ++i)
		unregister_kprobe(&kp[i]);
	kfree(kp);
}

static struct kunit_case kprobes_testcases[] = {
	KUNIT_CASE(test_kprobe_riscv),
	{}
};

static struct kunit_suite kprobes_test_suite = {
	.name = "kprobes_test_riscv",
	.test_cases = kprobes_testcases,
};

kunit_test_suites(&kprobes_test_suite);
Loading