[PATCH v2] lib: sbi: add UBSan support
Marcos Oduardo
marcos.oduardo at gmail.com
Sun Feb 22 14:40:33 PST 2026
From: marcos <marcos.oduardo at gmail.com>
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,
incurring unexpected results in the execution of the program. Compiling
and testing the OpenSBI firmware against UBSan will print a message in
the console if any sentence performs any of these actions.
Support for this implementation 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 to verify correct operation at runtime.
Usage:
Users may compile OpenSBI with the UBSan instrumentation adding the
next flag to the make command:
ENABLEUBSAN=y
Users may compile OpenSBI with the UBSan tests adding the next flag to
the make command:
ENABLEUBSANTESTS=y
Note that the implementation of UBSan in the compilation of OpenSBI
adds a certain overhead caused by the checks performed at runtime;
therefore, it is only expected to be used in development builds, never
in production. If ENABLEUBSAN is not set, tests won't be compiled even
if the ENABLEUBSANTESTS flag is enabled.
Signed-off-by: Marcos Oduardo <marcos.oduardo at gmail.com>
---
Makefile | 14 +
include/sbi/sbi_ubsan_test.h | 8 +
lib/sbi/objects.mk | 6 +
lib/sbi/sbi_init.c | 6 +
lib/sbi/sbi_ubsan.c | 849 +++++++++++++++++
lib/sbi/sbi_ubsan_test.c | 147 +++
v2-0001-lib-sbi-add-UBSan-support.patch | 1145 +++++++++++++++++++++++
7 files changed, 2175 insertions(+)
create mode 100644 include/sbi/sbi_ubsan_test.h
create mode 100644 lib/sbi/sbi_ubsan.c
create mode 100644 lib/sbi/sbi_ubsan_test.c
create mode 100644 v2-0001-lib-sbi-add-UBSan-support.patch
diff --git a/Makefile b/Makefile
index 46541063..0689242b 100644
--- a/Makefile
+++ b/Makefile
@@ -455,6 +455,20 @@ else
CFLAGS += -O2
endif
+ifeq ($(ENABLEUBSAN),y)
+UBSAN_CC_FLAGS := -fsanitize=undefined
+UBSAN_CC_FLAGS += -DUBSAN_ENABLED
+ifeq ($(ENABLEUBSANTESTS),y)
+UBSAN_CC_FLAGS += -DUBSAN_TESTS_ENABLED
+endif
+UBSAN_CC_FLAGS += -fno-sanitize=vptr
+UBSAN_CC_FLAGS += -fno-sanitize=float-cast-overflow
+UBSAN_CC_FLAGS += -fno-sanitize=float-divide-by-zero
+UBSAN_CC_FLAGS += -fsanitize-recover=undefined
+UBSAN_CC_FLAGS += -fsanitize=pointer-overflow
+CFLAGS += $(UBSAN_CC_FLAGS)
+endif
+
ifeq ($(V), 1)
ELFFLAGS += -Wl,--print-gc-sections
endif
diff --git a/include/sbi/sbi_ubsan_test.h b/include/sbi/sbi_ubsan_test.h
new file mode 100644
index 00000000..13c6a562
--- /dev/null
+++ b/include/sbi/sbi_ubsan_test.h
@@ -0,0 +1,8 @@
+#ifdef UBSAN_TESTS_ENABLED
+#ifndef __SBI_UBSAN_TEST_H__
+#define __SBI_UBSAN_TEST_H__
+
+void sbi_ubsan_test_suite(void);
+
+#endif
+#endif
\ No newline at end of file
diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
index 07d13229..b6f97120 100644
--- a/lib/sbi/objects.mk
+++ b/lib/sbi/objects.mk
@@ -99,6 +99,12 @@ libsbi-objs-y += sbi_tlb.o
libsbi-objs-y += sbi_trap.o
libsbi-objs-y += sbi_trap_ldst.o
libsbi-objs-y += sbi_trap_v_ldst.o
+ifeq ($(ENABLEUBSAN), y)
+libsbi-objs-y += sbi_ubsan.o
+ifeq ($(ENABLEUBSANTESTS), y)
+libsbi-objs-y += sbi_ubsan_test.o
+endif
+endif
libsbi-objs-y += sbi_unpriv.o
libsbi-objs-y += sbi_expected_trap.o
libsbi-objs-y += sbi_cppc.o
diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c
index 5259064b..5155716c 100644
--- a/lib/sbi/sbi_init.c
+++ b/lib/sbi/sbi_init.c
@@ -35,6 +35,8 @@
#include <sbi/sbi_tlb.h>
#include <sbi/sbi_version.h>
#include <sbi/sbi_unit_test.h>
+#include <sbi/sbi_ubsan_test.h>
+
#define BANNER \
" ____ _____ ____ _____\n" \
@@ -288,6 +290,10 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
sbi_double_trap_init(scratch);
+ #ifdef UBSAN_TESTS_ENABLED
+ sbi_ubsan_test_suite();
+ #endif
+
rc = sbi_irqchip_init(scratch, true);
if (rc) {
sbi_printf("%s: irqchip init failed (error %d)\n",
diff --git a/lib/sbi/sbi_ubsan.c b/lib/sbi/sbi_ubsan.c
new file mode 100644
index 00000000..f341275f
--- /dev/null
+++ b/lib/sbi/sbi_ubsan.c
@@ -0,0 +1,849 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Marcos Oduardo <marcos.oduardo at gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef UBSAN_ENABLED
+
+#include <sbi/sbi_types.h>
+#include <sbi/sbi_console.h>
+
+/* Undefined Behavior specific defines and structures. Type descriptors defined by the compiler ABI */
+
+#define KIND_INTEGER 0
+#define KIND_FLOAT 1
+#define KIND_UNKNOWN UINT16_MAX
+
+struct CSourceLocation {
+ char *mFilename;
+ uint32_t mLine;
+ uint32_t mColumn;
+};
+
+struct CTypeDescriptor {
+ uint16_t mTypeKind;
+ uint16_t mTypeInfo;
+ uint8_t mTypeName[1];
+};
+
+struct COverflowData {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mType;
+};
+
+struct CUnreachableData {
+ struct CSourceLocation mLocation;
+};
+
+struct CCFICheckFailData {
+ uint8_t mCheckKind;
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mType;
+};
+
+struct CDynamicTypeCacheMissData {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mType;
+ void *mTypeInfo;
+ uint8_t mTypeCheckKind;
+};
+
+struct CFunctionTypeMismatchData {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mType;
+};
+
+struct CInvalidBuiltinData {
+ struct CSourceLocation mLocation;
+ uint8_t mKind;
+};
+
+struct CInvalidValueData {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mType;
+};
+
+struct CNonNullArgData {
+ struct CSourceLocation mLocation;
+ struct CSourceLocation mAttributeLocation;
+ int mArgIndex;
+};
+
+struct CNonNullReturnData {
+ struct CSourceLocation mAttributeLocation;
+};
+
+struct COutOfBoundsData {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mArrayType;
+ struct CTypeDescriptor *mIndexType;
+};
+
+struct CPointerOverflowData {
+ struct CSourceLocation mLocation;
+};
+
+struct CShiftOutOfBoundsData {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mLHSType;
+ struct CTypeDescriptor *mRHSType;
+};
+
+struct CTypeMismatchData {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mType;
+ unsigned long mLogAlignment;
+ uint8_t mTypeCheckKind;
+};
+
+struct CTypeMismatchData_v1 {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mType;
+ uint8_t mLogAlignment;
+ uint8_t mTypeCheckKind;
+};
+
+struct CVLABoundData {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mType;
+};
+
+struct CFloatCastOverflowData {
+ struct CSourceLocation mLocation; /* This field exists in this struct since 2015 August 11th */
+ struct CTypeDescriptor *mFromType;
+ struct CTypeDescriptor *mToType;
+};
+
+struct CImplicitConversionData {
+ struct CSourceLocation mLocation;
+ struct CTypeDescriptor *mFromType;
+ struct CTypeDescriptor *mToType;
+ uint8_t mKind;
+};
+
+struct CAlignmentAssumptionData {
+ struct CSourceLocation mLocation;
+ struct CSourceLocation mAssumptionLocation;
+ struct CTypeDescriptor *mType;
+};
+
+/* Public symbols used in the instrumentation of the code generation part */
+void __ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_alignment_assumption(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);
+void __ubsan_handle_alignment_assumption_abort(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);
+void __ubsan_handle_builtin_unreachable(struct CUnreachableData *pData);
+void __ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
+void __ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
+void __ubsan_handle_function_type_mismatch_v1(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction, unsigned long ulCalleeRTTI, unsigned long ulFnRTTI);
+void __ubsan_handle_function_type_mismatch_v1_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction, unsigned long ulCalleeRTTI, unsigned long ulFnRTTI);
+void __ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData);
+void __ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData);
+void __ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulVal);
+void __ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulVal);
+void __ubsan_handle_missing_return(struct CUnreachableData *pData);
+void __ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldVal);
+void __ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldVal);
+void __ubsan_handle_nullability_arg(struct CNonNullArgData *pData);
+void __ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData);
+void __ubsan_handle_nullability_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
+void __ubsan_handle_nullability_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
+void __ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex);
+void __ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex);
+void __ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
+void __ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
+void __ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
+void __ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, unsigned long ulPointer);
+void __ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, unsigned long ulPointer);
+void __ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer);
+void __ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer);
+void __ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound);
+void __ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound);
+void __ubsan_get_current_report_data(const char **ppOutIssueKind, const char **ppOutMessage, const char **ppOutFilename, uint32_t *pOutLine, uint32_t *pOutCol, char **ppOutMemoryAddr);
+
+#if 0
+Unimplemented:
+
+void __ubsan_handle_implicit_conversion(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo);
+void __ubsan_handle_implicit_conversion_abort(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo);
+void __ubsan_handle_nonnull_arg(struct CNonNullArgData *pData);
+void __ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData);
+void __ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
+void __ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
+void __ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom);
+void __ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom);
+void __ubsan_handle_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
+void __ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
+void __ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer);
+void __ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable);
+void __ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable);
+#endif
+
+static void HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation);
+static void HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue);
+static void HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData);
+static void HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer);
+static void HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound);
+static void HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex);
+static void HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
+static void HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue);
+static void HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData);
+static void HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
+static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData);
+static void HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
+static void HandleAlignmentAssumption(bool isFatal, struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);
+
+#if 0
+Unimplemented:
+
+static void HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer);
+static void HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData);
+static void HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
+static void HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
+static void HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom);
+#endif
+
+
+#define NUMBER_SIGNED_BIT 1
+#define NUMBER_MAXLEN 128
+#define __arraycount(__a) (sizeof(__a) / sizeof(__a[0]))
+#define __BIT(__n) (1UL << (__n))
+#define SEPARATOR sbi_printf("===========================================\n")
+#define ACK_REPORTED (1U << 31)
+
+static bool isAlreadyReported(struct CSourceLocation *pLocation)
+{
+ uint32_t siOldValue;
+ volatile uint32_t *pLine;
+
+ if (!pLocation)
+ return false;
+
+ pLine = &pLocation->mLine;
+
+ do {
+ siOldValue = *pLine;
+ } while (__sync_val_compare_and_swap(pLine, siOldValue, siOldValue | ACK_REPORTED) != siOldValue);
+
+ return ((siOldValue) & (ACK_REPORTED));
+}
+
+static void HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation)
+{
+ if(!pData)
+ {
+ return;
+ }
+
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT;
+
+ sbi_printf("UBSan: %s integer overflow: ", is_signed ? "signed" : "unsigned");
+
+ if (is_signed){
+ sbi_printf("%ld %s %ld ",(long)ulLHS, szOperation, (long)ulRHS);
+ }
+ else{
+ sbi_printf("%lu %s %lu ", ulLHS, szOperation, ulRHS);
+ }
+
+ sbi_printf("cannot be represented in type %s\n", pData->mType->mTypeName);
+
+ SEPARATOR;
+}
+
+static void HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue)
+{
+ if(!pData)
+ {
+ return;
+ }
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT;
+
+ sbi_printf("UBSan: Negation of ");
+
+ if (is_signed){
+ sbi_printf("%ld", (long)ulOldValue);
+ }
+ else{
+ sbi_printf("%lu", ulOldValue);
+ }
+
+ sbi_printf("cannot be represented in type %s\n", pData->mType->mTypeName);
+
+ SEPARATOR;
+}
+
+static void HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData)
+{
+ if(!pData)
+ {
+ return;
+ }
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ SEPARATOR;
+}
+
+const char *rgczTypeCheckKinds[] = {
+ "load of",
+ "store to",
+ "reference binding to",
+ "member access within",
+ "member call on",
+ "constructor call on",
+ "downcast of",
+ "downcast of",
+ "upcast of",
+ "cast to virtual base of",
+ "_Nonnull binding to",
+ "dynamic operation on"
+};
+
+static void HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer)
+{
+
+ if((!mLocation) || (!mType)) {
+ return;
+ }
+
+ if (isAlreadyReported(mLocation))
+ {
+ return;
+ }
+
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ mLocation->mFilename,
+ mLocation->mLine & ~ACK_REPORTED,
+ mLocation->mColumn);
+
+ const char *kind = (mTypeCheckKind < __arraycount(rgczTypeCheckKinds)) ? rgczTypeCheckKinds[mTypeCheckKind] : "access to";
+
+ if (ulPointer == 0) {
+ sbi_printf("%s null pointer of type %s\n", kind, mType->mTypeName);
+ }
+ else if ((mLogAlignment - 1) & ulPointer) { //mLogAlignment is converted on the wrapper function call
+ sbi_printf("%s misaligned address %p for type %s which requires %ld byte alignment\n", kind, (void *)ulPointer, mType->mTypeName, mLogAlignment);
+ }
+ else {
+ sbi_printf("%s address %p with insufficient space for an object of type %s\n", kind, (void*)ulPointer, mType->mTypeName);
+ }
+ SEPARATOR;
+}
+
+static void HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound)
+{
+ if(!pData)
+ {
+ return;
+ }
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ sbi_printf("variable length array bound value ");
+
+ bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT;
+
+ if (is_signed){
+ sbi_printf("%ld", (long)ulBound);
+ }
+ else{
+ sbi_printf("%lu", ulBound);
+ }
+
+ sbi_printf(" <= 0\n");
+
+ SEPARATOR;
+}
+
+static void HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex)
+{
+ if(!pData)
+ {
+ return;
+ }
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ bool is_signed = pData->mIndexType->mTypeInfo & NUMBER_SIGNED_BIT;
+
+ if (is_signed){
+ sbi_printf("index %ld", (long)ulIndex);
+ }
+ else{
+ sbi_printf("index %lu", ulIndex);
+ }
+
+ sbi_printf(" is out of range for type %s\n", pData->mArrayType->mTypeName);
+
+ SEPARATOR;
+}
+
+static bool isNegativeNumber(struct CTypeDescriptor *pType, unsigned long ulVal)
+{
+ if (!(pType->mTypeInfo & NUMBER_SIGNED_BIT))
+ {
+ return false;
+ }
+
+ return (long)ulVal < 0;
+}
+
+static size_t type_width(struct CTypeDescriptor *pType)
+{
+ return 1UL << (pType->mTypeInfo >> 1);
+}
+
+static void HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ if(!pData)
+ {
+ return;
+ }
+
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ if (isNegativeNumber(pData->mRHSType, ulRHS)) {
+ sbi_printf("shift exponent %ld is negative\n", (long)ulRHS);
+ }
+ else if (ulRHS >= type_width(pData->mLHSType)) {
+ sbi_printf("shift exponent %lu is too large for %lu-bit type %s\n", ulRHS, (unsigned long)type_width(pData->mLHSType), pData->mLHSType->mTypeName);
+
+ }
+ else if (isNegativeNumber(pData->mLHSType, ulLHS)) {
+ sbi_printf("left shift of negative value %ld\n", (long)ulLHS);
+ }
+ else {
+ sbi_printf("left shift of %lu by %lu places cannot be represented in type %s\n", ulLHS, ulRHS, pData->mLHSType->mTypeName);
+ }
+
+ SEPARATOR;
+}
+
+static void HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue)
+{
+ if(!pData)
+ {
+ return;
+ }
+
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT;
+
+ sbi_printf("load of value ");
+
+ if (is_signed){
+ sbi_printf("%ld ", (long)ulValue);
+ }
+ else{
+ sbi_printf("%lu ", ulValue);
+ }
+
+ sbi_printf("is not a valid value for type %s\n", pData->mType->mTypeName);
+
+ SEPARATOR;
+}
+
+const char *rgczBuiltinCheckKinds[] = {
+ "ctz()",
+ "clz()"
+};
+
+
+static void HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData)
+{
+ if(!pData)
+ {
+ return;
+ }
+
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ const char *builtin = (pData->mKind < __arraycount(rgczBuiltinCheckKinds)) ? rgczBuiltinCheckKinds[pData->mKind] : "unknown builtin";
+
+ sbi_printf("passing zero to %s, which is not a valid argument\n", builtin);
+
+ SEPARATOR;
+}
+
+static void HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
+{
+ if(!pData)
+ {
+ return;
+ }
+
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+
+ sbi_printf("call to function %#lx through pointer to incorrect function type %s\n", ulFunction, pData->mType->mTypeName);
+
+ SEPARATOR;
+}
+
+static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData)
+{
+ if(!pData)
+ {
+ return;
+ }
+
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ sbi_printf("execution reached the end of a value-returning function without returning a value\n");
+
+ SEPARATOR;
+}
+
+static void HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
+{
+ if(!pData)
+ {
+ return;
+ }
+
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ sbi_printf("pointer expression with base %#lx overflowed to %#lx\n", ulBase, ulResult);
+
+ SEPARATOR;
+}
+
+static void HandleAlignmentAssumption(bool isFatal, struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
+{
+
+ if(!pData)
+ {
+ return;
+ }
+
+ if (isAlreadyReported(&pData->mLocation))
+ {
+ return;
+ }
+
+ SEPARATOR;
+
+ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
+ pData->mLocation.mFilename,
+ pData->mLocation.mLine & ~ACK_REPORTED,
+ pData->mLocation.mColumn);
+
+ unsigned long ulRealPointer = ulPointer - ulOffset;
+ sbi_printf("alignment assumption of %lu for pointer %p (offset %p)", ulAlignment, (void*) ulRealPointer, (void*) ulOffset);
+
+ if (pData->mAssumptionLocation.mFilename != NULL) {
+ sbi_printf(", assumption made in %s:%u:%u",
+ pData->mAssumptionLocation.mFilename,
+ pData->mAssumptionLocation.mLine,
+ pData->mAssumptionLocation.mColumn);
+ }
+
+ sbi_printf("\n");
+
+ SEPARATOR;
+}
+
+/* Definions of public symbols emitted by the instrumentation code */
+void __ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleOverflow(false, pData, ulLHS, ulRHS, "+");
+}
+
+void __ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleOverflow(true, pData, ulLHS, ulRHS, "+");
+}
+
+void __ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleOverflow(false, pData, ulLHS, ulRHS, "-");
+}
+
+void __ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleOverflow(true, pData, ulLHS, ulRHS, "-");
+}
+
+void __ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleOverflow(false, pData, ulLHS, ulRHS, "*");
+}
+
+void __ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleOverflow(true, pData, ulLHS, ulRHS, "*");
+}
+
+void __ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldValue)
+{
+ HandleNegateOverflow(false, pData, ulOldValue);
+}
+
+void __ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldValue)
+{
+ HandleNegateOverflow(true, pData, ulOldValue);
+}
+
+void __ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleOverflow(false, pData, ulLHS, ulRHS, "divrem");
+}
+
+void __ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleOverflow(true, pData, ulLHS, ulRHS, "divrem");
+}
+
+void __ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer)
+{
+ HandleTypeMismatch(false, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer);
+}
+
+void __ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer)
+{
+ HandleTypeMismatch(true, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer);
+}
+
+void __ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex)
+{
+ HandleOutOfBounds(false, pData, ulIndex);
+}
+
+void __ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex)
+{
+ HandleOutOfBounds(true, pData, ulIndex);
+}
+
+void __ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleShiftOutOfBounds(false, pData, ulLHS, ulRHS);
+}
+
+void __ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
+{
+ HandleShiftOutOfBounds(true, pData, ulLHS, ulRHS);
+}
+
+void __ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
+{
+ HandlePointerOverflow(false, pData, ulBase, ulResult);
+}
+
+void __ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
+{
+ HandlePointerOverflow(true, pData, ulBase, ulResult);
+}
+
+void __ubsan_handle_alignment_assumption(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
+{
+ HandleAlignmentAssumption(false, pData, ulPointer, ulAlignment, ulOffset);
+}
+
+void __ubsan_handle_alignment_assumption_abort(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
+{
+ HandleAlignmentAssumption(true, pData, ulPointer, ulAlignment, ulOffset);
+}
+
+void __ubsan_handle_builtin_unreachable(struct CUnreachableData *pData)
+{
+ HandleBuiltinUnreachable(true, pData);
+}
+
+void __ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData)
+{
+ HandleInvalidBuiltin(true, pData);
+}
+
+void __ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData)
+{
+ HandleInvalidBuiltin(true, pData);
+}
+
+void __ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulValue)
+{
+ HandleLoadInvalidValue(false, pData, ulValue);
+}
+
+void __ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulValue)
+{
+ HandleLoadInvalidValue(true, pData, ulValue);
+}
+
+void __ubsan_handle_missing_return(struct CUnreachableData *pData)
+{
+ HandleMissingReturn(true, pData);
+}
+
+void __ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound)
+{
+ HandleVlaBoundNotPositive(false, pData, ulBound);
+}
+
+void __ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound)
+{
+ HandleVlaBoundNotPositive(true, pData, ulBound);
+}
+
+void __ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
+{
+ HandleFunctionTypeMismatch(false, pData, ulFunction);
+}
+
+void __ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
+{
+ HandleFunctionTypeMismatch(true, pData, ulFunction);
+}
+#endif
\ No newline at end of file
diff --git a/lib/sbi/sbi_ubsan_test.c b/lib/sbi/sbi_ubsan_test.c
new file mode 100644
index 00000000..487790ef
--- /dev/null
+++ b/lib/sbi/sbi_ubsan_test.c
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Marcos Oduardo <marcos.oduardo at gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef UBSAN_TESTS_ENABLED
+#include <sbi/sbi_types.h>
+#include <sbi/sbi_console.h>
+
+void sbi_ubsan_test_suite(void)
+{
+ sbi_printf("\n[UBSan Test] Starting NetBSD-based test suite\n");
+
+ {
+ sbi_printf("\n[UBSan Test] Add Overflow Test\n");
+ volatile int a = 0x7FFFFFFF;
+ (void)a;
+ volatile int b = 1;
+ (void)b;
+ volatile int c = a + b;
+ (void)c;
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Sub Overflow Test\n");
+ volatile int a = 0x80000000;
+ (void)a;
+ volatile int b = 1;
+ (void)b;
+ volatile int c = a - b;
+ (void)c;
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Mul Overflow Test\n");
+ volatile int a = 0x7FFFFFFF;
+ (void)a;
+ volatile int b = 2;
+ (void)b;
+ volatile int c = a * b;
+ (void)c;
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Div/Rem Overflow Test\n");
+ volatile int a = 10;
+ (void)a;
+ volatile int b = 0;
+ (void)b;
+ volatile int c = a / b;
+ (void)c;
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Index Out of Bounds Test\n");
+ volatile int idx = 5;
+ (void)idx;
+ int arr[3] = {1, 2, 3};
+ volatile int val = arr[idx];
+ (void)val;
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Shift Exponent Too Large Test\n");
+ volatile unsigned long val = 1;
+ (void)val;
+ volatile int shift = 64;
+ (void)shift;
+ volatile unsigned long res = val << shift;
+ (void)res;
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Shift Exponent Negative Test\n");
+ volatile int val = 1;
+ (void)val;
+ volatile int shift = -1;
+ (void)shift;
+ volatile int res = val << shift;
+ (void)res;
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Misaligned Access Test\n");
+ char buffer[16] __attribute__((aligned(16)));
+ volatile int *ptr = (int *)&buffer[1];
+ (void)ptr;
+ volatile int val = *ptr;
+ (void)val;
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Null Dereference Test\n");
+ volatile int *ptr = NULL;
+ (void)ptr;
+ sbi_printf("\n[UBSan Test] Uncomment next two lines of code for testing Null dereference\n");
+
+ //volatile int val = *ptr;
+ //(void)val;
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Load Invalid Value Test\n");
+ volatile char bool_val = 5;
+ (void)bool_val;
+ volatile bool *b_ptr = (bool *)&bool_val;
+ (void)b_ptr;
+ if (*b_ptr) { (void)0; }
+ }
+
+ {
+ sbi_printf("\n[UBSan Test] Pointer Overflow Test\n");
+ volatile uintptr_t base = 0xFFFFFFFFFFFFFFFEUL;
+ (void)base;
+ volatile char *ptr = (char *)base;
+ (void)ptr;
+ volatile char *res = ptr + 5;
+ (void)res;
+ }
+
+ sbi_printf("\n[UBSan Test] All tests dispatched successfully.\n\n");
+}
+#endif
\ No newline at end of file
diff --git a/v2-0001-lib-sbi-add-UBSan-support.patch b/v2-0001-lib-sbi-add-UBSan-support.patch
new file mode 100644
index 00000000..d27729d0
--- /dev/null
+++ b/v2-0001-lib-sbi-add-UBSan-support.patch
@@ -0,0 +1,1145 @@
+From 8d4d6025f448bbbcc07117dbd12bdd6f95c16606 Mon Sep 17 00:00:00 2001
+From: Marcos Oduardo <marcos.oduardo at gmail.com>
+Date: Sun, 22 Feb 2026 23:00:45 +0100
+Subject: [PATCH v2] 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,
+incurring unexpected results in the execution of the program. Compiling
+and testing the OpenSBI firmware against UBSan will print a message in
+the console if any sentence performs any of these actions.
+
+Support for this implementation 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 to verify correct operation at runtime.
+
+Usage:
+
+Users may compile OpenSBI with the UBSan instrumentation adding the
+next flag to the make command:
+ENABLEUBSAN=y
+
+Users may compile OpenSBI with the UBSan tests adding the next flag to
+the make command:
+ENABLEUBSANTESTS=y
+
+Note that the implementation of UBSan in the compilation of OpenSBI
+adds a certain overhead caused by the checks performed at runtime;
+therefore, it is only expected to be used in development builds, never
+in production. If ENABLEUBSAN is not set, tests won't be compiled even
+if the ENABLEUBSANTESTS flag is enabled.
+
+Signed-off-by: Marcos Oduardo <marcos.oduardo at gmail.com>
+---
+Changes since v1:
+
+ - Based the UBSan implementation from NetBSD, fixing the incompatible
+ licenses problem.
+
+ Makefile | 14 +
+ include/sbi/sbi_ubsan_test.h | 8 +
+ lib/sbi/objects.mk | 6 +
+ lib/sbi/sbi_init.c | 6 +
+ lib/sbi/sbi_ubsan.c | 849 +++++++++++++++++++++++++++++++++++
+ lib/sbi/sbi_ubsan_test.c | 147 ++++++
+ 6 files changed, 1030 insertions(+)
+ create mode 100644 include/sbi/sbi_ubsan_test.h
+ create mode 100644 lib/sbi/sbi_ubsan.c
+ create mode 100644 lib/sbi/sbi_ubsan_test.c
+
+diff --git a/Makefile b/Makefile
+index 46541063..0689242b 100644
+--- a/Makefile
++++ b/Makefile
+@@ -455,6 +455,20 @@ else
+ CFLAGS += -O2
+ endif
+
++ifeq ($(ENABLEUBSAN),y)
++UBSAN_CC_FLAGS := -fsanitize=undefined
++UBSAN_CC_FLAGS += -DUBSAN_ENABLED
++ifeq ($(ENABLEUBSANTESTS),y)
++UBSAN_CC_FLAGS += -DUBSAN_TESTS_ENABLED
++endif
++UBSAN_CC_FLAGS += -fno-sanitize=vptr
++UBSAN_CC_FLAGS += -fno-sanitize=float-cast-overflow
++UBSAN_CC_FLAGS += -fno-sanitize=float-divide-by-zero
++UBSAN_CC_FLAGS += -fsanitize-recover=undefined
++UBSAN_CC_FLAGS += -fsanitize=pointer-overflow
++CFLAGS += $(UBSAN_CC_FLAGS)
++endif
++
+ ifeq ($(V), 1)
+ ELFFLAGS += -Wl,--print-gc-sections
+ endif
+diff --git a/include/sbi/sbi_ubsan_test.h b/include/sbi/sbi_ubsan_test.h
+new file mode 100644
+index 00000000..13c6a562
+--- /dev/null
++++ b/include/sbi/sbi_ubsan_test.h
+@@ -0,0 +1,8 @@
++#ifdef UBSAN_TESTS_ENABLED
++#ifndef __SBI_UBSAN_TEST_H__
++#define __SBI_UBSAN_TEST_H__
++
++void sbi_ubsan_test_suite(void);
++
++#endif
++#endif
+\ No newline at end of file
+diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
+index 07d13229..b6f97120 100644
+--- a/lib/sbi/objects.mk
++++ b/lib/sbi/objects.mk
+@@ -99,6 +99,12 @@ libsbi-objs-y += sbi_tlb.o
+ libsbi-objs-y += sbi_trap.o
+ libsbi-objs-y += sbi_trap_ldst.o
+ libsbi-objs-y += sbi_trap_v_ldst.o
++ifeq ($(ENABLEUBSAN), y)
++libsbi-objs-y += sbi_ubsan.o
++ifeq ($(ENABLEUBSANTESTS), y)
++libsbi-objs-y += sbi_ubsan_test.o
++endif
++endif
+ libsbi-objs-y += sbi_unpriv.o
+ libsbi-objs-y += sbi_expected_trap.o
+ libsbi-objs-y += sbi_cppc.o
+diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c
+index 5259064b..5155716c 100644
+--- a/lib/sbi/sbi_init.c
++++ b/lib/sbi/sbi_init.c
+@@ -35,6 +35,8 @@
+ #include <sbi/sbi_tlb.h>
+ #include <sbi/sbi_version.h>
+ #include <sbi/sbi_unit_test.h>
++#include <sbi/sbi_ubsan_test.h>
++
+
+ #define BANNER \
+ " ____ _____ ____ _____\n" \
+@@ -288,6 +290,10 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
+
+ sbi_double_trap_init(scratch);
+
++ #ifdef UBSAN_TESTS_ENABLED
++ sbi_ubsan_test_suite();
++ #endif
++
+ rc = sbi_irqchip_init(scratch, true);
+ if (rc) {
+ sbi_printf("%s: irqchip init failed (error %d)\n",
+diff --git a/lib/sbi/sbi_ubsan.c b/lib/sbi/sbi_ubsan.c
+new file mode 100644
+index 00000000..df3d1742
+--- /dev/null
++++ b/lib/sbi/sbi_ubsan.c
+@@ -0,0 +1,849 @@
++/* SPDX-License-Identifier: BSD-2-Clause
++ *
++ * Copyright (c) 2018 The NetBSD Foundation, Inc.
++ * All rights reserved.
++ *
++ * Author: Marcos Oduardo <marcos.oduardo at gmail.com>
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
++ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
++ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ * POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#ifdef UBSAN_ENABLED
++
++#include <sbi/sbi_types.h>
++#include <sbi/sbi_console.h>
++
++/* Undefined Behavior specific defines and structures. Type descriptors defined by the compiler ABI */
++
++#define KIND_INTEGER 0
++#define KIND_FLOAT 1
++#define KIND_UNKNOWN UINT16_MAX
++
++struct CSourceLocation {
++ char *mFilename;
++ uint32_t mLine;
++ uint32_t mColumn;
++};
++
++struct CTypeDescriptor {
++ uint16_t mTypeKind;
++ uint16_t mTypeInfo;
++ uint8_t mTypeName[1];
++};
++
++struct COverflowData {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mType;
++};
++
++struct CUnreachableData {
++ struct CSourceLocation mLocation;
++};
++
++struct CCFICheckFailData {
++ uint8_t mCheckKind;
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mType;
++};
++
++struct CDynamicTypeCacheMissData {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mType;
++ void *mTypeInfo;
++ uint8_t mTypeCheckKind;
++};
++
++struct CFunctionTypeMismatchData {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mType;
++};
++
++struct CInvalidBuiltinData {
++ struct CSourceLocation mLocation;
++ uint8_t mKind;
++};
++
++struct CInvalidValueData {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mType;
++};
++
++struct CNonNullArgData {
++ struct CSourceLocation mLocation;
++ struct CSourceLocation mAttributeLocation;
++ int mArgIndex;
++};
++
++struct CNonNullReturnData {
++ struct CSourceLocation mAttributeLocation;
++};
++
++struct COutOfBoundsData {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mArrayType;
++ struct CTypeDescriptor *mIndexType;
++};
++
++struct CPointerOverflowData {
++ struct CSourceLocation mLocation;
++};
++
++struct CShiftOutOfBoundsData {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mLHSType;
++ struct CTypeDescriptor *mRHSType;
++};
++
++struct CTypeMismatchData {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mType;
++ unsigned long mLogAlignment;
++ uint8_t mTypeCheckKind;
++};
++
++struct CTypeMismatchData_v1 {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mType;
++ uint8_t mLogAlignment;
++ uint8_t mTypeCheckKind;
++};
++
++struct CVLABoundData {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mType;
++};
++
++struct CFloatCastOverflowData {
++ struct CSourceLocation mLocation; /* This field exists in this struct since 2015 August 11th */
++ struct CTypeDescriptor *mFromType;
++ struct CTypeDescriptor *mToType;
++};
++
++struct CImplicitConversionData {
++ struct CSourceLocation mLocation;
++ struct CTypeDescriptor *mFromType;
++ struct CTypeDescriptor *mToType;
++ uint8_t mKind;
++};
++
++struct CAlignmentAssumptionData {
++ struct CSourceLocation mLocation;
++ struct CSourceLocation mAssumptionLocation;
++ struct CTypeDescriptor *mType;
++};
++
++/* Public symbols used in the instrumentation of the code generation part */
++void __ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_alignment_assumption(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);
++void __ubsan_handle_alignment_assumption_abort(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);
++void __ubsan_handle_builtin_unreachable(struct CUnreachableData *pData);
++void __ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
++void __ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
++void __ubsan_handle_function_type_mismatch_v1(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction, unsigned long ulCalleeRTTI, unsigned long ulFnRTTI);
++void __ubsan_handle_function_type_mismatch_v1_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction, unsigned long ulCalleeRTTI, unsigned long ulFnRTTI);
++void __ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData);
++void __ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData);
++void __ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulVal);
++void __ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulVal);
++void __ubsan_handle_missing_return(struct CUnreachableData *pData);
++void __ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldVal);
++void __ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldVal);
++void __ubsan_handle_nullability_arg(struct CNonNullArgData *pData);
++void __ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData);
++void __ubsan_handle_nullability_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
++void __ubsan_handle_nullability_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
++void __ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex);
++void __ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex);
++void __ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
++void __ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
++void __ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
++void __ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, unsigned long ulPointer);
++void __ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, unsigned long ulPointer);
++void __ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer);
++void __ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer);
++void __ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound);
++void __ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound);
++void __ubsan_get_current_report_data(const char **ppOutIssueKind, const char **ppOutMessage, const char **ppOutFilename, uint32_t *pOutLine, uint32_t *pOutCol, char **ppOutMemoryAddr);
++
++#if 0
++Unimplemented:
++
++void __ubsan_handle_implicit_conversion(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo);
++void __ubsan_handle_implicit_conversion_abort(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo);
++void __ubsan_handle_nonnull_arg(struct CNonNullArgData *pData);
++void __ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData);
++void __ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
++void __ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
++void __ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom);
++void __ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom);
++void __ubsan_handle_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
++void __ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
++void __ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer);
++void __ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable);
++void __ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable);
++#endif
++
++static void HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation);
++static void HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue);
++static void HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData);
++static void HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer);
++static void HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound);
++static void HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex);
++static void HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
++static void HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue);
++static void HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData);
++static void HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
++static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData);
++static void HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
++static void HandleAlignmentAssumption(bool isFatal, struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);
++
++#if 0
++Unimplemented:
++
++static void HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer);
++static void HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData);
++static void HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
++static void HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
++static void HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom);
++#endif
++
++
++#define NUMBER_SIGNED_BIT 1
++#define NUMBER_MAXLEN 128
++#define __arraycount(__a) (sizeof(__a) / sizeof(__a[0]))
++#define __BIT(__n) (1UL << (__n))
++#define SEPARATOR sbi_printf("===========================================\n")
++#define ACK_REPORTED (1U << 31)
++
++static bool isAlreadyReported(struct CSourceLocation *pLocation)
++{
++ uint32_t siOldValue;
++ volatile uint32_t *pLine;
++
++ if (!pLocation)
++ return false;
++
++ pLine = &pLocation->mLine;
++
++ do {
++ siOldValue = *pLine;
++ } while (__sync_val_compare_and_swap(pLine, siOldValue, siOldValue | ACK_REPORTED) != siOldValue);
++
++ return ((siOldValue) & (ACK_REPORTED));
++}
++
++static void HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation)
++{
++ if(!pData)
++ {
++ return;
++ }
++
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT;
++
++ sbi_printf("UBSan: %s integer overflow: ", is_signed ? "signed" : "unsigned");
++
++ if (is_signed){
++ sbi_printf("%ld %s %ld ",(long)ulLHS, szOperation, (long)ulRHS);
++ }
++ else{
++ sbi_printf("%lu %s %lu ", ulLHS, szOperation, ulRHS);
++ }
++
++ sbi_printf("cannot be represented in type %s\n", pData->mType->mTypeName);
++
++ SEPARATOR;
++}
++
++static void HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue)
++{
++ if(!pData)
++ {
++ return;
++ }
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT;
++
++ sbi_printf("UBSan: Negation of ");
++
++ if (is_signed){
++ sbi_printf("%ld", (long)ulOldValue);
++ }
++ else{
++ sbi_printf("%lu", ulOldValue);
++ }
++
++ sbi_printf("cannot be represented in type %s\n", pData->mType->mTypeName);
++
++ SEPARATOR;
++}
++
++static void HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData)
++{
++ if(!pData)
++ {
++ return;
++ }
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ SEPARATOR;
++}
++
++const char *rgczTypeCheckKinds[] = {
++ "load of",
++ "store to",
++ "reference binding to",
++ "member access within",
++ "member call on",
++ "constructor call on",
++ "downcast of",
++ "downcast of",
++ "upcast of",
++ "cast to virtual base of",
++ "_Nonnull binding to",
++ "dynamic operation on"
++};
++
++static void HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer)
++{
++
++ if((!mLocation) || (!mType)) {
++ return;
++ }
++
++ if (isAlreadyReported(mLocation))
++ {
++ return;
++ }
++
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ mLocation->mFilename,
++ mLocation->mLine & ~ACK_REPORTED,
++ mLocation->mColumn);
++
++ const char *kind = (mTypeCheckKind < __arraycount(rgczTypeCheckKinds)) ? rgczTypeCheckKinds[mTypeCheckKind] : "access to";
++
++ if (ulPointer == 0) {
++ sbi_printf("%s null pointer of type %s\n", kind, mType->mTypeName);
++ }
++ else if ((mLogAlignment - 1) & ulPointer) { //mLogAlignment is converted on the wrapper function call
++ sbi_printf("%s misaligned address %p for type %s which requires %ld byte alignment\n", kind, (void *)ulPointer, mType->mTypeName, mLogAlignment);
++ }
++ else {
++ sbi_printf("%s address %p with insufficient space for an object of type %s\n", kind, (void*)ulPointer, mType->mTypeName);
++ }
++ SEPARATOR;
++}
++
++static void HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound)
++{
++ if(!pData)
++ {
++ return;
++ }
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ sbi_printf("variable length array bound value ");
++
++ bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT;
++
++ if (is_signed){
++ sbi_printf("%ld", (long)ulBound);
++ }
++ else{
++ sbi_printf("%lu", ulBound);
++ }
++
++ sbi_printf(" <= 0\n");
++
++ SEPARATOR;
++}
++
++static void HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex)
++{
++ if(!pData)
++ {
++ return;
++ }
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ bool is_signed = pData->mIndexType->mTypeInfo & NUMBER_SIGNED_BIT;
++
++ if (is_signed){
++ sbi_printf("index %ld", (long)ulIndex);
++ }
++ else{
++ sbi_printf("index %lu", ulIndex);
++ }
++
++ sbi_printf(" is out of range for type %s\n", pData->mArrayType->mTypeName);
++
++ SEPARATOR;
++}
++
++static bool isNegativeNumber(struct CTypeDescriptor *pType, unsigned long ulVal)
++{
++ if (!(pType->mTypeInfo & NUMBER_SIGNED_BIT))
++ {
++ return false;
++ }
++
++ return (long)ulVal < 0;
++}
++
++static size_t type_width(struct CTypeDescriptor *pType)
++{
++ return 1UL << (pType->mTypeInfo >> 1);
++}
++
++static void HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ if(!pData)
++ {
++ return;
++ }
++
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ if (isNegativeNumber(pData->mRHSType, ulRHS)) {
++ sbi_printf("shift exponent %ld is negative\n", (long)ulRHS);
++ }
++ else if (ulRHS >= type_width(pData->mLHSType)) {
++ sbi_printf("shift exponent %lu is too large for %lu-bit type %s\n", ulRHS, (unsigned long)type_width(pData->mLHSType), pData->mLHSType->mTypeName);
++
++ }
++ else if (isNegativeNumber(pData->mLHSType, ulLHS)) {
++ sbi_printf("left shift of negative value %ld\n", (long)ulLHS);
++ }
++ else {
++ sbi_printf("left shift of %lu by %lu places cannot be represented in type %s\n", ulLHS, ulRHS, pData->mLHSType->mTypeName);
++ }
++
++ SEPARATOR;
++}
++
++static void HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue)
++{
++ if(!pData)
++ {
++ return;
++ }
++
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT;
++
++ sbi_printf("load of value ");
++
++ if (is_signed){
++ sbi_printf("%ld ", (long)ulValue);
++ }
++ else{
++ sbi_printf("%lu ", ulValue);
++ }
++
++ sbi_printf("is not a valid value for type %s\n", pData->mType->mTypeName);
++
++ SEPARATOR;
++}
++
++const char *rgczBuiltinCheckKinds[] = {
++ "ctz()",
++ "clz()"
++};
++
++
++static void HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData)
++{
++ if(!pData)
++ {
++ return;
++ }
++
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ const char *builtin = (pData->mKind < __arraycount(rgczBuiltinCheckKinds)) ? rgczBuiltinCheckKinds[pData->mKind] : "unknown builtin";
++
++ sbi_printf("passing zero to %s, which is not a valid argument\n", builtin);
++
++ SEPARATOR;
++}
++
++static void HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
++{
++ if(!pData)
++ {
++ return;
++ }
++
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++
++ sbi_printf("call to function %#lx through pointer to incorrect function type %s\n", ulFunction, pData->mType->mTypeName);
++
++ SEPARATOR;
++}
++
++static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData)
++{
++ if(!pData)
++ {
++ return;
++ }
++
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ sbi_printf("execution reached the end of a value-returning function without returning a value\n");
++
++ SEPARATOR;
++}
++
++static void HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
++{
++ if(!pData)
++ {
++ return;
++ }
++
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ sbi_printf("pointer expression with base %#lx overflowed to %#lx\n", ulBase, ulResult);
++
++ SEPARATOR;
++}
++
++static void HandleAlignmentAssumption(bool isFatal, struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
++{
++
++ if(!pData)
++ {
++ return;
++ }
++
++ if (isAlreadyReported(&pData->mLocation))
++ {
++ return;
++ }
++
++ SEPARATOR;
++
++ sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n",
++ pData->mLocation.mFilename,
++ pData->mLocation.mLine & ~ACK_REPORTED,
++ pData->mLocation.mColumn);
++
++ unsigned long ulRealPointer = ulPointer - ulOffset;
++ sbi_printf("alignment assumption of %lu for pointer %p (offset %p)", ulAlignment, (void*) ulRealPointer, (void*) ulOffset);
++
++ if (pData->mAssumptionLocation.mFilename != NULL) {
++ sbi_printf(", assumption made in %s:%u:%u",
++ pData->mAssumptionLocation.mFilename,
++ pData->mAssumptionLocation.mLine,
++ pData->mAssumptionLocation.mColumn);
++ }
++
++ sbi_printf("\n");
++
++ SEPARATOR;
++}
++
++/* Definions of public symbols emitted by the instrumentation code */
++void __ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleOverflow(false, pData, ulLHS, ulRHS, "+");
++}
++
++void __ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleOverflow(true, pData, ulLHS, ulRHS, "+");
++}
++
++void __ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleOverflow(false, pData, ulLHS, ulRHS, "-");
++}
++
++void __ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleOverflow(true, pData, ulLHS, ulRHS, "-");
++}
++
++void __ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleOverflow(false, pData, ulLHS, ulRHS, "*");
++}
++
++void __ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleOverflow(true, pData, ulLHS, ulRHS, "*");
++}
++
++void __ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldValue)
++{
++ HandleNegateOverflow(false, pData, ulOldValue);
++}
++
++void __ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldValue)
++{
++ HandleNegateOverflow(true, pData, ulOldValue);
++}
++
++void __ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleOverflow(false, pData, ulLHS, ulRHS, "divrem");
++}
++
++void __ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleOverflow(true, pData, ulLHS, ulRHS, "divrem");
++}
++
++void __ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer)
++{
++ HandleTypeMismatch(false, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer);
++}
++
++void __ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer)
++{
++ HandleTypeMismatch(true, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer);
++}
++
++void __ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex)
++{
++ HandleOutOfBounds(false, pData, ulIndex);
++}
++
++void __ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex)
++{
++ HandleOutOfBounds(true, pData, ulIndex);
++}
++
++void __ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleShiftOutOfBounds(false, pData, ulLHS, ulRHS);
++}
++
++void __ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
++{
++ HandleShiftOutOfBounds(true, pData, ulLHS, ulRHS);
++}
++
++void __ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
++{
++ HandlePointerOverflow(false, pData, ulBase, ulResult);
++}
++
++void __ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
++{
++ HandlePointerOverflow(true, pData, ulBase, ulResult);
++}
++
++void __ubsan_handle_alignment_assumption(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
++{
++ HandleAlignmentAssumption(false, pData, ulPointer, ulAlignment, ulOffset);
++}
++
++void __ubsan_handle_alignment_assumption_abort(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
++{
++ HandleAlignmentAssumption(true, pData, ulPointer, ulAlignment, ulOffset);
++}
++
++void __ubsan_handle_builtin_unreachable(struct CUnreachableData *pData)
++{
++ HandleBuiltinUnreachable(true, pData);
++}
++
++void __ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData)
++{
++ HandleInvalidBuiltin(true, pData);
++}
++
++void __ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData)
++{
++ HandleInvalidBuiltin(true, pData);
++}
++
++void __ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulValue)
++{
++ HandleLoadInvalidValue(false, pData, ulValue);
++}
++
++void __ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulValue)
++{
++ HandleLoadInvalidValue(true, pData, ulValue);
++}
++
++void __ubsan_handle_missing_return(struct CUnreachableData *pData)
++{
++ HandleMissingReturn(true, pData);
++}
++
++void __ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound)
++{
++ HandleVlaBoundNotPositive(false, pData, ulBound);
++}
++
++void __ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound)
++{
++ HandleVlaBoundNotPositive(true, pData, ulBound);
++}
++
++void __ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
++{
++ HandleFunctionTypeMismatch(false, pData, ulFunction);
++}
++
++void __ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
++{
++ HandleFunctionTypeMismatch(true, pData, ulFunction);
++}
++#endif
+\ No newline at end of file
+diff --git a/lib/sbi/sbi_ubsan_test.c b/lib/sbi/sbi_ubsan_test.c
+new file mode 100644
+index 00000000..487790ef
+--- /dev/null
++++ b/lib/sbi/sbi_ubsan_test.c
+@@ -0,0 +1,147 @@
++/* SPDX-License-Identifier: BSD-2-Clause
++ *
++ * Copyright (c) 2018 The NetBSD Foundation, Inc.
++ * All rights reserved.
++ *
++ * Author: Marcos Oduardo <marcos.oduardo at gmail.com>
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
++ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
++ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ * POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#ifdef UBSAN_TESTS_ENABLED
++#include <sbi/sbi_types.h>
++#include <sbi/sbi_console.h>
++
++void sbi_ubsan_test_suite(void)
++{
++ sbi_printf("\n[UBSan Test] Starting NetBSD-based test suite\n");
++
++ {
++ sbi_printf("\n[UBSan Test] Add Overflow Test\n");
++ volatile int a = 0x7FFFFFFF;
++ (void)a;
++ volatile int b = 1;
++ (void)b;
++ volatile int c = a + b;
++ (void)c;
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Sub Overflow Test\n");
++ volatile int a = 0x80000000;
++ (void)a;
++ volatile int b = 1;
++ (void)b;
++ volatile int c = a - b;
++ (void)c;
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Mul Overflow Test\n");
++ volatile int a = 0x7FFFFFFF;
++ (void)a;
++ volatile int b = 2;
++ (void)b;
++ volatile int c = a * b;
++ (void)c;
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Div/Rem Overflow Test\n");
++ volatile int a = 10;
++ (void)a;
++ volatile int b = 0;
++ (void)b;
++ volatile int c = a / b;
++ (void)c;
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Index Out of Bounds Test\n");
++ volatile int idx = 5;
++ (void)idx;
++ int arr[3] = {1, 2, 3};
++ volatile int val = arr[idx];
++ (void)val;
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Shift Exponent Too Large Test\n");
++ volatile unsigned long val = 1;
++ (void)val;
++ volatile int shift = 64;
++ (void)shift;
++ volatile unsigned long res = val << shift;
++ (void)res;
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Shift Exponent Negative Test\n");
++ volatile int val = 1;
++ (void)val;
++ volatile int shift = -1;
++ (void)shift;
++ volatile int res = val << shift;
++ (void)res;
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Misaligned Access Test\n");
++ char buffer[16] __attribute__((aligned(16)));
++ volatile int *ptr = (int *)&buffer[1];
++ (void)ptr;
++ volatile int val = *ptr;
++ (void)val;
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Null Dereference Test\n");
++ volatile int *ptr = NULL;
++ (void)ptr;
++ sbi_printf("\n[UBSan Test] Uncomment next two lines of code for testing Null dereference\n");
++
++ //volatile int val = *ptr;
++ //(void)val;
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Load Invalid Value Test\n");
++ volatile char bool_val = 5;
++ (void)bool_val;
++ volatile bool *b_ptr = (bool *)&bool_val;
++ (void)b_ptr;
++ if (*b_ptr) { (void)0; }
++ }
++
++ {
++ sbi_printf("\n[UBSan Test] Pointer Overflow Test\n");
++ volatile uintptr_t base = 0xFFFFFFFFFFFFFFFEUL;
++ (void)base;
++ volatile char *ptr = (char *)base;
++ (void)ptr;
++ volatile char *res = ptr + 5;
++ (void)res;
++ }
++
++ sbi_printf("\n[UBSan Test] All tests dispatched successfully.\n\n");
++}
++#endif
+\ No newline at end of file
+--
+2.53.0
+
--
2.53.0
More information about the opensbi
mailing list