Skip to content

Commit

Permalink
[test_ubsan] Add test_ubsan for testing all ubsan handler behavior
Browse files Browse the repository at this point in the history
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
=================================================================================
  • Loading branch information
ericchancf committed Jul 10, 2024
1 parent e533721 commit c0be848
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 0 deletions.
14 changes: 14 additions & 0 deletions lib/test_ubsan/rules.mk
Original file line number Diff line number Diff line change
@@ -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
296 changes: 296 additions & 0 deletions lib/test_ubsan/test_ubsan.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
// Copyright 2024, Google LLC

#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <lk/console_cmd.h>
#include <lk/err.h>
#include <lk/utils.h>

#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);
13 changes: 13 additions & 0 deletions lib/test_ubsan/test_ubsan.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
2 changes: 2 additions & 0 deletions lib/ubsan/rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/ubsan.cpp

ifeq ($(UBSAN), 1)
GLOBAL_COMPILEFLAGS += -fsanitize=undefined
endif

include make/module.mk

0 comments on commit c0be848

Please sign in to comment.