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