Index: head/sys/kern/kern_ubsan.c =================================================================== --- head/sys/kern/kern_ubsan.c (nonexistent) +++ head/sys/kern/kern_ubsan.c (revision 340189) @@ -0,0 +1,1638 @@ +/* $NetBSD: ubsan.c,v 1.3 2018/08/03 16:31:04 kamil Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * The micro UBSan implementation for the userland (uUBSan) and kernel (kUBSan). + * The uBSSan versions is suitable for inclusion into libc or used standalone + * with ATF tests. + * + * This file due to long symbol names generated by a compiler during the + * instrumentation process does not follow the KNF style with 80-column limit. + */ + +#include +#if defined(_KERNEL) +__KERNEL_RCSID(0, "$NetBSD: ubsan.c,v 1.3 2018/08/03 16:31:04 kamil Exp $"); +#else +__RCSID("$NetBSD: ubsan.c,v 1.3 2018/08/03 16:31:04 kamil Exp $"); +#endif + +#if defined(_KERNEL) +#include +#include +#include +#define ASSERT(x) KASSERT(x) +#else +#if defined(_LIBC) +#include "namespace.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_LIBC) +#include "extern.h" +#define ubsan_vsyslog vsyslog_ss +#define ASSERT(x) _DIAGASSERT(x) +#else +#define ubsan_vsyslog vsyslog_r +#define ASSERT(x) assert(x) +#endif +/* These macros are available in _KERNEL only */ +#define SET(t, f) ((t) |= (f)) +#define ISSET(t, f) ((t) & (f)) +#define CLR(t, f) ((t) &= ~(f)) +#endif + +#define REINTERPRET_CAST(__dt, __st) ((__dt)(__st)) +#define STATIC_CAST(__dt, __st) ((__dt)(__st)) + +#define ACK_REPORTED __BIT(31) + +#define MUL_STRING "*" +#define PLUS_STRING "+" +#define MINUS_STRING "-" +#define DIVREM_STRING "divrem" + +#define CFI_VCALL 0 +#define CFI_NVCALL 1 +#define CFI_DERIVEDCAST 2 +#define CFI_UNRELATEDCAST 3 +#define CFI_ICALL 4 +#define CFI_NVMFCALL 5 +#define CFI_VMFCALL 6 + +#define NUMBER_MAXLEN 128 +#define LOCATION_MAXLEN (PATH_MAX + 32 /* ':LINE:COLUMN' */) + +#define WIDTH_8 8 +#define WIDTH_16 16 +#define WIDTH_32 32 +#define WIDTH_64 64 +#define WIDTH_80 80 +#define WIDTH_96 96 +#define WIDTH_128 128 + +#define NUMBER_SIGNED_BIT 1U + +#if __SIZEOF_INT128__ +typedef __int128 longest; +typedef unsigned __int128 ulongest; +#else +typedef int64_t longest; +typedef uint64_t ulongest; +#endif + +#ifndef _KERNEL +static int ubsan_flags = -1; +#define UBSAN_ABORT __BIT(0) +#define UBSAN_STDOUT __BIT(1) +#define UBSAN_STDERR __BIT(2) +#define UBSAN_SYSLOG __BIT(3) +#endif + +/* Undefined Behavior specific defines and structures */ + +#define KIND_INTEGER 0 +#define KIND_FLOAT 1 +#define KIND_UNKNOWN UINT16_MAX + +struct CSourceLocation { + char *mFilename; + uint32_t mLine; + uint32_t mColumn; +}; + +struct CTypeDescriptor { + uint16_t mTypeKind; + uint16_t mTypeInfo; + uint8_t mTypeName[1]; +}; + +struct COverflowData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CUnreachableData { + struct CSourceLocation mLocation; +}; + +struct CCFICheckFailData { + uint8_t mCheckKind; + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CDynamicTypeCacheMissData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; + void *mTypeInfo; + uint8_t mTypeCheckKind; +}; + +struct CFunctionTypeMismatchData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CInvalidBuiltinData { + struct CSourceLocation mLocation; + uint8_t mKind; +}; + +struct CInvalidValueData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CNonNullArgData { + struct CSourceLocation mLocation; + struct CSourceLocation mAttributeLocation; + int mArgIndex; +}; + +struct CNonNullReturnData { + struct CSourceLocation mAttributeLocation; +}; + +struct COutOfBoundsData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mArrayType; + struct CTypeDescriptor *mIndexType; +}; + +struct CPointerOverflowData { + struct CSourceLocation mLocation; +}; + +struct CShiftOutOfBoundsData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mLHSType; + struct CTypeDescriptor *mRHSType; +}; + +struct CTypeMismatchData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; + unsigned long mLogAlignment; + uint8_t mTypeCheckKind; +}; + +struct CTypeMismatchData_v1 { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; + uint8_t mLogAlignment; + uint8_t mTypeCheckKind; +}; + +struct CVLABoundData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CFloatCastOverflowData { + struct CSourceLocation mLocation; /* This field exists in this struct since 2015 August 11th */ + struct CTypeDescriptor *mFromType; + struct CTypeDescriptor *mToType; +}; + +/* Local utility functions */ +static void Report(bool isFatal, const char *pFormat, ...) __printflike(2, 3); +static bool isAlreadyReported(struct CSourceLocation *pLocation); +static size_t zDeserializeTypeWidth(struct CTypeDescriptor *pType); +static void DeserializeLocation(char *pBuffer, size_t zBUfferLength, struct CSourceLocation *pLocation); +#ifdef __SIZEOF_INT128__ +static void DeserializeUINT128(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, __uint128_t U128); +#endif +static void DeserializeNumberSigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, longest L); +static void DeserializeNumberUnsigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, ulongest L); +#ifndef _KERNEL +static void DeserializeFloatOverPointer(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long *pNumber); +static void DeserializeFloatInlined(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber); +#endif +static longest llliGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber); +static ulongest llluGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber); +#ifndef _KERNEL +static void DeserializeNumberFloat(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber); +#endif +static void DeserializeNumber(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber); +static const char *DeserializeTypeCheckKind(uint8_t hhuTypeCheckKind); +static const char *DeserializeBuiltinCheckKind(uint8_t hhuBuiltinCheckKind); +static const char *DeserializeCFICheckKind(uint8_t hhuCFICheckKind); +static bool isNegativeNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber); +static bool isShiftExponentTooLarge(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber, size_t zWidth); + +/* Unused in this implementation, emitted by the C++ check dynamic type cast. */ +intptr_t __ubsan_vptr_type_cache[128]; + +/* 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_builtin_unreachable(struct CUnreachableData *pData); +void __ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer); +void __ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable); +void __ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable); +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_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash); +void __ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash); +void __ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom); +void __ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom); +void __ubsan_handle_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_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_nonnull_arg(struct CNonNullArgData *pData); +void __ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData); +void __ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer); +void __ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer); +void __ubsan_handle_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 HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer); +static void HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash); +static void HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom); +static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData); +static void HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData); +static void HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer); +static void HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult); + +static void +HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation) +{ + char szLocation[LOCATION_MAXLEN]; + char szLHS[NUMBER_MAXLEN]; + char szRHS[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szLHS, NUMBER_MAXLEN, pData->mType, ulLHS); + DeserializeNumber(szLocation, szRHS, NUMBER_MAXLEN, pData->mType, ulRHS); + + Report(isFatal, "UBSan: Undefined Behavior in %s, %s integer overflow: %s %s %s cannot be represented in type %s\n", + szLocation, ISSET(pData->mType->mTypeInfo, NUMBER_SIGNED_BIT) ? "signed" : "unsigned", szLHS, szOperation, szRHS, pData->mType->mTypeName); +} + +static void +HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue) +{ + char szLocation[LOCATION_MAXLEN]; + char szOldValue[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szOldValue, NUMBER_MAXLEN, pData->mType, ulOldValue); + + Report(isFatal, "UBSan: Undefined Behavior in %s, negation of %s cannot be represented in type %s\n", + szLocation, szOldValue, pData->mType->mTypeName); +} + +static void +HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, calling __builtin_unreachable()\n", + szLocation); +} + +static void +HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(mLocation); + ASSERT(mType); + + if (isAlreadyReported(mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, mLocation); + + if (ulPointer == 0) { + Report(isFatal, "UBSan: Undefined Behavior in %s, %s null pointer of type %s\n", + szLocation, DeserializeTypeCheckKind(mTypeCheckKind), mType->mTypeName); + } else if ((mLogAlignment - 1) & ulPointer) { + Report(isFatal, "UBSan: Undefined Behavior in %s, %s misaligned address %p for type %s which requires %ld byte alignment\n", + szLocation, DeserializeTypeCheckKind(mTypeCheckKind), REINTERPRET_CAST(void *, ulPointer), mType->mTypeName, mLogAlignment); + } else { + Report(isFatal, "UBSan: Undefined Behavior in %s, %s address %p with insufficient space for an object of type %s\n", + szLocation, DeserializeTypeCheckKind(mTypeCheckKind), REINTERPRET_CAST(void *, ulPointer), mType->mTypeName); + } +} + +static void +HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound) +{ + char szLocation[LOCATION_MAXLEN]; + char szBound[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szBound, NUMBER_MAXLEN, pData->mType, ulBound); + + Report(isFatal, "UBSan: Undefined Behavior in %s, variable length array bound value %s <= 0\n", + szLocation, szBound); +} + +static void +HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex) +{ + char szLocation[LOCATION_MAXLEN]; + char szIndex[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szIndex, NUMBER_MAXLEN, pData->mIndexType, ulIndex); + + Report(isFatal, "UBSan: Undefined Behavior in %s, index %s is out of range for type %s\n", + szLocation, szIndex, pData->mArrayType->mTypeName); +} + +static void +HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + char szLocation[LOCATION_MAXLEN]; + char szLHS[NUMBER_MAXLEN]; + char szRHS[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szLHS, NUMBER_MAXLEN, pData->mLHSType, ulLHS); + DeserializeNumber(szLocation, szRHS, NUMBER_MAXLEN, pData->mRHSType, ulRHS); + + if (isNegativeNumber(szLocation, pData->mRHSType, ulRHS)) + Report(isFatal, "UBSan: Undefined Behavior in %s, shift exponent %s is negative\n", + szLocation, szRHS); + else if (isShiftExponentTooLarge(szLocation, pData->mRHSType, ulRHS, zDeserializeTypeWidth(pData->mLHSType))) + Report(isFatal, "UBSan: Undefined Behavior in %s, shift exponent %s is too large for %zu-bit type %s\n", + szLocation, szRHS, zDeserializeTypeWidth(pData->mLHSType), pData->mLHSType->mTypeName); + else if (isNegativeNumber(szLocation, pData->mLHSType, ulLHS)) + Report(isFatal, "UBSan: Undefined Behavior in %s, left shift of negative value %s\n", + szLocation, szLHS); + else + Report(isFatal, "UBSan: Undefined Behavior in %s, left shift of %s by %s places cannot be represented in type %s\n", + szLocation, szLHS, szRHS, pData->mLHSType->mTypeName); +} + +static void +HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue) +{ + char szLocation[LOCATION_MAXLEN]; + char szValue[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szValue, NUMBER_MAXLEN, pData->mType, ulValue); + + Report(isFatal, "UBSan: Undefined Behavior in %s, load of value %s is not a valid value for type %s\n", + szLocation, szValue, pData->mType->mTypeName); +} + +static void +HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, passing zero to %s, which is not a valid argument\n", + szLocation, DeserializeBuiltinCheckKind(pData->mKind)); +} + +static void +HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction) +{ + char szLocation[LOCATION_MAXLEN]; + + /* + * There is no a portable C solution to translate an address of a + * function to its name. On the cost of getting this routine simple + * and portable without ifdefs between the userland and the kernel + * just print the address of the function as-is. + * + * For better diagnostic messages in the userland, users shall use + * the full upstream version shipped along with the compiler toolchain. + */ + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, call to function %#lx through pointer to incorrect function type %s\n", + szLocation, ulFunction, pData->mType->mTypeName); +} + +static void +HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer) +{ + char szLocation[LOCATION_MAXLEN]; + + /* + * This is a minimal implementation without diving into C++ + * specifics and (Itanium) ABI deserialization. + */ + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + if (pData->mCheckKind == CFI_ICALL || pData->mCheckKind == CFI_VMFCALL) { + Report(isFatal, "UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx)\n", + szLocation, pData->mType->mTypeName, DeserializeCFICheckKind(pData->mCheckKind), ulVtable); + } else { + Report(isFatal || FromUnrecoverableHandler, "UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx; %s vtable; from %s handler; Program Counter %#lx; Frame Pointer %#lx)\n", + szLocation, pData->mType->mTypeName, DeserializeCFICheckKind(pData->mCheckKind), ulVtable, *bValidVtable ? "valid" : "invalid", *FromUnrecoverableHandler ? "unrecoverable" : "recoverable", *ProgramCounter, *FramePointer); + } +} + +static void +HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash) +{ +#if 0 + char szLocation[LOCATION_MAXLEN]; + + /* + * Unimplemented. + * + * This UBSan handler is special as the check has to be impelemented + * in an implementation. In order to handle it there is need to + * introspect into C++ ABI internals (RTTI) and use low-level + * C++ runtime interfaces. + */ + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, %s address %#lx which might not point to an object of type %s\n" + szLocation, DeserializeTypeCheckKind(pData->mTypeCheckKind), ulPointer, pData->mType); +#endif +} + +static void +HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom) +{ + char szLocation[LOCATION_MAXLEN]; + char szFrom[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szFrom, NUMBER_MAXLEN, pData->mFromType, ulFrom); + + Report(isFatal, "UBSan: Undefined Behavior in %s, %s (of type %s) is outside the range of representable values of type %s\n", + szLocation, szFrom, pData->mFromType->mTypeName, pData->mToType->mTypeName); +} + +static void +HandleMissingReturn(bool isFatal, struct CUnreachableData *pData) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, execution reached the end of a value-returning function without returning a value\n", + szLocation); +} + +static void +HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData) +{ + char szLocation[LOCATION_MAXLEN]; + char szAttributeLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + if (pData->mAttributeLocation.mFilename) + DeserializeLocation(szAttributeLocation, LOCATION_MAXLEN, &pData->mAttributeLocation); + else + szAttributeLocation[0] = '\0'; + + Report(isFatal, "UBSan: Undefined Behavior in %s, null pointer passed as argument %d, which is declared to never be null%s%s\n", + szLocation, pData->mArgIndex, pData->mAttributeLocation.mFilename ? ", nonnull/_Nonnull specified in " : "", szAttributeLocation); +} + +static void +HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + char szLocation[LOCATION_MAXLEN]; + char szAttributeLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + ASSERT(pLocationPointer); + + if (isAlreadyReported(pLocationPointer)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, pLocationPointer); + if (pData->mAttributeLocation.mFilename) + DeserializeLocation(szAttributeLocation, LOCATION_MAXLEN, &pData->mAttributeLocation); + else + szAttributeLocation[0] = '\0'; + + Report(isFatal, "UBSan: Undefined Behavior in %s, null pointer returned from function declared to never return null%s%s\n", + szLocation, pData->mAttributeLocation.mFilename ? ", nonnull/_Nonnull specified in " : "", szAttributeLocation); +} + +static void +HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, pointer expression with base %#lx overflowed to %#lx\n", + szLocation, ulBase, ulResult); +} + +/* Definions of public symbols emitted by the instrumentation code */ +void +__ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(false, pData, ulLHS, ulRHS, PLUS_STRING); +} + +void +__ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(true, pData, ulLHS, ulRHS, PLUS_STRING); +} + +void +__ubsan_handle_builtin_unreachable(struct CUnreachableData *pData) +{ + + ASSERT(pData); + + HandleBuiltinUnreachable(true, pData); +} + +void +__ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer) +{ + + ASSERT(pData); + + HandleCFIBadType(false, pData, ulVtable, &bValidVtable, &FromUnrecoverableHandler, &ProgramCounter, &FramePointer); +} + +void +__ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable) +{ + + ASSERT(pData); + + HandleCFIBadType(false, pData, ulValue, 0, 0, 0, 0); +} + +void +__ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable) +{ + + ASSERT(pData); + + HandleCFIBadType(true, pData, ulValue, 0, 0, 0, 0); +} + +void +__ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(false, pData, ulLHS, ulRHS, DIVREM_STRING); +} + +void +__ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(true, pData, ulLHS, ulRHS, DIVREM_STRING); +} + +void +__ubsan_handle_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash) +{ + + ASSERT(pData); + + HandleDynamicTypeCacheMiss(false, pData, ulPointer, ulHash); +} + +void +__ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash) +{ + + ASSERT(pData); + + HandleDynamicTypeCacheMiss(false, pData, ulPointer, ulHash); +} + +void +__ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom) +{ + + ASSERT(pData); + + HandleFloatCastOverflow(false, pData, ulFrom); +} + +void +__ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom) +{ + + ASSERT(pData); + + HandleFloatCastOverflow(true, pData, ulFrom); +} + +void +__ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction) +{ + + ASSERT(pData); + + HandleFunctionTypeMismatch(false, pData, ulFunction); +} + +void +__ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction) +{ + + ASSERT(pData); + + HandleFunctionTypeMismatch(false, pData, ulFunction); +} + +void +__ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData) +{ + + ASSERT(pData); + + HandleInvalidBuiltin(true, pData); +} + +void +__ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData) +{ + + ASSERT(pData); + + HandleInvalidBuiltin(true, pData); +} + +void +__ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulValue) +{ + + ASSERT(pData); + + HandleLoadInvalidValue(false, pData, ulValue); +} + +void +__ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulValue) +{ + + ASSERT(pData); + + HandleLoadInvalidValue(true, pData, ulValue); +} + +void +__ubsan_handle_missing_return(struct CUnreachableData *pData) +{ + + ASSERT(pData); + + HandleMissingReturn(true, pData); +} + +void +__ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(false, pData, ulLHS, ulRHS, MUL_STRING); +} + +void +__ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(true, pData, ulLHS, ulRHS, MUL_STRING); +} + +void +__ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldValue) +{ + + ASSERT(pData); + + HandleNegateOverflow(false, pData, ulOldValue); +} + +void +__ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldValue) +{ + + ASSERT(pData); + + HandleNegateOverflow(true, pData, ulOldValue); +} + +void +__ubsan_handle_nonnull_arg(struct CNonNullArgData *pData) +{ + + ASSERT(pData); + + HandleNonnullArg(false, pData); +} + +void +__ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData) +{ + + ASSERT(pData); + + HandleNonnullArg(true, pData); +} + +void +__ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + + ASSERT(pData); + ASSERT(pLocationPointer); + + HandleNonnullReturn(false, pData, pLocationPointer); +} + +void +__ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + + ASSERT(pData); + ASSERT(pLocationPointer); + + HandleNonnullReturn(true, pData, pLocationPointer); +} + +void +__ubsan_handle_nullability_arg(struct CNonNullArgData *pData) +{ + + ASSERT(pData); + + HandleNonnullArg(false, pData); +} + +void +__ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData) +{ + + ASSERT(pData); + + HandleNonnullArg(true, pData); +} + +void +__ubsan_handle_nullability_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + + ASSERT(pData); + ASSERT(pLocationPointer); + + HandleNonnullReturn(false, pData, pLocationPointer); +} + +void +__ubsan_handle_nullability_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + + ASSERT(pData); + ASSERT(pLocationPointer); + + HandleNonnullReturn(true, pData, pLocationPointer); +} + +void +__ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex) +{ + + ASSERT(pData); + + HandleOutOfBounds(false, pData, ulIndex); +} + +void +__ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex) +{ + + ASSERT(pData); + + HandleOutOfBounds(true, pData, ulIndex); +} + +void +__ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult) +{ + + ASSERT(pData); + + HandlePointerOverflow(false, pData, ulBase, ulResult); +} + +void +__ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult) +{ + + ASSERT(pData); + + HandlePointerOverflow(true, pData, ulBase, ulResult); +} + +void +__ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleShiftOutOfBounds(false, pData, ulLHS, ulRHS); +} + +void +__ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleShiftOutOfBounds(true, pData, ulLHS, ulRHS); +} + +void +__ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(false, pData, ulLHS, ulRHS, MINUS_STRING); +} + +void +__ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(true, pData, ulLHS, ulRHS, MINUS_STRING); +} + +void +__ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, unsigned long ulPointer) +{ + + ASSERT(pData); + + HandleTypeMismatch(false, &pData->mLocation, pData->mType, pData->mLogAlignment, pData->mTypeCheckKind, ulPointer); +} + +void +__ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, unsigned long ulPointer) +{ + + ASSERT(pData); + + HandleTypeMismatch(true, &pData->mLocation, pData->mType, pData->mLogAlignment, pData->mTypeCheckKind, ulPointer); +} + +void +__ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer) +{ + + ASSERT(pData); + + 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) +{ + + ASSERT(pData); + + HandleTypeMismatch(true, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer); +} + +void +__ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound) +{ + + ASSERT(pData); + + HandleVlaBoundNotPositive(false, pData, ulBound); +} + +void +__ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound) +{ + + ASSERT(pData); + + HandleVlaBoundNotPositive(true, pData, ulBound); +} + +void +__ubsan_get_current_report_data(const char **ppOutIssueKind, const char **ppOutMessage, const char **ppOutFilename, uint32_t *pOutLine, uint32_t *pOutCol, char **ppOutMemoryAddr) +{ + /* + * Unimplemented. + * + * The __ubsan_on_report() feature is non trivial to implement in a + * shared code between the kernel and userland. It's also opening + * new sets of potential problems as we are not expected to slow down + * execution of certain kernel subsystems (synchronization issues, + * interrupt handling etc). + * + * A proper solution would need probably a lock-free bounded queue built + * with atomic operations with the property of miltiple consumers and + * multiple producers. Maintaining and validating such code is not + * worth the effort. + * + * A legitimate user - besides testing framework - is a debugger plugin + * intercepting reports from the UBSan instrumentation. For such + * scenarios it is better to run the Clang/GCC version. + */ +} + +/* Local utility functions */ + +static void +Report(bool isFatal, const char *pFormat, ...) +{ + va_list ap; + + ASSERT(pFormat); + + va_start(ap, pFormat); +#if defined(_KERNEL) + if (isFatal) + vpanic(pFormat, ap); + else + vprintf(pFormat, ap); +#else + if (ubsan_flags == -1) { + char buf[1024]; + char *p; + + ubsan_flags = UBSAN_STDERR; + + if (getenv_r("LIBC_UBSAN", buf, sizeof(buf)) != -1) { + for (p = buf; *p; p++) { + switch (*p) { + case 'a': + SET(ubsan_flags, UBSAN_ABORT); + break; + case 'A': + CLR(ubsan_flags, UBSAN_ABORT); + break; + case 'e': + SET(ubsan_flags, UBSAN_STDERR); + break; + case 'E': + CLR(ubsan_flags, UBSAN_STDERR); + break; + case 'l': + SET(ubsan_flags, UBSAN_SYSLOG); + break; + case 'L': + CLR(ubsan_flags, UBSAN_SYSLOG); + break; + case 'o': + SET(ubsan_flags, UBSAN_STDOUT); + break; + case 'O': + CLR(ubsan_flags, UBSAN_STDOUT); + break; + default: + break; + } + } + } + } + + // The *v*print* functions can flush the va_list argument. + // Create a local copy for each call to prevent invalid read. + if (ISSET(ubsan_flags, UBSAN_STDOUT)) { + va_list tmp; + va_copy(tmp, ap); + vprintf(pFormat, tmp); + va_end(tmp); + fflush(stdout); + } + if (ISSET(ubsan_flags, UBSAN_STDERR)) { + va_list tmp; + va_copy(tmp, ap); + vfprintf(stderr, pFormat, tmp); + va_end(tmp); + fflush(stderr); + } + if (ISSET(ubsan_flags, UBSAN_SYSLOG)) { + va_list tmp; + va_copy(tmp, ap); + struct syslog_data SyslogData = SYSLOG_DATA_INIT; + ubsan_vsyslog(LOG_DEBUG | LOG_USER, &SyslogData, pFormat, tmp); + va_end(tmp); + } + if (isFatal || ISSET(ubsan_flags, UBSAN_ABORT)) { + abort(); + /* NOTREACHED */ + } +#endif + va_end(ap); +} + +static bool +isAlreadyReported(struct CSourceLocation *pLocation) +{ + /* + * This code is shared between libc, kernel and standalone usage. + * It shall work in early bootstrap phase of both of them. + */ + + uint32_t siOldValue; + volatile uint32_t *pLine; + + ASSERT(pLocation); + + pLine = &pLocation->mLine; + + do { + siOldValue = *pLine; + } while (__sync_val_compare_and_swap(pLine, siOldValue, siOldValue | ACK_REPORTED) != siOldValue); + + return ISSET(siOldValue, ACK_REPORTED); +} + +static size_t +zDeserializeTypeWidth(struct CTypeDescriptor *pType) +{ + size_t zWidth = 0; + + ASSERT(pType); + + switch (pType->mTypeKind) { + case KIND_INTEGER: + zWidth = __BIT(__SHIFTOUT(pType->mTypeInfo, ~NUMBER_SIGNED_BIT)); + break; + case KIND_FLOAT: + zWidth = pType->mTypeInfo; + break; + default: + Report(true, "UBSan: Unknown variable type %#04" PRIx16 "\n", pType->mTypeKind); + /* NOTREACHED */ + } + + /* Invalid width will be transformed to 0 */ + ASSERT(zWidth > 0); + + return zWidth; +} + +static void +DeserializeLocation(char *pBuffer, size_t zBUfferLength, struct CSourceLocation *pLocation) +{ + + ASSERT(pLocation); + ASSERT(pLocation->mFilename); + + snprintf(pBuffer, zBUfferLength, "%s:%" PRIu32 ":%" PRIu32, pLocation->mFilename, pLocation->mLine & (uint32_t)~ACK_REPORTED, pLocation->mColumn); +} + +#ifdef __SIZEOF_INT128__ +static void +DeserializeUINT128(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, __uint128_t U128) +{ + char szBuf[3]; /* 'XX\0' */ + char rgNumber[sizeof(ulongest)]; + ssize_t zI; + + memcpy(rgNumber, &U128, sizeof(U128)); + + strlcpy(pBuffer, "Undecoded-128-bit-Integer-Type (0x", zBUfferLength); +#if BYTE_ORDER == LITTLE_ENDIAN + for (zI = sizeof(ulongest) - 1; zI >= 0; zI--) { +#else + for (zI = 0; zI < (ssize_t)sizeof(ulongest); zI++) { +#endif + snprintf(szBuf, sizeof(szBuf), "%02" PRIx8, rgNumber[zI]); + strlcat(pBuffer, szBuf, zBUfferLength); + } + strlcat(pBuffer, ")", zBUfferLength); +} +#endif + +static void +DeserializeNumberSigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, longest L) +{ + + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)); + + switch (zDeserializeTypeWidth(pType)) { + default: + ASSERT(0 && "Invalid codepath"); + /* NOTREACHED */ +#ifdef __SIZEOF_INT128__ + case WIDTH_128: + DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L)); + break; +#endif + case WIDTH_64: + case WIDTH_32: + case WIDTH_16: + case WIDTH_8: + snprintf(pBuffer, zBUfferLength, "%" PRId64, STATIC_CAST(int64_t, L)); + break; + } +} + +static void +DeserializeNumberUnsigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, ulongest L) +{ + + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(!ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)); + + switch (zDeserializeTypeWidth(pType)) { + default: + ASSERT(0 && "Invalid codepath"); + /* NOTREACHED */ +#ifdef __SIZEOF_INT128__ + case WIDTH_128: + DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L)); + break; +#endif + case WIDTH_64: + case WIDTH_32: + case WIDTH_16: + case WIDTH_8: + snprintf(pBuffer, zBUfferLength, "%" PRIu64, STATIC_CAST(uint64_t, L)); + break; + } +} + +#ifndef _KERNEL +static void +DeserializeFloatOverPointer(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long *pNumber) +{ + double D; +#ifdef __HAVE_LONG_DOUBLE + long double LD; +#endif + + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(pNumber); + /* + * This function handles 64-bit number over a pointer on 32-bit CPUs. + */ + ASSERT((sizeof(*pNumber) * CHAR_BIT < WIDTH_64) || (zDeserializeTypeWidth(pType) >= WIDTH_64)); + ASSERT(sizeof(D) == sizeof(uint64_t)); +#ifdef __HAVE_LONG_DOUBLE + ASSERT(sizeof(LD) > sizeof(uint64_t)); +#endif + + switch (zDeserializeTypeWidth(pType)) { +#ifdef __HAVE_LONG_DOUBLE + case WIDTH_128: + case WIDTH_96: + case WIDTH_80: + memcpy(&LD, pNumber, sizeof(long double)); + snprintf(pBuffer, zBUfferLength, "%Lg", LD); + break; +#endif + case WIDTH_64: + memcpy(&D, pNumber, sizeof(double)); + snprintf(pBuffer, zBUfferLength, "%g", D); + break; + } +} + +static void +DeserializeFloatInlined(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + float F; + double D; + uint32_t U32; + + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(sizeof(F) == sizeof(uint32_t)); + ASSERT(sizeof(D) == sizeof(uint64_t)); + + switch (zDeserializeTypeWidth(pType)) { + case WIDTH_64: + memcpy(&D, &ulNumber, sizeof(double)); + snprintf(pBuffer, zBUfferLength, "%g", D); + break; + case WIDTH_32: + /* + * On supported platforms sizeof(float)==sizeof(uint32_t) + * unsigned long is either 32 or 64-bit, cast it to 32-bit + * value in order to call memcpy(3) in an Endian-aware way. + */ + U32 = STATIC_CAST(uint32_t, ulNumber); + memcpy(&F, &U32, sizeof(float)); + snprintf(pBuffer, zBUfferLength, "%g", F); + break; + case WIDTH_16: + snprintf(pBuffer, zBUfferLength, "Undecoded-16-bit-Floating-Type (%#04" PRIx16 ")", STATIC_CAST(uint16_t, ulNumber)); + break; + } +} +#endif + +static longest +llliGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + size_t zNumberWidth; + longest L = 0; + + ASSERT(szLocation); + ASSERT(pType); + + zNumberWidth = zDeserializeTypeWidth(pType); + switch (zNumberWidth) { + default: + Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation); + /* NOTREACHED */ + case WIDTH_128: +#ifdef __SIZEOF_INT128__ + memcpy(&L, REINTERPRET_CAST(longest *, ulNumber), sizeof(longest)); +#else + Report(true, "UBSan: Unexpected 128-Bit Type in %s\n", szLocation); + /* NOTREACHED */ +#endif + break; + case WIDTH_64: + if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) { + L = *REINTERPRET_CAST(int64_t *, ulNumber); + } else { + L = STATIC_CAST(int64_t, STATIC_CAST(uint64_t, ulNumber)); + } + break; + case WIDTH_32: + L = STATIC_CAST(int32_t, STATIC_CAST(uint32_t, ulNumber)); + break; + case WIDTH_16: + L = STATIC_CAST(int16_t, STATIC_CAST(uint16_t, ulNumber)); + break; + case WIDTH_8: + L = STATIC_CAST(int8_t, STATIC_CAST(uint8_t, ulNumber)); + break; + } + + return L; +} + +static ulongest +llluGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + size_t zNumberWidth; + ulongest UL = 0; + + ASSERT(pType); + + zNumberWidth = zDeserializeTypeWidth(pType); + switch (zNumberWidth) { + default: + Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation); + /* NOTREACHED */ + case WIDTH_128: +#ifdef __SIZEOF_INT128__ + memcpy(&UL, REINTERPRET_CAST(ulongest *, ulNumber), sizeof(ulongest)); + break; +#else + Report(true, "UBSan: Unexpected 128-Bit Type in %s\n", szLocation); + /* NOTREACHED */ +#endif + case WIDTH_64: + if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) { + UL = *REINTERPRET_CAST(uint64_t *, ulNumber); + break; + } + /* FALLTHROUGH */ + case WIDTH_32: + /* FALLTHROUGH */ + case WIDTH_16: + /* FALLTHROUGH */ + case WIDTH_8: + UL = ulNumber; + break; + } + + return UL; +} + +#ifndef _KERNEL +static void +DeserializeNumberFloat(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + size_t zNumberWidth; + + ASSERT(szLocation); + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(pType->mTypeKind == KIND_FLOAT); + + zNumberWidth = zDeserializeTypeWidth(pType); + switch (zNumberWidth) { + default: + Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation); + /* NOTREACHED */ +#ifdef __HAVE_LONG_DOUBLE + case WIDTH_128: + case WIDTH_96: + case WIDTH_80: + DeserializeFloatOverPointer(pBuffer, zBUfferLength, pType, REINTERPRET_CAST(unsigned long *, ulNumber)); + break; +#endif + case WIDTH_64: + if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) { + DeserializeFloatOverPointer(pBuffer, zBUfferLength, pType, REINTERPRET_CAST(unsigned long *, ulNumber)); + break; + } + case WIDTH_32: + case WIDTH_16: + DeserializeFloatInlined(pBuffer, zBUfferLength, pType, ulNumber); + break; + } +} +#endif + +static void +DeserializeNumber(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + + ASSERT(szLocation); + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + + switch(pType->mTypeKind) { + case KIND_INTEGER: + if (ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)) { + longest L = llliGetNumber(szLocation, pType, ulNumber); + DeserializeNumberSigned(pBuffer, zBUfferLength, pType, L); + } else { + ulongest UL = llluGetNumber(szLocation, pType, ulNumber); + DeserializeNumberUnsigned(pBuffer, zBUfferLength, pType, UL); + } + break; + case KIND_FLOAT: +#ifdef _KERNEL + Report(true, "UBSan: Unexpected Float Type in %s\n", szLocation); + /* NOTREACHED */ +#else + DeserializeNumberFloat(szLocation, pBuffer, zBUfferLength, pType, ulNumber); +#endif + break; + case KIND_UNKNOWN: + Report(true, "UBSan: Unknown Type in %s\n", szLocation); + /* NOTREACHED */ + break; + } +} + +static const char * +DeserializeTypeCheckKind(uint8_t hhuTypeCheckKind) +{ + 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" + }; + + ASSERT(__arraycount(rgczTypeCheckKinds) > hhuTypeCheckKind); + + return rgczTypeCheckKinds[hhuTypeCheckKind]; +} + +static const char * +DeserializeBuiltinCheckKind(uint8_t hhuBuiltinCheckKind) +{ + const char *rgczBuiltinCheckKinds[] = { + "ctz()", + "clz()" + }; + + ASSERT(__arraycount(rgczBuiltinCheckKinds) > hhuBuiltinCheckKind); + + return rgczBuiltinCheckKinds[hhuBuiltinCheckKind]; +} + +static const char * +DeserializeCFICheckKind(uint8_t hhuCFICheckKind) +{ + const char *rgczCFICheckKinds[] = { + "virtual call", // CFI_VCALL + "non-virtual call", // CFI_NVCALL + "base-to-derived cast", // CFI_DERIVEDCAST + "cast to unrelated type", // CFI_UNRELATEDCAST + "indirect function call", // CFI_ICALL + "non-virtual pointer to member function call", // CFI_NVMFCALL + "virtual pointer to member function call", // CFI_VMFCALL + }; + + ASSERT(__arraycount(rgczCFICheckKinds) > hhuCFICheckKind); + + return rgczCFICheckKinds[hhuCFICheckKind]; +} + +static bool +isNegativeNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + + ASSERT(szLocation); + ASSERT(pType); + ASSERT(pType->mTypeKind == KIND_INTEGER); + + if (!ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)) + return false; + + return llliGetNumber(szLocation, pType, ulNumber) < 0; +} + +static bool +isShiftExponentTooLarge(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber, size_t zWidth) +{ + + ASSERT(szLocation); + ASSERT(pType); + ASSERT(pType->mTypeKind == KIND_INTEGER); + + return llluGetNumber(szLocation, pType, ulNumber) >= zWidth; +} Property changes on: head/sys/kern/kern_ubsan.c ___________________________________________________________________ Added: fbsd:nokeywords ## -0,0 +1 ## +yes \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property