/*      $NetBSD: ubsan.c,v 1.12 2023/12/07 07:10:44 andvar 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 <sys/cdefs.h>
#if defined(_KERNEL)
__KERNEL_RCSID(0, "$NetBSD: ubsan.c,v 1.12 2023/12/07 07:10:44 andvar Exp $");
#else
__RCSID("$NetBSD: ubsan.c,v 1.12 2023/12/07 07:10:44 andvar Exp $");
#endif

#if defined(_KERNEL)
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stdarg.h>
#define ASSERT(x) KASSERT(x)
#else
#if defined(_LIBC)
#include "namespace.h"
#endif
#include <sys/param.h>
#include <assert.h>
#include <inttypes.h>
#include <math.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#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

#ifdef UBSAN_ALWAYS_FATAL
static const bool alwaysFatal = true;
#else
static const bool alwaysFatal = false;
#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

#ifdef __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;
};

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;
};

/* 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 const char *DeserializeImplicitConversionCheckKind(uint8_t hhuImplicitConversionCheckKind);
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_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_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_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_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_handle_implicit_conversion(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo);
void __ubsan_handle_implicit_conversion_abort(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo);
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 HandleAlignmentAssumption(bool isFatal, struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);

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);
}

static void
HandleImplicitConversion(bool isFatal, struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo)
{
       char szLocation[LOCATION_MAXLEN];
       char szFrom[NUMBER_MAXLEN];
       char szTo[NUMBER_MAXLEN];

       ASSERT(pData);

       if (isAlreadyReported(&pData->mLocation))
               return;

       DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
       DeserializeNumber(szLocation, szFrom, NUMBER_MAXLEN, pData->mFromType, ulFrom);
       DeserializeNumber(szLocation, szTo, NUMBER_MAXLEN, pData->mToType, ulTo);

       Report(isFatal, "UBSan: Undefined Behavior in %s, %s from %s %zu-bit %s (%s) to %s changed the value to %s %zu-bit %s\n",
              szLocation, DeserializeImplicitConversionCheckKind(pData->mKind), szFrom, zDeserializeTypeWidth(pData->mFromType), ISSET(pData->mFromType->mTypeInfo, NUMBER_SIGNED_BIT) ? "signed" : "unsigned", pData->mFromType->mTypeName, pData->mToType->mTypeName, szTo, zDeserializeTypeWidth(pData->mToType), ISSET(pData->mToType->mTypeInfo, NUMBER_SIGNED_BIT) ? "signed" : "unsigned");
}

static void
HandleAlignmentAssumption(bool isFatal, struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
{
       char szLocation[LOCATION_MAXLEN];
       char szAssumptionLocation[LOCATION_MAXLEN];
       unsigned long ulRealPointer;

       ASSERT(pData);

       if (isAlreadyReported(&pData->mLocation))
               return;

       DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);

       ulRealPointer = ulPointer - ulOffset;

       if (pData->mAssumptionLocation.mFilename != NULL) {
               DeserializeLocation(szAssumptionLocation, LOCATION_MAXLEN,
                   &pData->mAssumptionLocation);
               Report(isFatal, "UBSan: Undefined Behavior in %s, alignment assumption of %#lx for pointer %#lx (offset %#lx), assumption made in %s\n",
                   szLocation, ulAlignment, ulRealPointer, ulOffset,
                   szAssumptionLocation);
       } else {
               Report(isFatal, "UBSan: Undefined Behavior in %s, alignment assumption of %#lx for pointer %#lx (offset %#lx)\n",
                   szLocation, ulAlignment, ulRealPointer, ulOffset);
       }
}

/* 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_alignment_assumption(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
{

       ASSERT(pData);

       HandleAlignmentAssumption(false, pData, ulPointer, ulAlignment, ulOffset);
}

void
__ubsan_handle_alignment_assumption_abort(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
{

       ASSERT(pData);

       HandleAlignmentAssumption(true, pData, ulPointer, ulAlignment, ulOffset);
}

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(true, pData, ulFunction);
}

void
__ubsan_handle_function_type_mismatch_v1(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction, unsigned long ulCalleeRTTI, unsigned long ulFnRTTI)
{

       ASSERT(pData);
#if 0
       /*
        * 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.
        */

       HandleFunctionTypeMismatch(false, pData, ulFunction);
