Files
opensbi/lib/sbi/tests/sbi_ubsan_test.c
T
Marcos Oduardo 7bdcf55705 lib: sbi: add UBSan support
UBSan (Undefined Behavior Sanitizer) is a tool implemented using
compiler instrumentation at runtime that allows checking for
statements whose output is not deterministic or defined by the C
standard. Compiling and running OpenSBI with UBSan instrumentation
will print a message in the console if any sentence performs such
an action.

Support involves two main components:
1. The UBSan implementation hooks (derived from NetBSD),
   used by the compiler to handle the check output.
2. A test suite integrated with the SBI unit test framework to
   verify correct operation at runtime.

Usage:

  make UBSAN=y PLATFORM=generic ...

The test suite is built when both UBSAN=y and CONFIG_SBIUNIT=y are
enabled.

When UBSan is enabled, FW_PAYLOAD_OFFSET may need to be increased
due to the size increase added by the instrumentation. A
value of 0x400000 has been tested.

UBSan adds runtime overhead and is intended for development builds
only, not for production.

Note: This patch marks __stack_chk_guard in sbi_init.c as a weak
symbol to prevent multiple definition errors at compile time with
UBSan instrumentation enabled. This resolves the conflict
between the .globl definitions in sbi_init.c and test_head.S.

Signed-off-by: Marcos Oduardo <marcos.oduardo@gmail.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20260515163321.2038366-1-marcos.oduardo@gmail.com
Signed-off-by: Anup Patel <anup@brainfault.org>
2026-06-12 11:58:35 +05:30

115 lines
2.9 KiB
C

/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Author: Marcos Oduardo <marcos.oduardo@gmail.com>
*/
#include <sbi/sbi_unit_test.h>
#include <sbi/sbi_types.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_ubsan.h>
#define UBSAN_EXPECT_FIRES(test, stmt) \
do { \
unsigned long _before = sbi_ubsan_report_count; \
stmt; \
SBIUNIT_EXPECT_NE(test, sbi_ubsan_report_count, _before); \
} while (0)
static void test_ubsan_add_overflow(struct sbiunit_test_case *test)
{
volatile int a = 0x7FFFFFFF; //INT_MAx
volatile int b = 1;
volatile int c;
UBSAN_EXPECT_FIRES(test, c = a + b);
(void)c;
}
static void test_ubsan_sub_overflow(struct sbiunit_test_case *test)
{
volatile int a = 0x80000000; //INT_MIN
volatile int b = 1;
volatile int c;
UBSAN_EXPECT_FIRES(test, c = a - b);
(void)c;
}
static void test_ubsan_mul_overflow(struct sbiunit_test_case *test)
{
volatile int a = 0x7FFFFFFF;
volatile int b = 2;
volatile int c;
UBSAN_EXPECT_FIRES(test, c = a * b);
(void)c;
}
static void test_ubsan_divrem(struct sbiunit_test_case *test)
{
volatile int a = 10;
volatile int b = 0;
volatile int c;
UBSAN_EXPECT_FIRES(test, c = a / b);
(void)c;
}
static void test_ubsan_oob(struct sbiunit_test_case *test)
{
volatile int idx = 5;
int arr[3] = { 1, 2, 3 };
volatile int val;
UBSAN_EXPECT_FIRES(test, val = arr[idx]);
(void)val;
}
static void test_ubsan_shift_too_large(struct sbiunit_test_case *test)
{
volatile unsigned long val = 1;
volatile int shift = 64;
volatile unsigned long res;
UBSAN_EXPECT_FIRES(test, res = val << shift);
(void)res;
}
static void test_ubsan_shift_negative(struct sbiunit_test_case *test)
{
volatile int val = 1;
volatile int shift = -1;
volatile int res;
UBSAN_EXPECT_FIRES(test, res = val << shift);
(void)res;
}
static void test_ubsan_load_invalid_bool(struct sbiunit_test_case *test)
{
volatile char bool_val = 5;
volatile bool *b_ptr = (bool *)&bool_val;
volatile int taken = 0;
UBSAN_EXPECT_FIRES(test, if (*b_ptr) taken = 1);
(void)taken;
}
static void test_ubsan_pointer_overflow(struct sbiunit_test_case *test)
{
volatile uintptr_t base = 0xFFFFFFFFFFFFFFFEUL;
volatile char *ptr = (char *)base;
volatile char *res;
UBSAN_EXPECT_FIRES(test, res = ptr + 5);
(void)res;
}
static struct sbiunit_test_case ubsan_tests[] = {
SBIUNIT_TEST_CASE(test_ubsan_add_overflow),
SBIUNIT_TEST_CASE(test_ubsan_sub_overflow),
SBIUNIT_TEST_CASE(test_ubsan_mul_overflow),
SBIUNIT_TEST_CASE(test_ubsan_divrem),
SBIUNIT_TEST_CASE(test_ubsan_oob),
SBIUNIT_TEST_CASE(test_ubsan_shift_too_large),
SBIUNIT_TEST_CASE(test_ubsan_shift_negative),
SBIUNIT_TEST_CASE(test_ubsan_load_invalid_bool),
SBIUNIT_TEST_CASE(test_ubsan_pointer_overflow),
SBIUNIT_END_CASE,
};
SBIUNIT_TEST_SUITE(ubsan_test_suite, ubsan_tests);