0f426dedcc
- s/asumption/assumption/
Obtained from: NetBSD
(cherry picked from commit 2aad906266
)
1705 lines
51 KiB
C
1705 lines
51 KiB
C
/* $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 <sys/cdefs.h>
|
|
#ifdef __FreeBSD__
|
|
__FBSDID("$FreeBSD$");
|
|
#else
|
|
#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
|
|
#endif
|
|
|
|
#if defined(_KERNEL)
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/limits.h>
|
|
#include <sys/systm.h>
|
|
#include <machine/_inttypes.h>
|
|
#include <machine/stdarg.h>
|
|
#define ASSERT(x) KASSERT(x, ("%s: " __STRING(x) " failed", __func__))
|
|
#define __arraycount(x) nitems(x)
|
|
#define ISSET(x, y) ((x) & (y))
|
|
#define __BIT(x) ((uintmax_t)1 << (uintmax_t)(x))
|
|
#define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask))
|
|
#define __SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask))
|
|
#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
|
|
|
|
#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;
|
|
};
|
|
|
|
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 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_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 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
|
|
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(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;
|
|
}
|