From 7e1789e32229355018086b12aae113d943b69e41 Mon Sep 17 00:00:00 2001 From: Eric Chan Date: Wed, 10 Jul 2024 18:01:18 +0000 Subject: [PATCH] [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 ================================================================================= --- lib/test_ubsan/rules.mk | 14 ++ lib/test_ubsan/test_ubsan.c | 296 ++++++++++++++++++++++++++++++++++ lib/test_ubsan/test_ubsan.cpp | 13 ++ 3 files changed, 323 insertions(+) create mode 100644 lib/test_ubsan/rules.mk create mode 100644 lib/test_ubsan/test_ubsan.c create mode 100644 lib/test_ubsan/test_ubsan.cpp diff --git a/lib/test_ubsan/rules.mk b/lib/test_ubsan/rules.mk new file mode 100644 index 000000000..a326978b9 --- /dev/null +++ b/lib/test_ubsan/rules.mk @@ -0,0 +1,14 @@ +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS += \ + $(LOCAL_DIR)/test_ubsan.c \ + $(LOCAL_DIR)/test_ubsan.cpp + +MODULE_DEPS += \ + lib/ubsan + +MODULE_COMPILEFLAGS += -fsanitize=undefined + +include make/module.mk diff --git a/lib/test_ubsan/test_ubsan.c b/lib/test_ubsan/test_ubsan.c new file mode 100644 index 000000000..374b4c078 --- /dev/null +++ b/lib/test_ubsan/test_ubsan.c @@ -0,0 +1,296 @@ +// Copyright 2024, Google LLC + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TESTCASE_UBSAN(x) { .func = x, .name = #x } + +typedef void (*test_ubsan_fp)(void); +struct test_ubsan_struct { + test_ubsan_fp func; + const char *const name; +}; + +static void add_overflow(void) { + volatile int val = INT_MAX; + volatile int val2 = 1; + + val += val2; +} + +static void sub_overflow(void) { + volatile int val = INT_MIN; + volatile int val2 = 1; + + val -= val2; +} + +static void mul_overflow(void) { + volatile int val = INT_MAX / 2; + volatile int val2 = 3; + + val *= val2; +} + +static void negate_overflow(void) { + volatile int val = INT_MIN; + + val = -val; +} + +static void divrem_overflow(void) { + volatile int val = 16; + volatile int val2 = 0; + + val /= val2; +} + +static void pointer_overflow(void) { + volatile uintptr_t offset = UINTPTR_MAX; + char buf[4]; + char *ptr = buf; + + ptr += offset; + (void) ptr; +} + +static void float_cast_overflow(void) { + volatile uint32_t val; + volatile uint32_t mul = 10; + + val = mul * 0xFFFF * 1e9; + (void) val; +} + +static void shift_out_of_bounds_overflow(void) { + volatile int pos = 1; + volatile int val = INT_MAX; + + val <<= pos; +} + +static void shift_out_of_bounds_negative(void) { + volatile int neg = -1; + volatile int val = 10; + + val <<= neg; +} + +static void out_of_bounds_overflow(void) { + volatile int val = 10; + volatile int overflow = 4; + volatile char above[4] = {}; + volatile int arr[4]; + volatile char below[4] = {}; + + above[0] = below[0]; + arr[overflow] = val; +} + +static void out_of_bounds_underflow(void) { + volatile int val = 10; + volatile int underflow = -1; + volatile char above[4] = {}; + volatile int arr[4]; + volatile char below[4] = {}; + + above[0] = below[0]; + arr[underflow] = val; +} + +static void load_invalid_value_bool(void) { + volatile char *dst, *src; + bool val, val2; + volatile char invalid_val = 2; + + src = &invalid_val; + dst = (char *)&val; + *dst = *src; + + val2 = val; + (void) val2; +} + +extern void load_invalid_value_enum(void); + +static void nullptr_deref(void) { + int *nullptr = NULL; + int val = *nullptr; + (void) val; +} + +static void misaligned_access(void) { + struct object { + int val; + }; + char buf[3]; + struct object *obj = (struct object *)buf; + + obj = (struct object*)(buf + 1); + obj->val = 0; +} + +static void object_size_mismatch(void) { + struct object { + int val; + }; + + char buf[3]; + struct object *obj = (struct object *)buf; + + obj->val = 0; +} + +static void func(int a) { return; } +static void function_type_mismatch(void) { + typedef void (*func_ptr)(void); + func_ptr f = (func_ptr) func; + f(); +} + +__attribute__((nonnull)) static void _nonnull_arg( void *ptr) { (void) ptr; } +static void nonnull_arg(void) { + volatile void *nullptr = NULL; + _nonnull_arg((void *)nullptr); +} + +__attribute__((returns_nonnull)) static void* _nonnull_return(void) { + volatile void *ret = NULL; + return (void *)ret; +} + +static void nonnull_return(void) { + _nonnull_return(); +} + +static void vla_bound_not_positive(void) { + volatile int size = 0; + int arr[size]; + + (void) arr; +} + +static void *_alignment_assumption(void) __attribute__((__assume_aligned__(32))); +static void *_alignment_assumption(void) { + volatile void *ptr = (void *)0x3; + return (void *)ptr; +} + +static void *_alignment_assumption_offset(void) __attribute__((__assume_aligned__(32, 4))); +static void *_alignment_assumption_offset(void) { + volatile void *ptr = (void *)0x3; + return (void *)ptr; +} + +static void alignment_assumption(void) { + void *ptr; + + ptr = _alignment_assumption(); + (void) ptr; +} + +static void alignment_assumption_offset(void) { + void *ptr; + + ptr = _alignment_assumption_offset(); + (void) ptr; +} + +// __builtin_clzg and __builtin_ctzg is not support +static void invalid_builtin_clz(void) { + int ret = __builtin_clz(0); + (void) ret; +} + +static void invalid_builtin_ctz(void) { + int ret = __builtin_ctz(0); + (void) ret; +} + +static void builtin_unreachable(void) { + __UNREACHABLE; +} + +static const struct test_ubsan_struct test_ubsan_array[] = { + TESTCASE_UBSAN(add_overflow), + TESTCASE_UBSAN(sub_overflow), + TESTCASE_UBSAN(mul_overflow), + TESTCASE_UBSAN(negate_overflow), + TESTCASE_UBSAN(pointer_overflow), + TESTCASE_UBSAN(float_cast_overflow), + TESTCASE_UBSAN(shift_out_of_bounds_overflow), + TESTCASE_UBSAN(shift_out_of_bounds_negative), + TESTCASE_UBSAN(out_of_bounds_overflow), + TESTCASE_UBSAN(out_of_bounds_underflow), + TESTCASE_UBSAN(load_invalid_value_bool), + TESTCASE_UBSAN(load_invalid_value_enum), + TESTCASE_UBSAN(nullptr_deref), + TESTCASE_UBSAN(misaligned_access), + TESTCASE_UBSAN(object_size_mismatch), + TESTCASE_UBSAN(function_type_mismatch), + TESTCASE_UBSAN(nonnull_arg), + TESTCASE_UBSAN(nonnull_return), + TESTCASE_UBSAN(vla_bound_not_positive), + TESTCASE_UBSAN(alignment_assumption), + TESTCASE_UBSAN(alignment_assumption_offset), + TESTCASE_UBSAN(invalid_builtin_clz), + TESTCASE_UBSAN(invalid_builtin_ctz), + TESTCASE_UBSAN(builtin_unreachable), +}; + + +static void show_usage(void) { + printf("Usage: test_ubsan [OPTION]\n"); + printf(" NUM - The testcase number you want to test\n"); + printf(" all - executing all ubsan test cases\n"); + printf("Available ubsan test cases:\n"); + printf("Num: testcase name\n"); + for (unsigned int i = 0; i < ARRAY_SIZE(test_ubsan_array); ++i) + printf("%3u: %s\n", i, test_ubsan_array[i].name); +} + +static int test_ubsan(int argc, const console_cmd_args *argv) { + + unsigned long id; + + if (argc != 2) { + show_usage(); + return argc == 1 ? NO_ERROR : ERR_INVALID_ARGS; + } + + if (!strcmp(argv[1].str, "all")) { + for (unsigned int i = 0; i < ARRAY_SIZE(test_ubsan_array); ++i) { + printf("start %s ...\n", test_ubsan_array[i].name); + test_ubsan_array[i].func(); + } + + return NO_ERROR; + } + + if (!isdigit(argv[1].str[0])) { + show_usage(); + return ERR_INVALID_ARGS; + } + + id = argv[1].u; + + if (id >= ARRAY_SIZE(test_ubsan_array)) { + show_usage(); + return ERR_INVALID_ARGS; + } + + printf("start %s ...\n", test_ubsan_array[id].name); + test_ubsan_array[id].func(); + return NO_ERROR; +} + +STATIC_COMMAND_START +STATIC_COMMAND("test_ubsan", "testing ubsan", test_ubsan) +STATIC_COMMAND_END(test_ubsan); diff --git a/lib/test_ubsan/test_ubsan.cpp b/lib/test_ubsan/test_ubsan.cpp new file mode 100644 index 000000000..73ef63e4b --- /dev/null +++ b/lib/test_ubsan/test_ubsan.cpp @@ -0,0 +1,13 @@ +extern "C" +{ + +// https://cplusplus.github.io/CWG/issues/1766.html +// It is only since C++17 that loading an out-of-range value for an enum is undefined behavior. +// But clang treats this as UB even with C++14(LK compiles C++ files with --std=c++14 by default) +void load_invalid_value_enum(void) { + enum E {e1, e2, e3, e4}; + volatile int a = 5; + volatile enum E e = *((volatile enum E*)(&a)); + (void) &e; +} +}