mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2026-06-12 22:31:45 +01:00
lib: sbi: add UBSan support
UBSan (Undefined Behavior Sanitizer) is a tool implemented using compiler instrumentation at runtime that allows checking for statements whose output is not deterministic or defined by the C standard. Compiling and running OpenSBI with UBSan instrumentation will print a message in the console if any sentence performs such an action. Support involves two main components: 1. The UBSan implementation hooks (derived from NetBSD), used by the compiler to handle the check output. 2. A test suite integrated with the SBI unit test framework to verify correct operation at runtime. Usage: make UBSAN=y PLATFORM=generic ... The test suite is built when both UBSAN=y and CONFIG_SBIUNIT=y are enabled. When UBSan is enabled, FW_PAYLOAD_OFFSET may need to be increased due to the size increase added by the instrumentation. A value of 0x400000 has been tested. UBSan adds runtime overhead and is intended for development builds only, not for production. Note: This patch marks __stack_chk_guard in sbi_init.c as a weak symbol to prevent multiple definition errors at compile time with UBSan instrumentation enabled. This resolves the conflict between the .globl definitions in sbi_init.c and test_head.S. Signed-off-by: Marcos Oduardo <marcos.oduardo@gmail.com> Reviewed-by: Anup Patel <anup@brainfault.org> Link: https://lore.kernel.org/r/20260515163321.2038366-1-marcos.oduardo@gmail.com Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
committed by
Anup Patel
parent
c175c97a27
commit
7bdcf55705
@@ -455,6 +455,23 @@ else
|
|||||||
CFLAGS += -O2
|
CFLAGS += -O2
|
||||||
endif
|
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)
|
ifeq ($(V), 1)
|
||||||
ELFFLAGS += -Wl,--print-gc-sections
|
ELFFLAGS += -Wl,--print-gc-sections
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Author: Marcos Oduardo <marcos.oduardo@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__ */
|
||||||
@@ -100,6 +100,9 @@ libsbi-objs-y += sbi_tlb.o
|
|||||||
libsbi-objs-y += sbi_trap.o
|
libsbi-objs-y += sbi_trap.o
|
||||||
libsbi-objs-y += sbi_trap_ldst.o
|
libsbi-objs-y += sbi_trap_ldst.o
|
||||||
libsbi-objs-y += sbi_trap_v_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_unpriv.o
|
||||||
libsbi-objs-y += sbi_expected_trap.o
|
libsbi-objs-y += sbi_expected_trap.o
|
||||||
libsbi-objs-y += sbi_cppc.o
|
libsbi-objs-y += sbi_cppc.o
|
||||||
|
|||||||
+1
-1
@@ -218,7 +218,7 @@ static void wake_coldboot_harts(struct sbi_scratch *scratch)
|
|||||||
__smp_store_release(&coldboot_done, 1);
|
__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 entry_count_offset;
|
||||||
static unsigned long init_count_offset;
|
static unsigned long init_count_offset;
|
||||||
|
|||||||
@@ -0,0 +1,924 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 The NetBSD Foundation, Inc.
|
||||||
|
*
|
||||||
|
* Author: Marcos Oduardo <marcos.oduardo@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
|
||||||
@@ -24,3 +24,8 @@ libsbi-objs-$(CONFIG_SBIUNIT) += tests/sbi_bitops_test.o
|
|||||||
|
|
||||||
carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += string_test_suite
|
carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += string_test_suite
|
||||||
libsbi-objs-$(CONFIG_SBIUNIT) += tests/sbi_string_test.o
|
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
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Author: Marcos Oduardo <marcos.oduardo@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sbi/sbi_unit_test.h>
|
||||||
|
#include <sbi/sbi_types.h>
|
||||||
|
#include <sbi/sbi_console.h>
|
||||||
|
#include <sbi/sbi_ubsan.h>
|
||||||
|
|
||||||
|
#define UBSAN_EXPECT_FIRES(test, stmt) \
|
||||||
|
do { \
|
||||||
|
unsigned long _before = sbi_ubsan_report_count; \
|
||||||
|
stmt; \
|
||||||
|
SBIUNIT_EXPECT_NE(test, sbi_ubsan_report_count, _before); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static void test_ubsan_add_overflow(struct sbiunit_test_case *test)
|
||||||
|
{
|
||||||
|
volatile int a = 0x7FFFFFFF; //INT_MAx
|
||||||
|
volatile int b = 1;
|
||||||
|
volatile int c;
|
||||||
|
UBSAN_EXPECT_FIRES(test, c = a + b);
|
||||||
|
(void)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ubsan_sub_overflow(struct sbiunit_test_case *test)
|
||||||
|
{
|
||||||
|
volatile int a = 0x80000000; //INT_MIN
|
||||||
|
volatile int b = 1;
|
||||||
|
volatile int c;
|
||||||
|
UBSAN_EXPECT_FIRES(test, c = a - b);
|
||||||
|
(void)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ubsan_mul_overflow(struct sbiunit_test_case *test)
|
||||||
|
{
|
||||||
|
volatile int a = 0x7FFFFFFF;
|
||||||
|
volatile int b = 2;
|
||||||
|
volatile int c;
|
||||||
|
UBSAN_EXPECT_FIRES(test, c = a * b);
|
||||||
|
(void)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ubsan_divrem(struct sbiunit_test_case *test)
|
||||||
|
{
|
||||||
|
volatile int a = 10;
|
||||||
|
volatile int b = 0;
|
||||||
|
volatile int c;
|
||||||
|
UBSAN_EXPECT_FIRES(test, c = a / b);
|
||||||
|
(void)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ubsan_oob(struct sbiunit_test_case *test)
|
||||||
|
{
|
||||||
|
volatile int idx = 5;
|
||||||
|
int arr[3] = { 1, 2, 3 };
|
||||||
|
volatile int val;
|
||||||
|
UBSAN_EXPECT_FIRES(test, val = arr[idx]);
|
||||||
|
(void)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ubsan_shift_too_large(struct sbiunit_test_case *test)
|
||||||
|
{
|
||||||
|
volatile unsigned long val = 1;
|
||||||
|
volatile int shift = 64;
|
||||||
|
volatile unsigned long res;
|
||||||
|
UBSAN_EXPECT_FIRES(test, res = val << shift);
|
||||||
|
(void)res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ubsan_shift_negative(struct sbiunit_test_case *test)
|
||||||
|
{
|
||||||
|
volatile int val = 1;
|
||||||
|
volatile int shift = -1;
|
||||||
|
volatile int res;
|
||||||
|
|
||||||
|
UBSAN_EXPECT_FIRES(test, res = val << shift);
|
||||||
|
(void)res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ubsan_load_invalid_bool(struct sbiunit_test_case *test)
|
||||||
|
{
|
||||||
|
volatile char bool_val = 5;
|
||||||
|
volatile bool *b_ptr = (bool *)&bool_val;
|
||||||
|
volatile int taken = 0;
|
||||||
|
UBSAN_EXPECT_FIRES(test, if (*b_ptr) taken = 1);
|
||||||
|
(void)taken;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ubsan_pointer_overflow(struct sbiunit_test_case *test)
|
||||||
|
{
|
||||||
|
volatile uintptr_t base = 0xFFFFFFFFFFFFFFFEUL;
|
||||||
|
volatile char *ptr = (char *)base;
|
||||||
|
volatile char *res;
|
||||||
|
UBSAN_EXPECT_FIRES(test, res = ptr + 5);
|
||||||
|
(void)res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sbiunit_test_case ubsan_tests[] = {
|
||||||
|
SBIUNIT_TEST_CASE(test_ubsan_add_overflow),
|
||||||
|
SBIUNIT_TEST_CASE(test_ubsan_sub_overflow),
|
||||||
|
SBIUNIT_TEST_CASE(test_ubsan_mul_overflow),
|
||||||
|
SBIUNIT_TEST_CASE(test_ubsan_divrem),
|
||||||
|
SBIUNIT_TEST_CASE(test_ubsan_oob),
|
||||||
|
SBIUNIT_TEST_CASE(test_ubsan_shift_too_large),
|
||||||
|
SBIUNIT_TEST_CASE(test_ubsan_shift_negative),
|
||||||
|
SBIUNIT_TEST_CASE(test_ubsan_load_invalid_bool),
|
||||||
|
SBIUNIT_TEST_CASE(test_ubsan_pointer_overflow),
|
||||||
|
SBIUNIT_END_CASE,
|
||||||
|
};
|
||||||
|
|
||||||
|
SBIUNIT_TEST_SUITE(ubsan_test_suite, ubsan_tests);
|
||||||
Reference in New Issue
Block a user