Skip to content

Commit c0be848

Browse files
committed
[test_ubsan] Add test_ubsan for testing all ubsan handler behavior
Some of platforms may have the memory limitation so can not enable UBSAN directly. So add a test module for ubsan. Test log is as below: test_ubsan Usage: test_ubsan [OPTION] NUM - The testcase number you want to test all - executing all ubsan test cases Available ubsan test cases: Num: testcase name 0: add_overflow 1: sub_overflow 2: mul_overflow 3: negate_overflow 4: pointer_overflow 5: float_cast_overflow 6: shift_out_of_bounds_overflow 7: shift_out_of_bounds_negative 8: out_of_bounds_overflow 9: out_of_bounds_underflow 10: load_invalid_value_bool 11: load_invalid_value_enum 12: nullptr_deref 13: misaligned_access 14: object_size_mismatch 15: function_type_mismatch 16: nonnull_arg 17: nonnull_return 18: vla_bound_not_positive 19: alignment_assumption 20: alignment_assumption_offset 21: invalid_builtin_clz 22: invalid_builtin_ctz 23: builtin_unreachable test_ubsan all start add_overflow ... ===================================UBSAN error=================================== ubsan: Integer overflow in lk/lib/test_ubsan/test_ubsan.c:25:9 signed integer overflow: 2147483647 + 2 can't be represented in type 'int' ================================================================================= start sub_overflow ... ===================================UBSAN error=================================== ubsan: Integer overflow in lk/lib/test_ubsan/test_ubsan.c:32:9 signed integer overflow: -2147483648 - 2 can't be represented in type 'int' ================================================================================= start mul_overflow ... ===================================UBSAN error=================================== ubsan: Integer overflow in lk/lib/test_ubsan/test_ubsan.c:39:9 signed integer overflow: 1073741823 * 3 can't be represented in type 'int' ================================================================================= start negate_overflow ... ===================================UBSAN error=================================== ubsan: Negation overflow in lk/lib/test_ubsan/test_ubsan.c:45:11 Negation of -2147483648 cannot be represented in type 'int' ================================================================================= start pointer_overflow ... ===================================UBSAN error=================================== ubsan: ptr overflow in lk/lib/test_ubsan/test_ubsan.c:60:9 ptr operation overflowed 0x2001d590 to 0x2001d58f ================================================================================= start float_cast_overflow ... ===================================UBSAN error=================================== ubsan: Float Cast Overflow in lk/lib/test_ubsan/test_ubsan.c:68:12 value 'double' is outside of the range of representable value of type 'unsigned int' at 0x2001d580 ================================================================================= start shift_out_of_bounds_overflow ... ===================================UBSAN error=================================== ubsan: shift-oob in lk/lib/test_ubsan/test_ubsan.c:76:9 left shift of 2147483647 by 1 places cannot be represented in type 'volatile int' ================================================================================= start shift_out_of_bounds_negative ... ===================================UBSAN error=================================== ubsan: shift-oob in lk/lib/test_ubsan/test_ubsan.c:83:9 shift exponent -1 is negative ================================================================================= start out_of_bounds_overflow ... ===================================UBSAN error=================================== ubsan: out of bounds in lk/lib/test_ubsan/test_ubsan.c:94:5 index 4 out of range for type 'volatile int[4]' (4) ================================================================================= start out_of_bounds_underflow ... ===================================UBSAN error=================================== ubsan: out of bounds in lk/lib/test_ubsan/test_ubsan.c:105:5 index -1 out of range for type 'volatile int[4]' (ffffffff) ================================================================================= start load_invalid_value_bool ... ===================================UBSAN error=================================== ubsan: load invalid value in lk/lib/test_ubsan/test_ubsan.c:117:12 load of value 2, which is not a valid value for type 'bool' ================================================================================= start load_invalid_value_enum ... ===================================UBSAN error=================================== ubsan: load invalid value in lk/lib/test_ubsan/test_ubsan.cpp:7:25 load of value 5, which is not a valid value for type 'volatile enum E' ================================================================================= ===================================UBSAN error=================================== ubsan: load invalid value in lk/lib/test_ubsan/test_ubsan.cpp:8:12 load of value 5, which is not a valid value for type 'volatile enum E' ================================================================================= start nullptr_deref ... ===================================UBSAN error=================================== ubsan: Null pointer dereference in lk/lib/test_ubsan/test_ubsan.c:125:15 load of null pointer of type 'int' ================================================================================= start misaligned_access ... ===================================UBSAN error=================================== ubsan: Unaligned access in lk/lib/test_ubsan/test_ubsan.c:137:10 member access within unaligned pointer 0x2001d58e of type 'struct object' (alignment 4) ================================================================================= ===================================UBSAN error=================================== ubsan: Unaligned access in lk/lib/test_ubsan/test_ubsan.c:137:10 store to unaligned pointer 0x2001d58e of type 'int' (alignment 4) ================================================================================= start object_size_mismatch ... ===================================UBSAN error=================================== ubsan: Insufficient object size in lk/lib/test_ubsan/test_ubsan.c:148:10 member access within address 0x2001d58c with insufficient space for type 'struct object' ================================================================================= ===================================UBSAN error=================================== ubsan: Insufficient object size in lk/lib/test_ubsan/test_ubsan.c:148:10 store to address 0x2001d58c with insufficient space for type 'int' ================================================================================= start function_type_mismatch ... ===================================UBSAN error=================================== ubsan: function type mismatch in lk/lib/test_ubsan/test_ubsan.c:155:5 Call function(0x12d0d) through pointer with incompatible type 'void (*)(void)' ================================================================================= start nonnull_arg ... ===================================UBSAN error=================================== ubsan: nonnull-argument in lk/lib/test_ubsan/test_ubsan.c:161:18 null pointer passed as argument 1, specified non-null ================================================================================= start nonnull_return ... ===================================UBSAN error=================================== ubsan: nonnull return in lk/lib/test_ubsan/test_ubsan.c:164:16 Returning a null pointer from function declared to never return null ================================================================================= start vla_bound_not_positive ... ===================================UBSAN error=================================== ubsan: vla bound not positive in lk/lib/test_ubsan/test_ubsan.c:175:13 vla bound not positive (0) ================================================================================= start alignment_assumption ... ===================================UBSAN error=================================== ubsan: Alignment Error in lk/lib/test_ubsan/test_ubsan.c:195:11 assumption of 32 byte alignment for pointer of type 'void *' failed address is 2 aligned, misalignment offset is 3 bytes ================================================================================= start alignment_assumption_offset ... ===================================UBSAN error=================================== ubsan: Alignment Error in lk/lib/test_ubsan/test_ubsan.c:202:11 assumption of 32 byte alignment (with offset of 4 byte) for pointer of type 'void *' failed offset address is 2 aligned, misalignment offset is 31 bytes ================================================================================= start invalid_builtin_clz ... ===================================UBSAN error=================================== ubsan: invalid builtin in lk/lib/test_ubsan/test_ubsan.c:208:29 Passed 0 to clz() ================================================================================= start invalid_builtin_ctz ... ===================================UBSAN error=================================== ubsan: invalid builtin in lk/lib/test_ubsan/test_ubsan.c:213:29 Passed 0 to ctz() ================================================================================= start builtin_unreachable ... ===================================UBSAN error=================================== ubsan: execution reached an unreachable program point in lk/lib/test_ubsan/test_ubsan.c:218:5 =================================================================================
1 parent e533721 commit c0be848