#endif
}

void
__ubsan_handle_function_type_mismatch_v1_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction, unsigned long ulCalleeRTTI, unsigned long ulFnRTTI)
{

       ASSERT(pData);
#if 0
       /*
        * 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.
        */

       HandleFunctionTypeMismatch(true, pData, ulFunction);
#endif
}

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_handle_implicit_conversion(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo)
{

       ASSERT(pData);

       HandleImplicitConversion(false, pData, ulFrom, ulTo);
}

void
__ubsan_handle_implicit_conversion_abort(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo)
{
       ASSERT(pData);

       HandleImplicitConversion(true, pData, ulFrom, ulTo);
}

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 multiple 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 || alwaysFatal)
               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 || alwaysFatal || ISSET(ubsan_flags, UBSAN_ABORT)) {
               abort();
               __unreachable();
               /* 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);
               __unreachable();
               /* 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");
               __unreachable();
               /* NOTREACHED */
#ifdef __SIZEOF_INT128__
       case WIDTH_128:
               DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L));
               break;
#endif
       case WIDTH_64:
               /* FALLTHROUGH */
       case WIDTH_32:
               /* FALLTHROUGH */
       case WIDTH_16:
               /* FALLTHROUGH */
       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");
               __unreachable();
               /* NOTREACHED */
#ifdef __SIZEOF_INT128__
       case WIDTH_128:
               DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L));
               break;
#endif
       case WIDTH_64:
               /* FALLTHROUGH */
       case WIDTH_32:
               /* FALLTHROUGH */
       case WIDTH_16:
               /* FALLTHROUGH */
       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:
               /* FALLTHROUGH */
       case WIDTH_96:
               /* FALLTHROUGH */
       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:
               ASSERT(sizeof(D) == sizeof(ulNumber));
               memcpy(&D, &ulNumber, sizeof(ulNumber));
               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);
               __unreachable();
               /* NOTREACHED */
       case WIDTH_128:
#ifdef __SIZEOF_INT128__
               memcpy(&L, REINTERPRET_CAST(longest *, ulNumber), sizeof(longest));
               break;
#else
               Report(true, "UBSan: Unexpected 128-Bit Type in %s\n", szLocation);
               __unreachable();
               /* NOTREACHED */
#endif
       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);
               __unreachable();
               /* 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);
               __unreachable();
               /* 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);
               __unreachable();
               /* NOTREACHED */
#ifdef __HAVE_LONG_DOUBLE
       case WIDTH_128:
               /* FALLTHROUGH */
       case WIDTH_96:
               /* FALLTHROUGH */
       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;
               }
               /* FALLTHROUGH */
       case WIDTH_32:
               /* FALLTHROUGH */
       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);
               __unreachable();
               /* NOTREACHED */
#else
               DeserializeNumberFloat(szLocation, pBuffer, zBUfferLength, pType, ulNumber);
               break;
#endif
       case KIND_UNKNOWN:
               Report(true, "UBSan: Unknown Type in %s\n", szLocation);
               __unreachable();
               /* NOTREACHED */
       }
}

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 const char *
DeserializeImplicitConversionCheckKind(uint8_t hhuImplicitConversionCheckKind)
{
       const char *rgczImplicitConversionCheckKind[] = {
               "integer truncation",                   /* Not used since 2018 October 11th */
               "unsigned integer truncation",
               "signed integer truncation",
               "integer sign change",
               "signed integer trunctation or sign change",
       };

       ASSERT(__arraycount(rgczImplicitConversionCheckKind) > hhuImplicitConversionCheckKind);

       return rgczImplicitConversionCheckKind[hhuImplicitConversionCheckKind];
}

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;
}