freebsd-dev/sys/kern/kern_ubsan.c
Andrew Turner 4ea56599e8 Port the NetBSD ubsan runtime to the FreeBSD kernel.
This allows us to build the ubsan code added in r340189 into the kernel
with the KUBSAN option. This will report when undefined behaviour is
detected in the currently running kernel.

As it can be large, the kernel is 65MB on arm64, loader may not be able to
load the kernel on all architectures so is disabled by default for now.

Sponsored by:	DARPA, AFRL
2018-11-06 17:32:07 +00:00

1651 lines
49 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;
};
/* Local utility functions */
static void Report(bool isFatal, const char *pFormat, ...) __printflike(2, 3);
static bool isAlreadyReported(struct CSourceLocation *pLocation);
static size_t zDeserializeTypeWidth(struct CTypeDescriptor *pType);
static void DeserializeLocation(char *pBuffer, size_t zBUfferLength, struct CSourceLocation *pLocation);
#ifdef __SIZEOF_INT128__
static void DeserializeUINT128(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, __uint128_t U128);
#endif
static void DeserializeNumberSigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, longest L);
static void DeserializeNumberUnsigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, ulongest L);
#ifndef _KERNEL
static void DeserializeFloatOverPointer(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long *pNumber);
static void DeserializeFloatInlined(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber);
#endif
static longest llliGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber);
static ulongest llluGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber);
#ifndef _KERNEL
static void DeserializeNumberFloat(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber);
#endif
static void DeserializeNumber(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber);
static const char *DeserializeTypeCheckKind(uint8_t hhuTypeCheckKind);
static const char *DeserializeBuiltinCheckKind(uint8_t hhuBuiltinCheckKind);
static const char *DeserializeCFICheckKind(uint8_t hhuCFICheckKind);
static bool isNegativeNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber);
static bool isShiftExponentTooLarge(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber, size_t zWidth);
/* Unused in this implementation, emitted by the C++ check dynamic type cast. */
intptr_t __ubsan_vptr_type_cache[128];
/* Public symbols used in the instrumentation of the code generation part */
void __ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_builtin_unreachable(struct CUnreachableData *pData);
void __ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer);
void __ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable);
void __ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable);
void __ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
void __ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
void __ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom);
void __ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom);
void __ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
void __ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
void __ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData);
void __ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData);
void __ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulVal);
void __ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulVal);
void __ubsan_handle_missing_return(struct CUnreachableData *pData);
void __ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldVal);
void __ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldVal);
void __ubsan_handle_nonnull_arg(struct CNonNullArgData *pData);
void __ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData);
void __ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
void __ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
void __ubsan_handle_nullability_arg(struct CNonNullArgData *pData);
void __ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData);
void __ubsan_handle_nullability_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
void __ubsan_handle_nullability_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
void __ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex);
void __ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex);
void __ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
void __ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
void __ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, unsigned long ulPointer);
void __ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, unsigned long ulPointer);
void __ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer);
void __ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer);
void __ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound);
void __ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound);
void __ubsan_get_current_report_data(const char **ppOutIssueKind, const char **ppOutMessage, const char **ppOutFilename, uint32_t *pOutLine, uint32_t *pOutCol, char **ppOutMemoryAddr);
static void HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation);
static void HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue);
static void HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData);
static void HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer);
static void HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound);
static void HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex);
static void HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
static void HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue);
static void HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData);
static void HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
static void HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer);
static void HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
static void HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom);
static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData);
static void HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData);
static void HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
static void HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
static void
HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation)
{
char szLocation[LOCATION_MAXLEN];
char szLHS[NUMBER_MAXLEN];
char szRHS[NUMBER_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
DeserializeNumber(szLocation, szLHS, NUMBER_MAXLEN, pData->mType, ulLHS);
DeserializeNumber(szLocation, szRHS, NUMBER_MAXLEN, pData->mType, ulRHS);
Report(isFatal, "UBSan: Undefined Behavior in %s, %s integer overflow: %s %s %s cannot be represented in type %s\n",
szLocation, ISSET(pData->mType->mTypeInfo, NUMBER_SIGNED_BIT) ? "signed" : "unsigned", szLHS, szOperation, szRHS, pData->mType->mTypeName);
}
static void
HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue)
{
char szLocation[LOCATION_MAXLEN];
char szOldValue[NUMBER_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
DeserializeNumber(szLocation, szOldValue, NUMBER_MAXLEN, pData->mType, ulOldValue);
Report(isFatal, "UBSan: Undefined Behavior in %s, negation of %s cannot be represented in type %s\n",
szLocation, szOldValue, pData->mType->mTypeName);
}
static void
HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData)
{
char szLocation[LOCATION_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
Report(isFatal, "UBSan: Undefined Behavior in %s, calling __builtin_unreachable()\n",
szLocation);
}
static void
HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer)
{
char szLocation[LOCATION_MAXLEN];
ASSERT(mLocation);
ASSERT(mType);
if (isAlreadyReported(mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, mLocation);
if (ulPointer == 0) {
Report(isFatal, "UBSan: Undefined Behavior in %s, %s null pointer of type %s\n",
szLocation, DeserializeTypeCheckKind(mTypeCheckKind), mType->mTypeName);
} else if ((mLogAlignment - 1) & ulPointer) {
Report(isFatal, "UBSan: Undefined Behavior in %s, %s misaligned address %p for type %s which requires %ld byte alignment\n",
szLocation, DeserializeTypeCheckKind(mTypeCheckKind), REINTERPRET_CAST(void *, ulPointer), mType->mTypeName, mLogAlignment);
} else {
Report(isFatal, "UBSan: Undefined Behavior in %s, %s address %p with insufficient space for an object of type %s\n",
szLocation, DeserializeTypeCheckKind(mTypeCheckKind), REINTERPRET_CAST(void *, ulPointer), mType->mTypeName);
}
}
static void
HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound)
{
char szLocation[LOCATION_MAXLEN];
char szBound[NUMBER_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
DeserializeNumber(szLocation, szBound, NUMBER_MAXLEN, pData->mType, ulBound);
Report(isFatal, "UBSan: Undefined Behavior in %s, variable length array bound value %s <= 0\n",
szLocation, szBound);
}
static void
HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex)
{
char szLocation[LOCATION_MAXLEN];
char szIndex[NUMBER_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
DeserializeNumber(szLocation, szIndex, NUMBER_MAXLEN, pData->mIndexType, ulIndex);
Report(isFatal, "UBSan: Undefined Behavior in %s, index %s is out of range for type %s\n",
szLocation, szIndex, pData->mArrayType->mTypeName);
}
static void
HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
char szLocation[LOCATION_MAXLEN];
char szLHS[NUMBER_MAXLEN];
char szRHS[NUMBER_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
DeserializeNumber(szLocation, szLHS, NUMBER_MAXLEN, pData->mLHSType, ulLHS);
DeserializeNumber(szLocation, szRHS, NUMBER_MAXLEN, pData->mRHSType, ulRHS);
if (isNegativeNumber(szLocation, pData->mRHSType, ulRHS))
Report(isFatal, "UBSan: Undefined Behavior in %s, shift exponent %s is negative\n",
szLocation, szRHS);
else if (isShiftExponentTooLarge(szLocation, pData->mRHSType, ulRHS, zDeserializeTypeWidth(pData->mLHSType)))
Report(isFatal, "UBSan: Undefined Behavior in %s, shift exponent %s is too large for %zu-bit type %s\n",
szLocation, szRHS, zDeserializeTypeWidth(pData->mLHSType), pData->mLHSType->mTypeName);
else if (isNegativeNumber(szLocation, pData->mLHSType, ulLHS))
Report(isFatal, "UBSan: Undefined Behavior in %s, left shift of negative value %s\n",
szLocation, szLHS);
else
Report(isFatal, "UBSan: Undefined Behavior in %s, left shift of %s by %s places cannot be represented in type %s\n",
szLocation, szLHS, szRHS, pData->mLHSType->mTypeName);
}
static void
HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue)
{
char szLocation[LOCATION_MAXLEN];
char szValue[NUMBER_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
DeserializeNumber(szLocation, szValue, NUMBER_MAXLEN, pData->mType, ulValue);
Report(isFatal, "UBSan: Undefined Behavior in %s, load of value %s is not a valid value for type %s\n",
szLocation, szValue, pData->mType->mTypeName);
}
static void
HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData)
{
char szLocation[LOCATION_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
Report(isFatal, "UBSan: Undefined Behavior in %s, passing zero to %s, which is not a valid argument\n",
szLocation, DeserializeBuiltinCheckKind(pData->mKind));
}
static void
HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
{
char szLocation[LOCATION_MAXLEN];
/*
* There is no a portable C solution to translate an address of a
* function to its name. On the cost of getting this routine simple
* and portable without ifdefs between the userland and the kernel
* just print the address of the function as-is.
*
* For better diagnostic messages in the userland, users shall use
* the full upstream version shipped along with the compiler toolchain.
*/
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
Report(isFatal, "UBSan: Undefined Behavior in %s, call to function %#lx through pointer to incorrect function type %s\n",
szLocation, ulFunction, pData->mType->mTypeName);
}
static void
HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer)
{
char szLocation[LOCATION_MAXLEN];
/*
* This is a minimal implementation without diving into C++
* specifics and (Itanium) ABI deserialization.
*/
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
if (pData->mCheckKind == CFI_ICALL || pData->mCheckKind == CFI_VMFCALL) {
Report(isFatal, "UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx)\n",
szLocation, pData->mType->mTypeName, DeserializeCFICheckKind(pData->mCheckKind), ulVtable);
} else {
Report(isFatal || FromUnrecoverableHandler, "UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx; %s vtable; from %s handler; Program Counter %#lx; Frame Pointer %#lx)\n",
szLocation, pData->mType->mTypeName, DeserializeCFICheckKind(pData->mCheckKind), ulVtable, *bValidVtable ? "valid" : "invalid", *FromUnrecoverableHandler ? "unrecoverable" : "recoverable", *ProgramCounter, *FramePointer);
}
}
static void
HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash)
{
#if 0
char szLocation[LOCATION_MAXLEN];
/*
* Unimplemented.
*
* This UBSan handler is special as the check has to be impelemented
* in an implementation. In order to handle it there is need to
* introspect into C++ ABI internals (RTTI) and use low-level
* C++ runtime interfaces.
*/
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
Report(isFatal, "UBSan: Undefined Behavior in %s, %s address %#lx which might not point to an object of type %s\n"
szLocation, DeserializeTypeCheckKind(pData->mTypeCheckKind), ulPointer, pData->mType);
#endif
}
static void
HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom)
{
char szLocation[LOCATION_MAXLEN];
char szFrom[NUMBER_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
DeserializeNumber(szLocation, szFrom, NUMBER_MAXLEN, pData->mFromType, ulFrom);
Report(isFatal, "UBSan: Undefined Behavior in %s, %s (of type %s) is outside the range of representable values of type %s\n",
szLocation, szFrom, pData->mFromType->mTypeName, pData->mToType->mTypeName);
}
static void
HandleMissingReturn(bool isFatal, struct CUnreachableData *pData)
{
char szLocation[LOCATION_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
Report(isFatal, "UBSan: Undefined Behavior in %s, execution reached the end of a value-returning function without returning a value\n",
szLocation);
}
static void
HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData)
{
char szLocation[LOCATION_MAXLEN];
char szAttributeLocation[LOCATION_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
if (pData->mAttributeLocation.mFilename)
DeserializeLocation(szAttributeLocation, LOCATION_MAXLEN, &pData->mAttributeLocation);
else
szAttributeLocation[0] = '\0';
Report(isFatal, "UBSan: Undefined Behavior in %s, null pointer passed as argument %d, which is declared to never be null%s%s\n",
szLocation, pData->mArgIndex, pData->mAttributeLocation.mFilename ? ", nonnull/_Nonnull specified in " : "", szAttributeLocation);
}
static void
HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{
char szLocation[LOCATION_MAXLEN];
char szAttributeLocation[LOCATION_MAXLEN];
ASSERT(pData);
ASSERT(pLocationPointer);
if (isAlreadyReported(pLocationPointer))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, pLocationPointer);
if (pData->mAttributeLocation.mFilename)
DeserializeLocation(szAttributeLocation, LOCATION_MAXLEN, &pData->mAttributeLocation);
else
szAttributeLocation[0] = '\0';
Report(isFatal, "UBSan: Undefined Behavior in %s, null pointer returned from function declared to never return null%s%s\n",
szLocation, pData->mAttributeLocation.mFilename ? ", nonnull/_Nonnull specified in " : "", szAttributeLocation);
}
static void
HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
{
char szLocation[LOCATION_MAXLEN];
ASSERT(pData);
if (isAlreadyReported(&pData->mLocation))
return;
DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
Report(isFatal, "UBSan: Undefined Behavior in %s, pointer expression with base %#lx overflowed to %#lx\n",
szLocation, ulBase, ulResult);
}
/* Definions of public symbols emitted by the instrumentation code */
void
__ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleOverflow(false, pData, ulLHS, ulRHS, PLUS_STRING);
}
void
__ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleOverflow(true, pData, ulLHS, ulRHS, PLUS_STRING);
}
void
__ubsan_handle_builtin_unreachable(struct CUnreachableData *pData)
{
ASSERT(pData);
HandleBuiltinUnreachable(true, pData);
}
void
__ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer)
{
ASSERT(pData);
HandleCFIBadType(false, pData, ulVtable, &bValidVtable, &FromUnrecoverableHandler, &ProgramCounter, &FramePointer);
}
void
__ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable)
{
ASSERT(pData);
HandleCFIBadType(false, pData, ulValue, 0, 0, 0, 0);
}
void
__ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable)
{
ASSERT(pData);
HandleCFIBadType(true, pData, ulValue, 0, 0, 0, 0);
}
void
__ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleOverflow(false, pData, ulLHS, ulRHS, DIVREM_STRING);
}
void
__ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleOverflow(true, pData, ulLHS, ulRHS, DIVREM_STRING);
}
void
__ubsan_handle_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash)
{
ASSERT(pData);
HandleDynamicTypeCacheMiss(false, pData, ulPointer, ulHash);
}
void
__ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash)
{
ASSERT(pData);
HandleDynamicTypeCacheMiss(false, pData, ulPointer, ulHash);
}
void
__ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom)
{
ASSERT(pData);
HandleFloatCastOverflow(false, pData, ulFrom);
}
void
__ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom)
{
ASSERT(pData);
HandleFloatCastOverflow(true, pData, ulFrom);
}
void
__ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
{
ASSERT(pData);
HandleFunctionTypeMismatch(false, pData, ulFunction);
}
void
__ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
{
ASSERT(pData);
HandleFunctionTypeMismatch(false, pData, ulFunction);
}
void
__ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData)
{
ASSERT(pData);
HandleInvalidBuiltin(true, pData);
}
void
__ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData)
{
ASSERT(pData);
HandleInvalidBuiltin(true, pData);
}
void
__ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulValue)
{
ASSERT(pData);
HandleLoadInvalidValue(false, pData, ulValue);
}
void
__ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulValue)
{
ASSERT(pData);
HandleLoadInvalidValue(true, pData, ulValue);
}
void
__ubsan_handle_missing_return(struct CUnreachableData *pData)
{
ASSERT(pData);
HandleMissingReturn(true, pData);
}
void
__ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleOverflow(false, pData, ulLHS, ulRHS, MUL_STRING);
}
void
__ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleOverflow(true, pData, ulLHS, ulRHS, MUL_STRING);
}
void
__ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldValue)
{
ASSERT(pData);
HandleNegateOverflow(false, pData, ulOldValue);
}
void
__ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldValue)
{
ASSERT(pData);
HandleNegateOverflow(true, pData, ulOldValue);
}
void
__ubsan_handle_nonnull_arg(struct CNonNullArgData *pData)
{
ASSERT(pData);
HandleNonnullArg(false, pData);
}
void
__ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData)
{
ASSERT(pData);
HandleNonnullArg(true, pData);
}
void
__ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{
ASSERT(pData);
ASSERT(pLocationPointer);
HandleNonnullReturn(false, pData, pLocationPointer);
}
void
__ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{
ASSERT(pData);
ASSERT(pLocationPointer);
HandleNonnullReturn(true, pData, pLocationPointer);
}
void
__ubsan_handle_nullability_arg(struct CNonNullArgData *pData)
{
ASSERT(pData);
HandleNonnullArg(false, pData);
}
void
__ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData)
{
ASSERT(pData);
HandleNonnullArg(true, pData);
}
void
__ubsan_handle_nullability_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{
ASSERT(pData);
ASSERT(pLocationPointer);
HandleNonnullReturn(false, pData, pLocationPointer);
}
void
__ubsan_handle_nullability_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{
ASSERT(pData);
ASSERT(pLocationPointer);
HandleNonnullReturn(true, pData, pLocationPointer);
}
void
__ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex)
{
ASSERT(pData);
HandleOutOfBounds(false, pData, ulIndex);
}
void
__ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex)
{
ASSERT(pData);
HandleOutOfBounds(true, pData, ulIndex);
}
void
__ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
{
ASSERT(pData);
HandlePointerOverflow(false, pData, ulBase, ulResult);
}
void
__ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
{
ASSERT(pData);
HandlePointerOverflow(true, pData, ulBase, ulResult);
}
void
__ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleShiftOutOfBounds(false, pData, ulLHS, ulRHS);
}
void
__ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleShiftOutOfBounds(true, pData, ulLHS, ulRHS);
}
void
__ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleOverflow(false, pData, ulLHS, ulRHS, MINUS_STRING);
}
void
__ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
ASSERT(pData);
HandleOverflow(true, pData, ulLHS, ulRHS, MINUS_STRING);
}
void
__ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, unsigned long ulPointer)
{
ASSERT(pData);
HandleTypeMismatch(false, &pData->mLocation, pData->mType, pData->mLogAlignment, pData->mTypeCheckKind, ulPointer);
}
void
__ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, unsigned long ulPointer)
{
ASSERT(pData);
HandleTypeMismatch(true, &pData->mLocation, pData->mType, pData->mLogAlignment, pData->mTypeCheckKind, ulPointer);
}
void
__ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer)
{
ASSERT(pData);
HandleTypeMismatch(false, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer);
}
void
__ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer)
{
ASSERT(pData);
HandleTypeMismatch(true, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer);
}
void
__ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound)
{
ASSERT(pData);
HandleVlaBoundNotPositive(false, pData, ulBound);
}
void
__ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound)
{
ASSERT(pData);
HandleVlaBoundNotPositive(true, pData, ulBound);
}
void
__ubsan_get_current_report_data(const char **ppOutIssueKind, const char **ppOutMessage, const char **ppOutFilename, uint32_t *pOutLine, uint32_t *pOutCol, char **ppOutMemoryAddr)
{
/*
* Unimplemented.
*
* The __ubsan_on_report() feature is non trivial to implement in a
* shared code between the kernel and userland. It's also opening
* new sets of potential problems as we are not expected to slow down
* execution of certain kernel subsystems (synchronization issues,
* interrupt handling etc).
*
* A proper solution would need probably a lock-free bounded queue built
* with atomic operations with the property of miltiple consumers and
* multiple producers. Maintaining and validating such code is not
* worth the effort.
*
* A legitimate user - besides testing framework - is a debugger plugin
* intercepting reports from the UBSan instrumentation. For such
* scenarios it is better to run the Clang/GCC version.
*/
}
/* Local utility functions */
static void
Report(bool isFatal, const char *pFormat, ...)
{
va_list ap;
ASSERT(pFormat);
va_start(ap, pFormat);
#if defined(_KERNEL)
if (isFatal)
vpanic(pFormat, ap);
else
vprintf(pFormat, ap);
#else
if (ubsan_flags == -1) {
char buf[1024];
char *p;
ubsan_flags = UBSAN_STDERR;
if (getenv_r("LIBC_UBSAN", buf, sizeof(buf)) != -1) {
for (p = buf; *p; p++) {
switch (*p) {
case 'a':
SET(ubsan_flags, UBSAN_ABORT);
break;
case 'A':
CLR(ubsan_flags, UBSAN_ABORT);
break;
case 'e':
SET(ubsan_flags, UBSAN_STDERR);
break;
case 'E':
CLR(ubsan_flags, UBSAN_STDERR);
break;
case 'l':
SET(ubsan_flags, UBSAN_SYSLOG);
break;
case 'L':
CLR(ubsan_flags, UBSAN_SYSLOG);
break;
case 'o':
SET(ubsan_flags, UBSAN_STDOUT);
break;
case 'O':
CLR(ubsan_flags, UBSAN_STDOUT);
break;
default:
break;
}
}
}
}
// The *v*print* functions can flush the va_list argument.
// Create a local copy for each call to prevent invalid read.
if (ISSET(ubsan_flags, UBSAN_STDOUT)) {
va_list tmp;
va_copy(tmp, ap);
vprintf(pFormat, tmp);
va_end(tmp);
fflush(stdout);
}
if (ISSET(ubsan_flags, UBSAN_STDERR)) {
va_list tmp;
va_copy(tmp, ap);
vfprintf(stderr, pFormat, tmp);
va_end(tmp);
fflush(stderr);
}
if (ISSET(ubsan_flags, UBSAN_SYSLOG)) {
va_list tmp;
va_copy(tmp, ap);
struct syslog_data SyslogData = SYSLOG_DATA_INIT;
ubsan_vsyslog(LOG_DEBUG | LOG_USER, &SyslogData, pFormat, tmp);
va_end(tmp);
}
if (isFatal || ISSET(ubsan_flags, UBSAN_ABORT)) {
abort();
/* NOTREACHED */
}
#endif
va_end(ap);
}
static bool
isAlreadyReported(struct CSourceLocation *pLocation)
{
/*
* This code is shared between libc, kernel and standalone usage.
* It shall work in early bootstrap phase of both of them.
*/
uint32_t siOldValue;
volatile uint32_t *pLine;
ASSERT(pLocation);
pLine = &pLocation->mLine;
do {
siOldValue = *pLine;
} while (__sync_val_compare_and_swap(pLine, siOldValue, siOldValue | ACK_REPORTED) != siOldValue);
return ISSET(siOldValue, ACK_REPORTED);
}
static size_t
zDeserializeTypeWidth(struct CTypeDescriptor *pType)
{
size_t zWidth = 0;
ASSERT(pType);
switch (pType->mTypeKind) {
case KIND_INTEGER:
zWidth = __BIT(__SHIFTOUT(pType->mTypeInfo, ~NUMBER_SIGNED_BIT));
break;
case KIND_FLOAT:
zWidth = pType->mTypeInfo;
break;
default:
Report(true, "UBSan: Unknown variable type %#04" PRIx16 "\n", pType->mTypeKind);
/* NOTREACHED */
}
/* Invalid width will be transformed to 0 */
ASSERT(zWidth > 0);
return zWidth;
}
static void
DeserializeLocation(char *pBuffer, size_t zBUfferLength, struct CSourceLocation *pLocation)
{
ASSERT(pLocation);
ASSERT(pLocation->mFilename);
snprintf(pBuffer, zBUfferLength, "%s:%" PRIu32 ":%" PRIu32, pLocation->mFilename, pLocation->mLine & (uint32_t)~ACK_REPORTED, pLocation->mColumn);
}
#ifdef __SIZEOF_INT128__
static void
DeserializeUINT128(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, __uint128_t U128)
{
char szBuf[3]; /* 'XX\0' */
char rgNumber[sizeof(ulongest)];
ssize_t zI;
memcpy(rgNumber, &U128, sizeof(U128));
strlcpy(pBuffer, "Undecoded-128-bit-Integer-Type (0x", zBUfferLength);
#if BYTE_ORDER == LITTLE_ENDIAN
for (zI = sizeof(ulongest) - 1; zI >= 0; zI--) {
#else
for (zI = 0; zI < (ssize_t)sizeof(ulongest); zI++) {
#endif
snprintf(szBuf, sizeof(szBuf), "%02" PRIx8, rgNumber[zI]);
strlcat(pBuffer, szBuf, zBUfferLength);
}
strlcat(pBuffer, ")", zBUfferLength);
}
#endif
static void
DeserializeNumberSigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, longest L)
{
ASSERT(pBuffer);
ASSERT(zBUfferLength > 0);
ASSERT(pType);
ASSERT(ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT));
switch (zDeserializeTypeWidth(pType)) {
default:
ASSERT(0 && "Invalid codepath");
/* NOTREACHED */
#ifdef __SIZEOF_INT128__
case WIDTH_128:
DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L));
break;
#endif
case WIDTH_64:
case WIDTH_32:
case WIDTH_16:
case WIDTH_8:
snprintf(pBuffer, zBUfferLength, "%" PRId64, STATIC_CAST(int64_t, L));
break;
}
}
static void
DeserializeNumberUnsigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, ulongest L)
{
ASSERT(pBuffer);
ASSERT(zBUfferLength > 0);
ASSERT(pType);
ASSERT(!ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT));
switch (zDeserializeTypeWidth(pType)) {
default:
ASSERT(0 && "Invalid codepath");
/* NOTREACHED */
#ifdef __SIZEOF_INT128__
case WIDTH_128:
DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L));
break;
#endif
case WIDTH_64:
case WIDTH_32:
case WIDTH_16:
case WIDTH_8:
snprintf(pBuffer, zBUfferLength, "%" PRIu64, STATIC_CAST(uint64_t, L));
break;
}
}
#ifndef _KERNEL
static void
DeserializeFloatOverPointer(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long *pNumber)
{
double D;
#ifdef __HAVE_LONG_DOUBLE
long double LD;
#endif
ASSERT(pBuffer);
ASSERT(zBUfferLength > 0);
ASSERT(pType);
ASSERT(pNumber);
/*
* This function handles 64-bit number over a pointer on 32-bit CPUs.
*/
ASSERT((sizeof(*pNumber) * CHAR_BIT < WIDTH_64) || (zDeserializeTypeWidth(pType) >= WIDTH_64));
ASSERT(sizeof(D) == sizeof(uint64_t));
#ifdef __HAVE_LONG_DOUBLE
ASSERT(sizeof(LD) > sizeof(uint64_t));
#endif
switch (zDeserializeTypeWidth(pType)) {
#ifdef __HAVE_LONG_DOUBLE
case WIDTH_128:
case WIDTH_96:
case WIDTH_80:
memcpy(&LD, pNumber, sizeof(long double));
snprintf(pBuffer, zBUfferLength, "%Lg", LD);
break;
#endif
case WIDTH_64:
memcpy(&D, pNumber, sizeof(double));
snprintf(pBuffer, zBUfferLength, "%g", D);
break;
}
}
static void
DeserializeFloatInlined(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
float F;
double D;
uint32_t U32;
ASSERT(pBuffer);
ASSERT(zBUfferLength > 0);
ASSERT(pType);
ASSERT(sizeof(F) == sizeof(uint32_t));
ASSERT(sizeof(D) == sizeof(uint64_t));
switch (zDeserializeTypeWidth(pType)) {
case WIDTH_64:
memcpy(&D, &ulNumber, sizeof(double));
snprintf(pBuffer, zBUfferLength, "%g", D);
break;
case WIDTH_32:
/*
* On supported platforms sizeof(float)==sizeof(uint32_t)
* unsigned long is either 32 or 64-bit, cast it to 32-bit
* value in order to call memcpy(3) in an Endian-aware way.
*/
U32 = STATIC_CAST(uint32_t, ulNumber);
memcpy(&F, &U32, sizeof(float));
snprintf(pBuffer, zBUfferLength, "%g", F);
break;
case WIDTH_16:
snprintf(pBuffer, zBUfferLength, "Undecoded-16-bit-Floating-Type (%#04" PRIx16 ")", STATIC_CAST(uint16_t, ulNumber));
break;
}
}
#endif
static longest
llliGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
size_t zNumberWidth;
longest L = 0;
ASSERT(szLocation);
ASSERT(pType);
zNumberWidth = zDeserializeTypeWidth(pType);
switch (zNumberWidth) {
default:
Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation);
/* NOTREACHED */
case WIDTH_128:
#ifdef __SIZEOF_INT128__
memcpy(&L, REINTERPRET_CAST(longest *, ulNumber), sizeof(longest));
#else
Report(true, "UBSan: Unexpected 128-Bit Type in %s\n", szLocation);
/* NOTREACHED */
#endif
break;
case WIDTH_64:
if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) {
L = *REINTERPRET_CAST(int64_t *, ulNumber);
} else {
L = STATIC_CAST(int64_t, STATIC_CAST(uint64_t, ulNumber));
}
break;
case WIDTH_32:
L = STATIC_CAST(int32_t, STATIC_CAST(uint32_t, ulNumber));
break;
case WIDTH_16:
L = STATIC_CAST(int16_t, STATIC_CAST(uint16_t, ulNumber));
break;
case WIDTH_8:
L = STATIC_CAST(int8_t, STATIC_CAST(uint8_t, ulNumber));
break;
}
return L;
}
static ulongest
llluGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
size_t zNumberWidth;
ulongest UL = 0;
ASSERT(pType);
zNumberWidth = zDeserializeTypeWidth(pType);
switch (zNumberWidth) {
default:
Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation);
/* NOTREACHED */
case WIDTH_128:
#ifdef __SIZEOF_INT128__
memcpy(&UL, REINTERPRET_CAST(ulongest *, ulNumber), sizeof(ulongest));
break;
#else
Report(true, "UBSan: Unexpected 128-Bit Type in %s\n", szLocation);
/* NOTREACHED */
#endif
case WIDTH_64:
if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) {
UL = *REINTERPRET_CAST(uint64_t *, ulNumber);
break;
}
/* FALLTHROUGH */
case WIDTH_32:
/* FALLTHROUGH */
case WIDTH_16:
/* FALLTHROUGH */
case WIDTH_8:
UL = ulNumber;
break;
}
return UL;
}
#ifndef _KERNEL
static void
DeserializeNumberFloat(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
size_t zNumberWidth;
ASSERT(szLocation);
ASSERT(pBuffer);
ASSERT(zBUfferLength > 0);
ASSERT(pType);
ASSERT(pType->mTypeKind == KIND_FLOAT);
zNumberWidth = zDeserializeTypeWidth(pType);
switch (zNumberWidth) {
default:
Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation);
/* NOTREACHED */
#ifdef __HAVE_LONG_DOUBLE
case WIDTH_128:
case WIDTH_96:
case WIDTH_80:
DeserializeFloatOverPointer(pBuffer, zBUfferLength, pType, REINTERPRET_CAST(unsigned long *, ulNumber));
break;
#endif
case WIDTH_64:
if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) {
DeserializeFloatOverPointer(pBuffer, zBUfferLength, pType, REINTERPRET_CAST(unsigned long *, ulNumber));
break;
}
case WIDTH_32:
case WIDTH_16:
DeserializeFloatInlined(pBuffer, zBUfferLength, pType, ulNumber);
break;
}
}
#endif
static void
DeserializeNumber(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
ASSERT(szLocation);
ASSERT(pBuffer);
ASSERT(zBUfferLength > 0);
ASSERT(pType);
switch(pType->mTypeKind) {
case KIND_INTEGER:
if (ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)) {
longest L = llliGetNumber(szLocation, pType, ulNumber);
DeserializeNumberSigned(pBuffer, zBUfferLength, pType, L);
} else {
ulongest UL = llluGetNumber(szLocation, pType, ulNumber);
DeserializeNumberUnsigned(pBuffer, zBUfferLength, pType, UL);
}
break;
case KIND_FLOAT:
#ifdef _KERNEL
Report(true, "UBSan: Unexpected Float Type in %s\n", szLocation);
/* NOTREACHED */
#else
DeserializeNumberFloat(szLocation, pBuffer, zBUfferLength, pType, ulNumber);
#endif
break;
case KIND_UNKNOWN:
Report(true, "UBSan: Unknown Type in %s\n", szLocation);
/* NOTREACHED */
break;
}
}
static const char *
DeserializeTypeCheckKind(uint8_t hhuTypeCheckKind)
{
const char *rgczTypeCheckKinds[] = {
"load of",
"store to",
"reference binding to",
"member access within",
"member call on",
"constructor call on",
"downcast of",
"downcast of",
"upcast of",
"cast to virtual base of",
"_Nonnull binding to",
"dynamic operation on"
};
ASSERT(__arraycount(rgczTypeCheckKinds) > hhuTypeCheckKind);
return rgczTypeCheckKinds[hhuTypeCheckKind];
}
static const char *
DeserializeBuiltinCheckKind(uint8_t hhuBuiltinCheckKind)
{
const char *rgczBuiltinCheckKinds[] = {
"ctz()",
"clz()"
};
ASSERT(__arraycount(rgczBuiltinCheckKinds) > hhuBuiltinCheckKind);
return rgczBuiltinCheckKinds[hhuBuiltinCheckKind];
}
static const char *
DeserializeCFICheckKind(uint8_t hhuCFICheckKind)
{
const char *rgczCFICheckKinds[] = {
"virtual call", // CFI_VCALL
"non-virtual call", // CFI_NVCALL
"base-to-derived cast", // CFI_DERIVEDCAST
"cast to unrelated type", // CFI_UNRELATEDCAST
"indirect function call", // CFI_ICALL
"non-virtual pointer to member function call", // CFI_NVMFCALL
"virtual pointer to member function call", // CFI_VMFCALL
};
ASSERT(__arraycount(rgczCFICheckKinds) > hhuCFICheckKind);
return rgczCFICheckKinds[hhuCFICheckKind];
}
static bool
isNegativeNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
ASSERT(szLocation);
ASSERT(pType);
ASSERT(pType->mTypeKind == KIND_INTEGER);
if (!ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT))
return false;
return llliGetNumber(szLocation, pType, ulNumber) < 0;
}
static bool
isShiftExponentTooLarge(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber, size_t zWidth)
{
ASSERT(szLocation);
ASSERT(pType);
ASSERT(pType->mTypeKind == KIND_INTEGER);
return llluGetNumber(szLocation, pType, ulNumber) >= zWidth;
}