4 files changed

Lines changed: 325 additions & 0 deletions

File tree

lib/test_ubsan/rules.mk

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
LOCAL_DIR := $(GET_LOCAL_DIR)
2+
3+
MODULE := $(LOCAL_DIR)
4+
5+
MODULE_SRCS += \
6+
$(LOCAL_DIR)/test_ubsan.c \
7+
$(LOCAL_DIR)/test_ubsan.cpp
8+
9+
MODULE_DEPS += \
10+
lib/ubsan
11+
12+
MODULE_COMPILEFLAGS += -fsanitize=undefined
13+
14+
include make/module.mk

lib/test_ubsan/test_ubsan.c

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
// Copyright 2024, Google LLC
2+
3+
#include <ctype.h>
4+
#include <stdint.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <string.h>
8+
9+
#include <lk/console_cmd.h>
10+
#include <lk/err.h>
11+
#include <lk/utils.h>
12+
13+
#define TESTCASE_UBSAN(x) { .func = x, .name = #x }
14+
15+
typedef void (*test_ubsan_fp)(void);
16+
struct test_ubsan_struct {
17+
test_ubsan_fp func;
18+
const char *const name;
19+
};
20+
21+
static void add_overflow(void) {
22+
volatile int val = INT_MAX;
23+
volatile int val2 = 1;
24+
25+
val += val2;
26+
}
27+
28+
static void sub_overflow(void) {
29+
volatile int val = INT_MIN;
30+
volatile int val2 = 1;
31+
32+
val -= val2;
33+
}
34+
35+
static void mul_overflow(void) {
36+
volatile int val = INT_MAX / 2;
37+
volatile int val2 = 3;
38+
39+
val *= val2;
40+
}
41+
42+
static void negate_overflow(void) {
43+
volatile int val = INT_MIN;
44+
45+
val = -val;
46+
}
47+
48+
static void divrem_overflow(void) {
49+
volatile int val = 16;
50+
volatile int val2 = 0;
51+
52+
val /= val2;
53+
}
54+
55+
static void pointer_overflow(void) {
56+
volatile uintptr_t offset = UINTPTR_MAX;
57+
char buf[4];
58+
char *ptr = buf;
59+
60+
ptr += offset;
61+
(void) ptr;
62+
}
63+
64+
static void float_cast_overflow(void) {
65+
volatile uint32_t val;
66+
volatile uint32_t mul = 10;
67+
68+
val = mul * 0xFFFF * 1e9;
69+
(void) val;
70+
}
71+
72+
static void shift_out_of_bounds_overflow(void) {
73+
volatile int pos = 1;
74+
volatile int val = INT_MAX;
75+
76+
val <<= pos;
77+
}
78+
79+
static void shift_out_of_bounds_negative(void) {
80+
volatile int neg = -1;
81+
volatile int val = 10;
82+
83+
val <<= neg;
84+
}
85+
86+
static void out_of_bounds_overflow(void) {
87+
volatile int val = 10;
88+
volatile int overflow = 4;
89+
volatile char above[4] = {};
90+
volatile int arr[4];
91+
volatile char below[4] = {};
92+
93+
above[0] = below[0];
94+
arr[overflow] = val;
95+
}
96+
97+
static void out_of_bounds_underflow(void) {
98+
volatile int val = 10;
99+
volatile int underflow = -1;
100+
volatile char above[4] = {};
101+
volatile int arr[4];
102+
volatile char below[4] = {};
103+
104+
above[0] = below[0];
105+
arr[underflow] = val;
106+
}
107+
108+
static void load_invalid_value_bool(void) {
109+
volatile char *dst, *src;
110+
bool val, val2;
111+
volatile char invalid_val = 2;
112+
113+
src = &invalid_val;
114+
dst = (char *)&val;
115+
*dst = *src;
116+
117+
val2 = val;
118+
(void) val2;
119+
}
120+
121+
extern void load_invalid_value_enum(void);
122+
123+
static void nullptr_deref(void) {
124+
int *nullptr = NULL;
125+
int val = *nullptr;
126+
(void) val;
127+
}
128+
129+
static void misaligned_access(void) {
130+
struct object {
131+
int val;
132+
};
133+
char buf[3];
134+
struct object *obj = (struct object *)buf;
135+
136+
obj = (struct object*)(buf + 1);
137+
obj->val = 0;
138+
}
139+
140+
static void object_size_mismatch(void) {
141+
struct object {
142+
int val;
143+
};
144+
145+
char buf[3];
146+
struct object *obj = (struct object *)buf;
147+
148+
obj->val = 0;
149+
}
150+
151+
static void func(int a) { return; }
152+
static void function_type_mismatch(void) {
153+
typedef void (*func_ptr)(void);
154+
func_ptr f = (func_ptr) func;
155+
f();
156+
}
157+
158+
__attribute__((nonnull)) static void _nonnull_arg( void *ptr) { (void) ptr; }
159+
static void nonnull_arg(void) {
160+
volatile void *nullptr = NULL;
161+
_nonnull_arg((void *)nullptr);
162+
}
163+
164+
__attribute__((returns_nonnull)) static void* _nonnull_return(void) {
165+
volatile void *ret = NULL;
166+
return (void *)ret;
167+
}
168+
169+
static void nonnull_return(void) {
170+
_nonnull_return();
171+
}
172+
173+
static void vla_bound_not_positive(void) {
174+
volatile int size = 0;
175+
int arr[size];
176+
177+
(void) arr;
178+
}
179+
180+
static void *_alignment_assumption(void) __attribute__((__assume_aligned__(32)));
181+
static void *_alignment_assumption(void) {
182+
volatile void *ptr = (void *)0x3;
183+
return (void *)ptr;
184+
}
185+
186+
static void *_alignment_assumption_offset(void) __attribute__((__assume_aligned__(32, 4)));
187+
static void *_alignment_assumption_offset(void) {
188+
volatile void *ptr = (void *)0x3;
189+
return (void *)ptr;
190+
}
191+
192+
static void alignment_assumption(void) {
193+
void *ptr;
194+
195+
ptr = _alignment_assumption();
196+
(void) ptr;
197+
}
198+
199+
static void alignment_assumption_offset(void) {
200+
void *ptr;
201+
202+
ptr = _alignment_assumption_offset();
203+
(void) ptr;
204+
}
205+
206+
// __builtin_clzg and __builtin_ctzg is not support
207+
static void invalid_builtin_clz(void) {
208+
int ret = __builtin_clz(0);
209+
(void) ret;
210+
}
211+
212+
static void invalid_builtin_ctz(void) {
213+
int ret = __builtin_ctz(0);
214+
(void) ret;
215+
}
216+
217+
static void builtin_unreachable(void) {
218+
__UNREACHABLE;
219+
}
220+
221+
static const struct test_ubsan_struct test_ubsan_array[] = {
222+
TESTCASE_UBSAN(add_overflow),
223+
TESTCASE_UBSAN(sub_overflow),
224+
TESTCASE_UBSAN(mul_overflow),
225+
TESTCASE_UBSAN(negate_overflow),
226+
TESTCASE_UBSAN(pointer_overflow),
227+
TESTCASE_UBSAN(float_cast_overflow),
228+
TESTCASE_UBSAN(shift_out_of_bounds_overflow),
229+
TESTCASE_UBSAN(shift_out_of_bounds_negative),
230+
TESTCASE_UBSAN(out_of_bounds_overflow),
231+
TESTCASE_UBSAN(out_of_bounds_underflow),
232+
TESTCASE_UBSAN(load_invalid_value_bool),
233+
TESTCASE_UBSAN(load_invalid_value_enum),
234+
TESTCASE_UBSAN(nullptr_deref),
235+
TESTCASE_UBSAN(misaligned_access),
236+
TESTCASE_UBSAN(object_size_mismatch),
237+
TESTCASE_UBSAN(function_type_mismatch),
238+
TESTCASE_UBSAN(nonnull_arg),
239+
TESTCASE_UBSAN(nonnull_return),
240+
TESTCASE_UBSAN(vla_bound_not_positive),
241+
TESTCASE_UBSAN(alignment_assumption),
242+
TESTCASE_UBSAN(alignment_assumption_offset),
243+
TESTCASE_UBSAN(invalid_builtin_clz),
244+
TESTCASE_UBSAN(invalid_builtin_ctz),
245+
TESTCASE_UBSAN(builtin_unreachable),
246+
};
247+
248+
249+
static void show_usage(void) {
250+
printf("Usage: test_ubsan [OPTION]\n");
251+
printf(" NUM - The testcase number you want to test\n");
252+
printf(" all - executing all ubsan test cases\n");
253+
printf("Available ubsan test cases:\n");
254+
printf("Num: testcase name\n");
255+
for (unsigned int i = 0; i < ARRAY_SIZE(test_ubsan_array); ++i)
256+
printf("%3u: %s\n", i, test_ubsan_array[i].name);
257+
}
258+
259+
static int test_ubsan(int argc, const console_cmd_args *argv) {
260+
261+
unsigned long id;
262+
263+
if (argc != 2) {
264+
show_usage();
265+
return argc == 1 ? NO_ERROR : ERR_INVALID_ARGS;
266+
}
267+
268+
if (!strcmp(argv[1].str, "all")) {
269+
for (unsigned int i = 0; i < ARRAY_SIZE(test_ubsan_array); ++i) {
270+
printf("start %s ...\n", test_ubsan_array[i].name);
271+
test_ubsan_array[i].func();
272+
}
273+
274+
return NO_ERROR;
275+
}
276+
277+
if (!isdigit(argv[1].str[0])) {
278+
show_usage();
279+
return ERR_INVALID_ARGS;
280+
}
281+
282+
id = argv[1].u;
283+
284+
if (id >= ARRAY_SIZE(test_ubsan_array)) {
285+
show_usage();
286+
return ERR_INVALID_ARGS;
287+
}
288+
289+
printf("start %s ...\n", test_ubsan_array[id].name);
290+
test_ubsan_array[id].func();
291+
return NO_ERROR;
292+
}
293+
294+
STATIC_COMMAND_START
295+
STATIC_COMMAND("test_ubsan", "testing ubsan", test_ubsan)
296+
STATIC_COMMAND_END(test_ubsan);

lib/test_ubsan/test_ubsan.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
extern "C"
2+
{
3+
4+
// https://cplusplus.github.io/CWG/issues/1766.html
5+
// It is only since C++17 that loading an out-of-range value for an enum is undefined behavior.
6+
// But clang treats this as UB even with C++14(LK compiles C++ files with --std=c++14 by default)
7+
void load_invalid_value_enum(void) {
8+
enum E {e1, e2, e3, e4};
9+
volatile int a = 5;
10+
volatile enum E e = *((volatile enum E*)(&a));
11+
(void) &e;
12+
}
13+
}

lib/ubsan/rules.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ MODULE := $(LOCAL_DIR)
55
MODULE_SRCS += \
66
$(LOCAL_DIR)/ubsan.cpp
77

8+
ifeq ($(UBSAN), 1)
89
GLOBAL_COMPILEFLAGS += -fsanitize=undefined
10+
endif
911

1012
include make/module.mk

0 commit comments

Comments
 (0)