[PATCH v4] lib: sbi: add UBSan support

Marcos Oduardo <[email protected]>
Newsgroups org.infradead.lists.opensbi
Message-ID <[email protected]>
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 <[email protected]>
---
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 <[email protected]>
+ */
+
+#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 <[email protected]>
+ */
+
+#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 <[email protected]>
+ */
+
+#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


-- 
opensbi mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/opensbi
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.