[PATCH v4] lib: sbi: add UBSan support

Anup Patel anup at brainfault.org
Fri Jun 12 05:32:12 PDT 2026


On Fri, May 15, 2026 at 10:03 PM Marcos Oduardo
<marcos.oduardo at gmail.com> wrote:
>
> 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
> +

This change in platform/generic/objects.mk is not needed
because we now use FW_PAYLOAD_ALIGN instead of
FW_PAYLOAD_OFFSET. I will drop it at the time of merging.

LGTM.

Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup



More information about the opensbi mailing list