Module Name: src
Committed By: kamil
Date: Fri Aug 3 02:05:43 UTC 2018
Added Files:
src/common/lib/libc/misc: ubsan.c
Log Message:
Import micro-UBSan (ubsan.c)
This is a reimplementation of the Undefined Behavior Sanitizer with the
following properties:
- pure C implementation,
- no -fsanitize=vpts support, as it requires RTTI support and C++
low-level routies to validate whether C++ objects are compatible
- designed to be used inside libc and known as uUBSan or user-UBSan
- designed to be shared with kernel and known as kUBSan or kernel-UBSan
- designed to be usable with ATF tests as a standalone runtime,
reachable without any MK* switches
- designed to be safer for hardening as it does not have side effects on
executables like writing to a selected location on demand
- controllable with environment variable LIBC_UBSAN with options:
* a - abort on report
* A - do not abort on a report (unless a failure is unrecoverable)
* e - output report to stderr
* E - do not output report on stderr
* l - output report on syslog (LOG_DEBUG | LOG_USER)
* L - do not output report on syslog
* o - output report on stdout
* O - do not output report on stdout
The default options are: "AeLO".
- compatible with Clang (3.8, 7.x) and GCC (6.x) code generation
- all handlers (except =vptr) from Clang/LLVM up to 7svn are supported
This file does not follow the regular KNF style, due to potential licensing
concerns.
Tested with Clang amd64+i386 and GCC amd64+i386.
To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/common/lib/libc/misc/ubsan.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Added files:
Index: src/common/lib/libc/misc/ubsan.c
diff -u /dev/null src/common/lib/libc/misc/ubsan.c:1.1
--- /dev/null Fri Aug 3 02:05:43 2018
+++ src/common/lib/libc/misc/ubsan.c Fri Aug 3 02:05:43 2018
@@ -0,0 +1,1640 @@
+/* $NetBSD: ubsan.c,v 1.1 2018/08/03 02:05:43 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 and licensing reasons does not fully
+ * follow the KNF style with 80-column limit. Hungarian style variables
+ * and function names are on the same purpose (Pascal and Snake style names,
+ * are used in different implementations).
+ */
+
+#include <sys/cdefs.h>
+#if defined(_KERNEL)
+__KERNEL_RCSID(0, "$NetBSD: ubsan.c,v 1.1 2018/08/03 02:05:43 kamil Exp $");
+#else
+__RCSID("$NetBSD: ubsan.c,v 1.1 2018/08/03 02:05:43 kamil Exp $");
+#endif
+
+#if defined(_KERNEL)
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stdarg.h>
+#define ASSERT(x) KASSERT(x)
+#else
+#if defined(_LIBC)
+#include "namespace.h"
+#endif
+#include <sys/param.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <math.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#if defined(_LIBC)
+#include "extern.h"
+#define ubsan_vsyslog vsyslog_ss
+#define ASSERT(x) _DIAGASSERT(x)
+#else
+#define ubsan_vsyslog vsyslog_r
+#define ASSERT(x) assert(x)
+#endif
+/* These macros are available in _KERNEL only */
+#define SET(t, f) ((t) |= (f))
+#define ISSET(t, f) ((t) & (f))
+#define CLR(t, f) ((t) &= ~(f))
+#endif
+
+#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 < 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;
+}