/* $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 #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 #include #include #include #include #include #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 #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_LIBC) #include "extern.h" #define ubsan_vsyslog vsyslog_ss #define ASSERT(x) _DIAGASSERT(x) #else #define ubsan_vsyslog vsyslog_r #define ASSERT(x) assert(x) #endif /* These macros are available in _KERNEL only */ #define SET(t, f) ((t) |= (f)) #define ISSET(t, f) ((t) & (f)) #define CLR(t, f) ((t) &= ~(f)) #endif #define REINTERPRET_CAST(__dt, __st) ((__dt)(__st)) #define STATIC_CAST(__dt, __st) ((__dt)(__st)) #define ACK_REPORTED __BIT(31) #define MUL_STRING "*" #define PLUS_STRING "+" #define MINUS_STRING "-" #define DIVREM_STRING "divrem" #define CFI_VCALL 0 #define CFI_NVCALL 1 #define CFI_DERIVEDCAST 2 #define CFI_UNRELATEDCAST 3 #define CFI_ICALL 4 #define CFI_NVMFCALL 5 #define CFI_VMFCALL 6 #define NUMBER_MAXLEN 128 #define LOCATION_MAXLEN (PATH_MAX + 32 /* ':LINE:COLUMN' */) #define WIDTH_8 8 #define WIDTH_16 16 #define WIDTH_32 32 #define WIDTH_64 64 #define WIDTH_80 80 #define WIDTH_96 96 #define WIDTH_128 128 #define NUMBER_SIGNED_BIT 1U #if __SIZEOF_INT128__ typedef __int128 longest; typedef unsigned __int128 ulongest; #else typedef int64_t longest; typedef uint64_t ulongest; #endif #ifndef _KERNEL static int ubsan_flags = -1; #define UBSAN_ABORT __BIT(0) #define UBSAN_STDOUT __BIT(1) #define UBSAN_STDERR __BIT(2) #define UBSAN_SYSLOG __BIT(3) #endif /* Undefined Behavior specific defines and structures */ #define KIND_INTEGER 0 #define KIND_FLOAT 1 #define KIND_UNKNOWN UINT16_MAX struct CSourceLocation { char *mFilename; uint32_t mLine; uint32_t mColumn; }; struct CTypeDescriptor { uint16_t mTypeKind; uint16_t mTypeInfo; uint8_t mTypeName[1]; }; struct COverflowData { struct CSourceLocation mLocation; struct CTypeDescriptor *mType; }; struct CUnreachableData { struct CSourceLocation mLocation; }; struct CCFICheckFailData { uint8_t mCheckKind; struct CSourceLocation mLocation; struct CTypeDescriptor *mType; }; struct CDynamicTypeCacheMissData { struct CSourceLocation mLocation; struct CTypeDescriptor *mType; void *mTypeInfo; uint8_t mTypeCheckKind; }; struct CFunctionTypeMismatchData { struct CSourceLocation mLocation; struct CTypeDescriptor *mType; }; struct CInvalidBuiltinData { struct CSourceLocation mLocation; uint8_t mKind; }; struct CInvalidValueData { struct CSourceLocation mLocation; struct CTypeDescriptor *mType; }; struct CNonNullArgData { struct CSourceLocation mLocation; struct CSourceLocation mAttributeLocation; int mArgIndex; }; struct CNonNullReturnData { struct CSourceLocation mAttributeLocation; }; struct COutOfBoundsData { struct CSourceLocation mLocation; struct CTypeDescriptor *mArrayType; struct CTypeDescriptor *mIndexType; }; struct CPointerOverflowData { struct CSourceLocation mLocation; }; struct CShiftOutOfBoundsData { struct CSourceLocation mLocation; struct CTypeDescriptor *mLHSType; struct CTypeDescriptor *mRHSType; }; struct CTypeMismatchData { struct CSourceLocation mLocation; struct CTypeDescriptor *mType; unsigned long mLogAlignment; uint8_t mTypeCheckKind; }; struct CTypeMismatchData_v1 { struct CSourceLocation mLocation; struct CTypeDescriptor *mType; uint8_t mLogAlignment; uint8_t mTypeCheckKind; }; struct CVLABoundData { struct CSourceLocation mLocation; struct CTypeDescriptor *mType; }; struct CFloatCastOverflowData { struct CSourceLocation mLocation; /* This field exists in this struct since 2015 August 11th */ struct CTypeDescriptor *mFromType; struct CTypeDescriptor *mToType; }; 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), asumption 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; }