162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/init.h>
362306a36Sopenharmony_ci#include <linux/kernel.h>
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_citypedef void(*test_ubsan_fp)(void);
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define UBSAN_TEST(config, ...)	do {					\
962306a36Sopenharmony_ci		pr_info("%s " __VA_ARGS__ "%s(%s=%s)\n", __func__,	\
1062306a36Sopenharmony_ci			sizeof(" " __VA_ARGS__) > 2 ? " " : "",		\
1162306a36Sopenharmony_ci			#config, IS_ENABLED(config) ? "y" : "n");	\
1262306a36Sopenharmony_ci	} while (0)
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic void test_ubsan_divrem_overflow(void)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	volatile int val = 16;
1762306a36Sopenharmony_ci	volatile int val2 = 0;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	UBSAN_TEST(CONFIG_UBSAN_DIV_ZERO);
2062306a36Sopenharmony_ci	val /= val2;
2162306a36Sopenharmony_ci}
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void test_ubsan_shift_out_of_bounds(void)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	volatile int neg = -1, wrap = 4;
2662306a36Sopenharmony_ci	int val1 = 10;
2762306a36Sopenharmony_ci	int val2 = INT_MAX;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	UBSAN_TEST(CONFIG_UBSAN_SHIFT, "negative exponent");
3062306a36Sopenharmony_ci	val1 <<= neg;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	UBSAN_TEST(CONFIG_UBSAN_SHIFT, "left overflow");
3362306a36Sopenharmony_ci	val2 <<= wrap;
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void test_ubsan_out_of_bounds(void)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	volatile int i = 4, j = 5, k = -1;
3962306a36Sopenharmony_ci	volatile char above[4] = { }; /* Protect surrounding memory. */
4062306a36Sopenharmony_ci	volatile int arr[4];
4162306a36Sopenharmony_ci	volatile char below[4] = { }; /* Protect surrounding memory. */
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	above[0] = below[0];
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	UBSAN_TEST(CONFIG_UBSAN_BOUNDS, "above");
4662306a36Sopenharmony_ci	arr[j] = i;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	UBSAN_TEST(CONFIG_UBSAN_BOUNDS, "below");
4962306a36Sopenharmony_ci	arr[k] = i;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cienum ubsan_test_enum {
5362306a36Sopenharmony_ci	UBSAN_TEST_ZERO = 0,
5462306a36Sopenharmony_ci	UBSAN_TEST_ONE,
5562306a36Sopenharmony_ci	UBSAN_TEST_MAX,
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void test_ubsan_load_invalid_value(void)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	volatile char *dst, *src;
6162306a36Sopenharmony_ci	bool val, val2, *ptr;
6262306a36Sopenharmony_ci	enum ubsan_test_enum eval, eval2, *eptr;
6362306a36Sopenharmony_ci	unsigned char c = 0xff;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	UBSAN_TEST(CONFIG_UBSAN_BOOL, "bool");
6662306a36Sopenharmony_ci	dst = (char *)&val;
6762306a36Sopenharmony_ci	src = &c;
6862306a36Sopenharmony_ci	*dst = *src;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	ptr = &val2;
7162306a36Sopenharmony_ci	val2 = val;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	UBSAN_TEST(CONFIG_UBSAN_ENUM, "enum");
7462306a36Sopenharmony_ci	dst = (char *)&eval;
7562306a36Sopenharmony_ci	src = &c;
7662306a36Sopenharmony_ci	*dst = *src;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	eptr = &eval2;
7962306a36Sopenharmony_ci	eval2 = eval;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic void test_ubsan_misaligned_access(void)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	volatile char arr[5] __aligned(4) = {1, 2, 3, 4, 5};
8562306a36Sopenharmony_ci	volatile int *ptr, val = 6;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	UBSAN_TEST(CONFIG_UBSAN_ALIGNMENT);
8862306a36Sopenharmony_ci	ptr = (int *)(arr + 1);
8962306a36Sopenharmony_ci	*ptr = val;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic const test_ubsan_fp test_ubsan_array[] = {
9362306a36Sopenharmony_ci	test_ubsan_shift_out_of_bounds,
9462306a36Sopenharmony_ci	test_ubsan_out_of_bounds,
9562306a36Sopenharmony_ci	test_ubsan_load_invalid_value,
9662306a36Sopenharmony_ci	test_ubsan_misaligned_access,
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/* Excluded because they Oops the module. */
10062306a36Sopenharmony_cistatic const test_ubsan_fp skip_ubsan_array[] = {
10162306a36Sopenharmony_ci	test_ubsan_divrem_overflow,
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int __init test_ubsan_init(void)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	unsigned int i;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(test_ubsan_array); i++)
10962306a36Sopenharmony_ci		test_ubsan_array[i]();
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_cimodule_init(test_ubsan_init);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void __exit test_ubsan_exit(void)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	/* do nothing */
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_cimodule_exit(test_ubsan_exit);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ciMODULE_AUTHOR("Jinbum Park <jinb.park7@gmail.com>");
12262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
123