[PATCH v4] lib: sbi: add UBSan support

Marcos Oduardo marcos.oduardo at gmail.com
Fri May 15 09:33:21 PDT 2026


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 at gmail.com>
---
Changes added to this patch:
  - Fixed formatting
  - Integrated the previous tests in the sbi unit test
  - Renamed main flag to UBSAN=y
  - Regarding the license header in sbi_ubsan.c: this patch is 
    BSD2-Clause compatible, but I am not sure whether the upstream 
    NetBSD header can be reduced to SPDX-only given COPYING.BSD, or 
    whether it should be kept in full as done for other imported code 
    in the tree (lib/utils/libquad/). In this patch I've trimmed it, 
    but I will follow whichever you prefer if a v5 is needed.

 Makefile                       |  17 +
 include/sbi/sbi_ubsan.h        |  14 +
 lib/sbi/objects.mk             |   3 +
 lib/sbi/sbi_init.c             |   2 +-
 lib/sbi/sbi_ubsan.c            | 924 +++++++++++++++++++++++++++++++++
 lib/sbi/tests/objects.mk       |   5 +
 lib/sbi/tests/sbi_ubsan_test.c | 114 ++++
 platform/generic/objects.mk    |   5 +
 8 files changed, 1083 insertions(+), 1 deletion(-)
 create mode 100644 include/sbi/sbi_ubsan.h
 create mode 100644 lib/sbi/sbi_ubsan.c
 create mode 100644 lib/sbi/tests/sbi_ubsan_test.c

diff --git a/Makefile b/Makefile
index 46541063..54eb15bc 100644
--- a/Makefile
+++ b/Makefile
@@ -455,6 +455,23 @@ else
 CFLAGS		+=	-O2
 endif
 
+ifeq ($(UBSAN),y)
+UBSAN_CC_FLAGS := -fsanitize=undefined
+UBSAN_CC_FLAGS += -DUBSAN_ENABLED
+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
+UBSAN_CC_FLAGS += -fsanitize=alignment
+UBSAN_CC_FLAGS += -fno-sanitize-recover=alignment
+UBSAN_CC_FLAGS += -fno-stack-protector
+ifeq ($(LLVM), y)
+UBSAN_CC_FLAGS += -fno-sanitize-link-runtime
+endif
+CFLAGS += $(UBSAN_CC_FLAGS)
+endif
+
 ifeq ($(V), 1)
 ELFFLAGS	+=	-Wl,--print-gc-sections
 endif
diff --git a/include/sbi/sbi_ubsan.h b/include/sbi/sbi_ubsan.h
new file mode 100644
index 00000000..f93215c4
--- /dev/null
+++ b/include/sbi/sbi_ubsan.h
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Author: Marcos Oduardo <marcos.oduardo at gmail.com>
+ */
+
+#ifndef __SBI_UBSAN_H__
+#define __SBI_UBSAN_H__
+
+#include <sbi/sbi_types.h>
+
+extern volatile unsigned long sbi_ubsan_report_count;
+
+#endif /* __SBI_UBSAN_H__ */
diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
index 07d13229..f1f5cc31 100644
--- a/lib/sbi/objects.mk
+++ b/lib/sbi/objects.mk
@@ -99,6 +99,9 @@ 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 ($(UBSAN), y)
+libsbi-objs-y += sbi_ubsan.o
+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 b248e73f..4e34bbc5 100644
--- a/lib/sbi/sbi_init.c
+++ b/lib/sbi/sbi_init.c
@@ -218,7 +218,7 @@ static void wake_coldboot_harts(struct sbi_scratch *scratch)
 	__smp_store_release(&coldboot_done, 1);
 }
 
-unsigned long __stack_chk_guard = 0x95B5FF5A;
+unsigned long __attribute__((weak)) __stack_chk_guard = 0x95B5FF5A;
 
 static unsigned long entry_count_offset;
 static unsigned long init_count_offset;
diff --git a/lib/sbi/sbi_ubsan.c b/lib/sbi/sbi_ubsan.c
new file mode 100644
index 00000000..271be7b0
--- /dev/null
+++ b/lib/sbi/sbi_ubsan.c
@@ -0,0 +1,924 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ *
+ * Author: Marcos Oduardo <marcos.oduardo at gmail.com>
+ */
+
+#ifdef UBSAN_ENABLED
+
+#include <sbi/sbi_types.h>
+#include <sbi/sbi_console.h>
+
+/* Undefined Behavior specific defines and structures defined by the compiler ABI */
+
+#define KIND_INTEGER 0
+#define KIND_FLOAT 1
+#define KIND_UNKNOWN UINT16_MAX
+
+volatile unsigned long sbi_ubsan_report_count;
+
+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);
+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);
+
+#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);
+
+	if (!(siOldValue & ACK_REPORTED)) {
+
+		sbi_ubsan_report_count++;
+
+		return false;
+	}
+
+	return true;
+}
+
+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
diff --git a/lib/sbi/tests/objects.mk b/lib/sbi/tests/objects.mk
index 3ee1c635..40c441e1 100644
--- a/lib/sbi/tests/objects.mk
+++ b/lib/sbi/tests/objects.mk
@@ -24,3 +24,8 @@ libsbi-objs-$(CONFIG_SBIUNIT) += tests/sbi_bitops_test.o
 
 carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += string_test_suite
 libsbi-objs-$(CONFIG_SBIUNIT) += tests/sbi_string_test.o
+
+ifeq ($(UBSAN),y)
+carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += ubsan_test_suite
+libsbi-objs-$(CONFIG_SBIUNIT) += tests/sbi_ubsan_test.o
+endif
diff --git a/lib/sbi/tests/sbi_ubsan_test.c b/lib/sbi/tests/sbi_ubsan_test.c
new file mode 100644
index 00000000..23409cb1
--- /dev/null
+++ b/lib/sbi/tests/sbi_ubsan_test.c
@@ -0,0 +1,114 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Author: Marcos Oduardo <marcos.oduardo at 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);
diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
index c4a8fee2..3f190625 100644
--- a/platform/generic/objects.mk
+++ b/platform/generic/objects.mk
@@ -39,5 +39,10 @@ ifeq ($(PLATFORM_RISCV_XLEN), 32)
 else
   # This needs to be 2MB aligned for 64-bit system
   FW_PAYLOAD_OFFSET=0x200000
+  ifeq ($(UBSAN), y)
+  # FW payload size overlaps data for 2MB
+  FW_PAYLOAD_OFFSET=0x400000
+  endif
+
 endif
 FW_PAYLOAD_FDT_OFFSET=$(FW_JUMP_FDT_OFFSET)
-- 
2.53.0




More information about the opensbi mailing list