1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/init.h> 3 #include <linux/kernel.h> 4 #include <linux/module.h> 5 6 typedef void(*test_ubsan_fp)(void); 7 8 #define UBSAN_TEST(config, ...) do { \ 9 pr_info("%s " __VA_ARGS__ "%s(%s=%s)\n", __func__, \ 10 sizeof(" " __VA_ARGS__) > 2 ? " " : "", \ 11 #config, IS_ENABLED(config) ? "y" : "n"); \ 12 } while (0) 13 14 static void test_ubsan_add_overflow(void) 15 { 16 volatile int val = INT_MAX; 17 18 UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP); 19 val += 2; 20 } 21 22 static void test_ubsan_sub_overflow(void) 23 { 24 volatile int val = INT_MIN; 25 volatile int val2 = 2; 26 27 UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP); 28 val -= val2; 29 } 30 31 static void test_ubsan_mul_overflow(void) 32 { 33 volatile int val = INT_MAX / 2; 34 35 UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP); 36 val *= 3; 37 } 38 39 static void test_ubsan_negate_overflow(void) 40 { 41 volatile int val = INT_MIN; 42 43 UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP); 44 val = -val; 45 } 46 47 static void test_ubsan_divrem_overflow(void) 48 { 49 volatile int val = 16; 50 volatile int val2 = 0; 51 52 UBSAN_TEST(CONFIG_UBSAN_DIV_ZERO); 53 val /= val2; 54 } 55 56 static void test_ubsan_truncate_signed(void) 57 { 58 volatile long val = LONG_MAX; 59 volatile int val2 = 0; 60 61 UBSAN_TEST(CONFIG_UBSAN_INTEGER_WRAP); 62 val2 = val; 63 } 64 65 static void test_ubsan_shift_out_of_bounds(void) 66 { 67 volatile int neg = -1, wrap = 4; 68 volatile int val1 = 10; 69 volatile int val2 = INT_MAX; 70 71 UBSAN_TEST(CONFIG_UBSAN_SHIFT, "negative exponent"); 72 val1 <<= neg; 73 74 UBSAN_TEST(CONFIG_UBSAN_SHIFT, "left overflow"); 75 val2 <<= wrap; 76 } 77 78 static void test_ubsan_out_of_bounds(void) 79 { 80 int i = 4, j = 4, k = -1; 81 volatile struct { 82 char above[4]; /* Protect surrounding memory. */ 83 int arr[4]; 84 char below[4]; /* Protect surrounding memory. */ 85 } data; 86 87 OPTIMIZER_HIDE_VAR(i); 88 OPTIMIZER_HIDE_VAR(j); 89 OPTIMIZER_HIDE_VAR(k); 90 91 UBSAN_TEST(CONFIG_UBSAN_BOUNDS, "above"); 92 data.arr[j] = i; 93 94 UBSAN_TEST(CONFIG_UBSAN_BOUNDS, "below"); 95 data.arr[k] = i; 96 } 97 98 enum ubsan_test_enum { 99 UBSAN_TEST_ZERO = 0, 100 UBSAN_TEST_ONE, 101 UBSAN_TEST_MAX, 102 }; 103 104 static void test_ubsan_load_invalid_value(void) 105 { 106 volatile char *dst, *src; 107 bool val, val2, *ptr; 108 enum ubsan_test_enum eval, eval2, *eptr; 109 unsigned char c = 0xff; 110 111 UBSAN_TEST(CONFIG_UBSAN_BOOL, "bool"); 112 dst = (char *)&val; 113 src = &c; 114 *dst = *src; 115 116 ptr = &val2; 117 val2 = val; 118 119 UBSAN_TEST(CONFIG_UBSAN_ENUM, "enum"); 120 dst = (char *)&eval; 121 src = &c; 122 *dst = *src; 123 124 eptr = &eval2; 125 eval2 = eval; 126 } 127 128 static void test_ubsan_misaligned_access(void) 129 { 130 volatile char arr[5] __aligned(4) = {1, 2, 3, 4, 5}; 131 volatile int *ptr, val = 6; 132 133 UBSAN_TEST(CONFIG_UBSAN_ALIGNMENT); 134 ptr = (int *)(arr + 1); 135 *ptr = val; 136 } 137 138 static const test_ubsan_fp test_ubsan_array[] = { 139 test_ubsan_add_overflow, 140 test_ubsan_sub_overflow, 141 test_ubsan_mul_overflow, 142 test_ubsan_negate_overflow, 143 test_ubsan_truncate_signed, 144 test_ubsan_shift_out_of_bounds, 145 test_ubsan_out_of_bounds, 146 test_ubsan_load_invalid_value, 147 test_ubsan_misaligned_access, 148 }; 149 150 /* Excluded because they Oops the module. */ 151 static __used const test_ubsan_fp skip_ubsan_array[] = { 152 test_ubsan_divrem_overflow, 153 }; 154 155 static int __init test_ubsan_init(void) 156 { 157 unsigned int i; 158 159 for (i = 0; i < ARRAY_SIZE(test_ubsan_array); i++) 160 test_ubsan_array[i](); 161 162 return 0; 163 } 164 module_init(test_ubsan_init); 165 166 static void __exit test_ubsan_exit(void) 167 { 168 /* do nothing */ 169 } 170 module_exit(test_ubsan_exit); 171 172 MODULE_AUTHOR("Jinbum Park <jinb.park7@gmail.com>"); 173 MODULE_DESCRIPTION("UBSAN unit test"); 174 MODULE_LICENSE("GPL v2"); 175