2018-11-06 16:56:49 +00:00
/* $NetBSD: ubsan.c,v 1.3 2018/08/03 16:31:04 kamil Exp $ */
/*-
* Copyright ( c ) 2018 The NetBSD Foundation , Inc .
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION , INC . AND CONTRIBUTORS
* ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED
* TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
/*
* The micro UBSan implementation for the userland ( uUBSan ) and kernel ( kUBSan ) .
* The uBSSan versions is suitable for inclusion into libc or used standalone
* with ATF tests .
*
* This file due to long symbol names generated by a compiler during the
* instrumentation process does not follow the KNF style with 80 - column limit .
*/
# include <sys/cdefs.h>
2018-11-06 17:32:07 +00:00
# ifdef __FreeBSD__
__FBSDID ( " $FreeBSD$ " ) ;
# else
2018-11-06 16:56:49 +00:00
# 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
2018-11-06 17:32:07 +00:00
# endif
2018-11-06 16:56:49 +00:00
# if defined(_KERNEL)
# include <sys/param.h>
# include <sys/types.h>
2018-11-06 17:32:07 +00:00
# include <sys/limits.h>
# include <sys/systm.h>
# include <machine/_inttypes.h>
# include <machine/stdarg.h>
# define ASSERT(x) KASSERT(x, ("%s: " __STRING(x) " failed", __func__))
# define __arraycount(x) nitems(x)
# define ISSET(x, y) ((x) & (y))
# define __BIT(x) ((uintmax_t)1 << (uintmax_t)(x))
# define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask))
# define __SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask))
2018-11-06 16:56:49 +00:00
# else
# if defined(_LIBC)
# include "namespace.h"
# endif
# include <sys/param.h>
# include <assert.h>
# include <inttypes.h>
# include <math.h>
# include <signal.h>
# include <stdarg.h>
# include <stdbool.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <syslog.h>
# include <unistd.h>
# if defined(_LIBC)
# include "extern.h"
# define ubsan_vsyslog vsyslog_ss
# define ASSERT(x) _DIAGASSERT(x)
# else
# define ubsan_vsyslog vsyslog_r
# define ASSERT(x) assert(x)
# endif
/* These macros are available in _KERNEL only */
# define SET(t, f) ((t) |= (f))
# define ISSET(t, f) ((t) & (f))
# define CLR(t, f) ((t) &= ~(f))
# endif
# define REINTERPRET_CAST(__dt, __st) ((__dt)(__st))
# define STATIC_CAST(__dt, __st) ((__dt)(__st))
# define ACK_REPORTED __BIT(31)
# define MUL_STRING "*"
# define PLUS_STRING "+"
# define MINUS_STRING "-"
# define DIVREM_STRING "divrem"
# define CFI_VCALL 0
# define CFI_NVCALL 1
# define CFI_DERIVEDCAST 2
# define CFI_UNRELATEDCAST 3
# define CFI_ICALL 4
# define CFI_NVMFCALL 5
# define CFI_VMFCALL 6
# define NUMBER_MAXLEN 128
# define LOCATION_MAXLEN (PATH_MAX + 32 /* ':LINE:COLUMN' */ )
# define WIDTH_8 8
# define WIDTH_16 16
# define WIDTH_32 32
# define WIDTH_64 64
# define WIDTH_80 80
# define WIDTH_96 96
# define WIDTH_128 128
# define NUMBER_SIGNED_BIT 1U
# if __SIZEOF_INT128__
typedef __int128 longest ;
typedef unsigned __int128 ulongest ;
# else
typedef int64_t longest ;
typedef uint64_t ulongest ;
# endif
# ifndef _KERNEL
static int ubsan_flags = - 1 ;
# define UBSAN_ABORT __BIT(0)
# define UBSAN_STDOUT __BIT(1)
# define UBSAN_STDERR __BIT(2)
# define UBSAN_SYSLOG __BIT(3)
# endif
/* Undefined Behavior specific defines and structures */
# define KIND_INTEGER 0
# define KIND_FLOAT 1
# define KIND_UNKNOWN UINT16_MAX
struct CSourceLocation {
char * mFilename ;
uint32_t mLine ;
uint32_t mColumn ;
} ;
struct CTypeDescriptor {
uint16_t mTypeKind ;
uint16_t mTypeInfo ;
uint8_t mTypeName [ 1 ] ;
} ;
struct COverflowData {
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mType ;
} ;
struct CUnreachableData {
struct CSourceLocation mLocation ;
} ;
struct CCFICheckFailData {
uint8_t mCheckKind ;
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mType ;
} ;
struct CDynamicTypeCacheMissData {
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mType ;
void * mTypeInfo ;
uint8_t mTypeCheckKind ;
} ;
struct CFunctionTypeMismatchData {
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mType ;
} ;
struct CInvalidBuiltinData {
struct CSourceLocation mLocation ;
uint8_t mKind ;
} ;
struct CInvalidValueData {
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mType ;
} ;
struct CNonNullArgData {
struct CSourceLocation mLocation ;
struct CSourceLocation mAttributeLocation ;
int mArgIndex ;
} ;
struct CNonNullReturnData {
struct CSourceLocation mAttributeLocation ;
} ;
struct COutOfBoundsData {
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mArrayType ;
struct CTypeDescriptor * mIndexType ;
} ;
struct CPointerOverflowData {
struct CSourceLocation mLocation ;
} ;
struct CShiftOutOfBoundsData {
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mLHSType ;
struct CTypeDescriptor * mRHSType ;
} ;
struct CTypeMismatchData {
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mType ;
unsigned long mLogAlignment ;
uint8_t mTypeCheckKind ;
} ;
struct CTypeMismatchData_v1 {
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mType ;
uint8_t mLogAlignment ;
uint8_t mTypeCheckKind ;
} ;
struct CVLABoundData {
struct CSourceLocation mLocation ;
struct CTypeDescriptor * mType ;
} ;
struct CFloatCastOverflowData {
struct CSourceLocation mLocation ; /* This field exists in this struct since 2015 August 11th */
struct CTypeDescriptor * mFromType ;
struct CTypeDescriptor * mToType ;
} ;
/* Local utility functions */
static void Report ( bool isFatal , const char * pFormat , . . . ) __printflike ( 2 , 3 ) ;
static bool isAlreadyReported ( struct CSourceLocation * pLocation ) ;
static size_t zDeserializeTypeWidth ( struct CTypeDescriptor * pType ) ;
static void DeserializeLocation ( char * pBuffer , size_t zBUfferLength , struct CSourceLocation * pLocation ) ;
# ifdef __SIZEOF_INT128__
static void DeserializeUINT128 ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , __uint128_t U128 ) ;
# endif
static void DeserializeNumberSigned ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , longest L ) ;
static void DeserializeNumberUnsigned ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , ulongest L ) ;
# ifndef _KERNEL
static void DeserializeFloatOverPointer ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , unsigned long * pNumber ) ;
static void DeserializeFloatInlined ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , unsigned long ulNumber ) ;
# endif
static longest llliGetNumber ( char * szLocation , struct CTypeDescriptor * pType , unsigned long ulNumber ) ;
static ulongest llluGetNumber ( char * szLocation , struct CTypeDescriptor * pType , unsigned long ulNumber ) ;
# ifndef _KERNEL
static void DeserializeNumberFloat ( char * szLocation , char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , unsigned long ulNumber ) ;
# endif
static void DeserializeNumber ( char * szLocation , char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , unsigned long ulNumber ) ;
static const char * DeserializeTypeCheckKind ( uint8_t hhuTypeCheckKind ) ;
static const char * DeserializeBuiltinCheckKind ( uint8_t hhuBuiltinCheckKind ) ;
static const char * DeserializeCFICheckKind ( uint8_t hhuCFICheckKind ) ;
static bool isNegativeNumber ( char * szLocation , struct CTypeDescriptor * pType , unsigned long ulNumber ) ;
static bool isShiftExponentTooLarge ( char * szLocation , struct CTypeDescriptor * pType , unsigned long ulNumber , size_t zWidth ) ;
/* Unused in this implementation, emitted by the C++ check dynamic type cast. */
intptr_t __ubsan_vptr_type_cache [ 128 ] ;
/* Public symbols used in the instrumentation of the code generation part */
void __ubsan_handle_add_overflow ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_add_overflow_abort ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_builtin_unreachable ( struct CUnreachableData * pData ) ;
void __ubsan_handle_cfi_bad_type ( struct CCFICheckFailData * pData , unsigned long ulVtable , bool bValidVtable , bool FromUnrecoverableHandler , unsigned long ProgramCounter , unsigned long FramePointer ) ;
void __ubsan_handle_cfi_check_fail ( struct CCFICheckFailData * pData , unsigned long ulValue , unsigned long ulValidVtable ) ;
void __ubsan_handle_cfi_check_fail_abort ( struct CCFICheckFailData * pData , unsigned long ulValue , unsigned long ulValidVtable ) ;
void __ubsan_handle_divrem_overflow ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_divrem_overflow_abort ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_dynamic_type_cache_miss ( struct CDynamicTypeCacheMissData * pData , unsigned long ulPointer , unsigned long ulHash ) ;
void __ubsan_handle_dynamic_type_cache_miss_abort ( struct CDynamicTypeCacheMissData * pData , unsigned long ulPointer , unsigned long ulHash ) ;
void __ubsan_handle_float_cast_overflow ( struct CFloatCastOverflowData * pData , unsigned long ulFrom ) ;
void __ubsan_handle_float_cast_overflow_abort ( struct CFloatCastOverflowData * pData , unsigned long ulFrom ) ;
void __ubsan_handle_function_type_mismatch ( struct CFunctionTypeMismatchData * pData , unsigned long ulFunction ) ;
void __ubsan_handle_function_type_mismatch_abort ( struct CFunctionTypeMismatchData * pData , unsigned long ulFunction ) ;
void __ubsan_handle_invalid_builtin ( struct CInvalidBuiltinData * pData ) ;
void __ubsan_handle_invalid_builtin_abort ( struct CInvalidBuiltinData * pData ) ;
void __ubsan_handle_load_invalid_value ( struct CInvalidValueData * pData , unsigned long ulVal ) ;
void __ubsan_handle_load_invalid_value_abort ( struct CInvalidValueData * pData , unsigned long ulVal ) ;
void __ubsan_handle_missing_return ( struct CUnreachableData * pData ) ;
void __ubsan_handle_mul_overflow ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_mul_overflow_abort ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_negate_overflow ( struct COverflowData * pData , unsigned long ulOldVal ) ;
void __ubsan_handle_negate_overflow_abort ( struct COverflowData * pData , unsigned long ulOldVal ) ;
void __ubsan_handle_nonnull_arg ( struct CNonNullArgData * pData ) ;
void __ubsan_handle_nonnull_arg_abort ( struct CNonNullArgData * pData ) ;
void __ubsan_handle_nonnull_return_v1 ( struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer ) ;
void __ubsan_handle_nonnull_return_v1_abort ( struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer ) ;
void __ubsan_handle_nullability_arg ( struct CNonNullArgData * pData ) ;
void __ubsan_handle_nullability_arg_abort ( struct CNonNullArgData * pData ) ;
void __ubsan_handle_nullability_return_v1 ( struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer ) ;
void __ubsan_handle_nullability_return_v1_abort ( struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer ) ;
void __ubsan_handle_out_of_bounds ( struct COutOfBoundsData * pData , unsigned long ulIndex ) ;
void __ubsan_handle_out_of_bounds_abort ( struct COutOfBoundsData * pData , unsigned long ulIndex ) ;
void __ubsan_handle_pointer_overflow ( struct CPointerOverflowData * pData , unsigned long ulBase , unsigned long ulResult ) ;
void __ubsan_handle_pointer_overflow_abort ( struct CPointerOverflowData * pData , unsigned long ulBase , unsigned long ulResult ) ;
void __ubsan_handle_shift_out_of_bounds ( struct CShiftOutOfBoundsData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_shift_out_of_bounds_abort ( struct CShiftOutOfBoundsData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_sub_overflow ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_sub_overflow_abort ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
void __ubsan_handle_type_mismatch ( struct CTypeMismatchData * pData , unsigned long ulPointer ) ;
void __ubsan_handle_type_mismatch_abort ( struct CTypeMismatchData * pData , unsigned long ulPointer ) ;
void __ubsan_handle_type_mismatch_v1 ( struct CTypeMismatchData_v1 * pData , unsigned long ulPointer ) ;
void __ubsan_handle_type_mismatch_v1_abort ( struct CTypeMismatchData_v1 * pData , unsigned long ulPointer ) ;
void __ubsan_handle_vla_bound_not_positive ( struct CVLABoundData * pData , unsigned long ulBound ) ;
void __ubsan_handle_vla_bound_not_positive_abort ( struct CVLABoundData * pData , unsigned long ulBound ) ;
void __ubsan_get_current_report_data ( const char * * ppOutIssueKind , const char * * ppOutMessage , const char * * ppOutFilename , uint32_t * pOutLine , uint32_t * pOutCol , char * * ppOutMemoryAddr ) ;
static void HandleOverflow ( bool isFatal , struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS , const char * szOperation ) ;
static void HandleNegateOverflow ( bool isFatal , struct COverflowData * pData , unsigned long ulOldValue ) ;
static void HandleBuiltinUnreachable ( bool isFatal , struct CUnreachableData * pData ) ;
static void HandleTypeMismatch ( bool isFatal , struct CSourceLocation * mLocation , struct CTypeDescriptor * mType , unsigned long mLogAlignment , uint8_t mTypeCheckKind , unsigned long ulPointer ) ;
static void HandleVlaBoundNotPositive ( bool isFatal , struct CVLABoundData * pData , unsigned long ulBound ) ;
static void HandleOutOfBounds ( bool isFatal , struct COutOfBoundsData * pData , unsigned long ulIndex ) ;
static void HandleShiftOutOfBounds ( bool isFatal , struct CShiftOutOfBoundsData * pData , unsigned long ulLHS , unsigned long ulRHS ) ;
static void HandleLoadInvalidValue ( bool isFatal , struct CInvalidValueData * pData , unsigned long ulValue ) ;
static void HandleInvalidBuiltin ( bool isFatal , struct CInvalidBuiltinData * pData ) ;
static void HandleFunctionTypeMismatch ( bool isFatal , struct CFunctionTypeMismatchData * pData , unsigned long ulFunction ) ;
static void HandleCFIBadType ( bool isFatal , struct CCFICheckFailData * pData , unsigned long ulVtable , bool * bValidVtable , bool * FromUnrecoverableHandler , unsigned long * ProgramCounter , unsigned long * FramePointer ) ;
static void HandleDynamicTypeCacheMiss ( bool isFatal , struct CDynamicTypeCacheMissData * pData , unsigned long ulPointer , unsigned long ulHash ) ;
static void HandleFloatCastOverflow ( bool isFatal , struct CFloatCastOverflowData * pData , unsigned long ulFrom ) ;
static void HandleMissingReturn ( bool isFatal , struct CUnreachableData * pData ) ;
static void HandleNonnullArg ( bool isFatal , struct CNonNullArgData * pData ) ;
static void HandleNonnullReturn ( bool isFatal , struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer ) ;
static void HandlePointerOverflow ( bool isFatal , struct CPointerOverflowData * pData , unsigned long ulBase , unsigned long ulResult ) ;
static void
HandleOverflow ( bool isFatal , struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS , const char * szOperation )
{
char szLocation [ LOCATION_MAXLEN ] ;
char szLHS [ NUMBER_MAXLEN ] ;
char szRHS [ NUMBER_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
DeserializeNumber ( szLocation , szLHS , NUMBER_MAXLEN , pData - > mType , ulLHS ) ;
DeserializeNumber ( szLocation , szRHS , NUMBER_MAXLEN , pData - > mType , ulRHS ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, %s integer overflow: %s %s %s cannot be represented in type %s \n " ,
szLocation , ISSET ( pData - > mType - > mTypeInfo , NUMBER_SIGNED_BIT ) ? " signed " : " unsigned " , szLHS , szOperation , szRHS , pData - > mType - > mTypeName ) ;
}
static void
HandleNegateOverflow ( bool isFatal , struct COverflowData * pData , unsigned long ulOldValue )
{
char szLocation [ LOCATION_MAXLEN ] ;
char szOldValue [ NUMBER_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
DeserializeNumber ( szLocation , szOldValue , NUMBER_MAXLEN , pData - > mType , ulOldValue ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, negation of %s cannot be represented in type %s \n " ,
szLocation , szOldValue , pData - > mType - > mTypeName ) ;
}
static void
HandleBuiltinUnreachable ( bool isFatal , struct CUnreachableData * pData )
{
char szLocation [ LOCATION_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, calling __builtin_unreachable() \n " ,
szLocation ) ;
}
static void
HandleTypeMismatch ( bool isFatal , struct CSourceLocation * mLocation , struct CTypeDescriptor * mType , unsigned long mLogAlignment , uint8_t mTypeCheckKind , unsigned long ulPointer )
{
char szLocation [ LOCATION_MAXLEN ] ;
ASSERT ( mLocation ) ;
ASSERT ( mType ) ;
if ( isAlreadyReported ( mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , mLocation ) ;
if ( ulPointer = = 0 ) {
Report ( isFatal , " UBSan: Undefined Behavior in %s, %s null pointer of type %s \n " ,
szLocation , DeserializeTypeCheckKind ( mTypeCheckKind ) , mType - > mTypeName ) ;
} else if ( ( mLogAlignment - 1 ) & ulPointer ) {
Report ( isFatal , " UBSan: Undefined Behavior in %s, %s misaligned address %p for type %s which requires %ld byte alignment \n " ,
szLocation , DeserializeTypeCheckKind ( mTypeCheckKind ) , REINTERPRET_CAST ( void * , ulPointer ) , mType - > mTypeName , mLogAlignment ) ;
} else {
Report ( isFatal , " UBSan: Undefined Behavior in %s, %s address %p with insufficient space for an object of type %s \n " ,
szLocation , DeserializeTypeCheckKind ( mTypeCheckKind ) , REINTERPRET_CAST ( void * , ulPointer ) , mType - > mTypeName ) ;
}
}
static void
HandleVlaBoundNotPositive ( bool isFatal , struct CVLABoundData * pData , unsigned long ulBound )
{
char szLocation [ LOCATION_MAXLEN ] ;
char szBound [ NUMBER_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
DeserializeNumber ( szLocation , szBound , NUMBER_MAXLEN , pData - > mType , ulBound ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, variable length array bound value %s <= 0 \n " ,
szLocation , szBound ) ;
}
static void
HandleOutOfBounds ( bool isFatal , struct COutOfBoundsData * pData , unsigned long ulIndex )
{
char szLocation [ LOCATION_MAXLEN ] ;
char szIndex [ NUMBER_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
DeserializeNumber ( szLocation , szIndex , NUMBER_MAXLEN , pData - > mIndexType , ulIndex ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, index %s is out of range for type %s \n " ,
szLocation , szIndex , pData - > mArrayType - > mTypeName ) ;
}
static void
HandleShiftOutOfBounds ( bool isFatal , struct CShiftOutOfBoundsData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
char szLocation [ LOCATION_MAXLEN ] ;
char szLHS [ NUMBER_MAXLEN ] ;
char szRHS [ NUMBER_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
DeserializeNumber ( szLocation , szLHS , NUMBER_MAXLEN , pData - > mLHSType , ulLHS ) ;
DeserializeNumber ( szLocation , szRHS , NUMBER_MAXLEN , pData - > mRHSType , ulRHS ) ;
if ( isNegativeNumber ( szLocation , pData - > mRHSType , ulRHS ) )
Report ( isFatal , " UBSan: Undefined Behavior in %s, shift exponent %s is negative \n " ,
szLocation , szRHS ) ;
else if ( isShiftExponentTooLarge ( szLocation , pData - > mRHSType , ulRHS , zDeserializeTypeWidth ( pData - > mLHSType ) ) )
Report ( isFatal , " UBSan: Undefined Behavior in %s, shift exponent %s is too large for %zu-bit type %s \n " ,
szLocation , szRHS , zDeserializeTypeWidth ( pData - > mLHSType ) , pData - > mLHSType - > mTypeName ) ;
else if ( isNegativeNumber ( szLocation , pData - > mLHSType , ulLHS ) )
Report ( isFatal , " UBSan: Undefined Behavior in %s, left shift of negative value %s \n " ,
szLocation , szLHS ) ;
else
Report ( isFatal , " UBSan: Undefined Behavior in %s, left shift of %s by %s places cannot be represented in type %s \n " ,
szLocation , szLHS , szRHS , pData - > mLHSType - > mTypeName ) ;
}
static void
HandleLoadInvalidValue ( bool isFatal , struct CInvalidValueData * pData , unsigned long ulValue )
{
char szLocation [ LOCATION_MAXLEN ] ;
char szValue [ NUMBER_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
DeserializeNumber ( szLocation , szValue , NUMBER_MAXLEN , pData - > mType , ulValue ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, load of value %s is not a valid value for type %s \n " ,
szLocation , szValue , pData - > mType - > mTypeName ) ;
}
static void
HandleInvalidBuiltin ( bool isFatal , struct CInvalidBuiltinData * pData )
{
char szLocation [ LOCATION_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, passing zero to %s, which is not a valid argument \n " ,
szLocation , DeserializeBuiltinCheckKind ( pData - > mKind ) ) ;
}
static void
HandleFunctionTypeMismatch ( bool isFatal , struct CFunctionTypeMismatchData * pData , unsigned long ulFunction )
{
char szLocation [ LOCATION_MAXLEN ] ;
/*
* There is no a portable C solution to translate an address of a
* function to its name . On the cost of getting this routine simple
* and portable without ifdefs between the userland and the kernel
* just print the address of the function as - is .
*
* For better diagnostic messages in the userland , users shall use
* the full upstream version shipped along with the compiler toolchain .
*/
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, call to function %#lx through pointer to incorrect function type %s \n " ,
szLocation , ulFunction , pData - > mType - > mTypeName ) ;
}
static void
HandleCFIBadType ( bool isFatal , struct CCFICheckFailData * pData , unsigned long ulVtable , bool * bValidVtable , bool * FromUnrecoverableHandler , unsigned long * ProgramCounter , unsigned long * FramePointer )
{
char szLocation [ LOCATION_MAXLEN ] ;
/*
* This is a minimal implementation without diving into C + +
* specifics and ( Itanium ) ABI deserialization .
*/
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
if ( pData - > mCheckKind = = CFI_ICALL | | pData - > mCheckKind = = CFI_VMFCALL ) {
Report ( isFatal , " UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx) \n " ,
szLocation , pData - > mType - > mTypeName , DeserializeCFICheckKind ( pData - > mCheckKind ) , ulVtable ) ;
} else {
Report ( isFatal | | FromUnrecoverableHandler , " UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx; %s vtable; from %s handler; Program Counter %#lx; Frame Pointer %#lx) \n " ,
szLocation , pData - > mType - > mTypeName , DeserializeCFICheckKind ( pData - > mCheckKind ) , ulVtable , * bValidVtable ? " valid " : " invalid " , * FromUnrecoverableHandler ? " unrecoverable " : " recoverable " , * ProgramCounter , * FramePointer ) ;
}
}
static void
HandleDynamicTypeCacheMiss ( bool isFatal , struct CDynamicTypeCacheMissData * pData , unsigned long ulPointer , unsigned long ulHash )
{
#if 0
char szLocation [ LOCATION_MAXLEN ] ;
/*
* Unimplemented .
*
* This UBSan handler is special as the check has to be impelemented
* in an implementation . In order to handle it there is need to
* introspect into C + + ABI internals ( RTTI ) and use low - level
* C + + runtime interfaces .
*/
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, %s address %#lx which might not point to an object of type %s \n "
szLocation , DeserializeTypeCheckKind ( pData - > mTypeCheckKind ) , ulPointer , pData - > mType ) ;
# endif
}
static void
HandleFloatCastOverflow ( bool isFatal , struct CFloatCastOverflowData * pData , unsigned long ulFrom )
{
char szLocation [ LOCATION_MAXLEN ] ;
char szFrom [ NUMBER_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
DeserializeNumber ( szLocation , szFrom , NUMBER_MAXLEN , pData - > mFromType , ulFrom ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, %s (of type %s) is outside the range of representable values of type %s \n " ,
szLocation , szFrom , pData - > mFromType - > mTypeName , pData - > mToType - > mTypeName ) ;
}
static void
HandleMissingReturn ( bool isFatal , struct CUnreachableData * pData )
{
char szLocation [ LOCATION_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, execution reached the end of a value-returning function without returning a value \n " ,
szLocation ) ;
}
static void
HandleNonnullArg ( bool isFatal , struct CNonNullArgData * pData )
{
char szLocation [ LOCATION_MAXLEN ] ;
char szAttributeLocation [ LOCATION_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
if ( pData - > mAttributeLocation . mFilename )
DeserializeLocation ( szAttributeLocation , LOCATION_MAXLEN , & pData - > mAttributeLocation ) ;
else
szAttributeLocation [ 0 ] = ' \0 ' ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, null pointer passed as argument %d, which is declared to never be null%s%s \n " ,
szLocation , pData - > mArgIndex , pData - > mAttributeLocation . mFilename ? " , nonnull/_Nonnull specified in " : " " , szAttributeLocation ) ;
}
static void
HandleNonnullReturn ( bool isFatal , struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer )
{
char szLocation [ LOCATION_MAXLEN ] ;
char szAttributeLocation [ LOCATION_MAXLEN ] ;
ASSERT ( pData ) ;
ASSERT ( pLocationPointer ) ;
if ( isAlreadyReported ( pLocationPointer ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , pLocationPointer ) ;
if ( pData - > mAttributeLocation . mFilename )
DeserializeLocation ( szAttributeLocation , LOCATION_MAXLEN , & pData - > mAttributeLocation ) ;
else
szAttributeLocation [ 0 ] = ' \0 ' ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, null pointer returned from function declared to never return null%s%s \n " ,
szLocation , pData - > mAttributeLocation . mFilename ? " , nonnull/_Nonnull specified in " : " " , szAttributeLocation ) ;
}
static void
HandlePointerOverflow ( bool isFatal , struct CPointerOverflowData * pData , unsigned long ulBase , unsigned long ulResult )
{
char szLocation [ LOCATION_MAXLEN ] ;
ASSERT ( pData ) ;
if ( isAlreadyReported ( & pData - > mLocation ) )
return ;
DeserializeLocation ( szLocation , LOCATION_MAXLEN , & pData - > mLocation ) ;
Report ( isFatal , " UBSan: Undefined Behavior in %s, pointer expression with base %#lx overflowed to %#lx \n " ,
szLocation , ulBase , ulResult ) ;
}
/* Definions of public symbols emitted by the instrumentation code */
void
__ubsan_handle_add_overflow ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleOverflow ( false , pData , ulLHS , ulRHS , PLUS_STRING ) ;
}
void
__ubsan_handle_add_overflow_abort ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleOverflow ( true , pData , ulLHS , ulRHS , PLUS_STRING ) ;
}
void
__ubsan_handle_builtin_unreachable ( struct CUnreachableData * pData )
{
ASSERT ( pData ) ;
HandleBuiltinUnreachable ( true , pData ) ;
}
void
__ubsan_handle_cfi_bad_type ( struct CCFICheckFailData * pData , unsigned long ulVtable , bool bValidVtable , bool FromUnrecoverableHandler , unsigned long ProgramCounter , unsigned long FramePointer )
{
ASSERT ( pData ) ;
HandleCFIBadType ( false , pData , ulVtable , & bValidVtable , & FromUnrecoverableHandler , & ProgramCounter , & FramePointer ) ;
}
void
__ubsan_handle_cfi_check_fail ( struct CCFICheckFailData * pData , unsigned long ulValue , unsigned long ulValidVtable )
{
ASSERT ( pData ) ;
HandleCFIBadType ( false , pData , ulValue , 0 , 0 , 0 , 0 ) ;
}
void
__ubsan_handle_cfi_check_fail_abort ( struct CCFICheckFailData * pData , unsigned long ulValue , unsigned long ulValidVtable )
{
ASSERT ( pData ) ;
HandleCFIBadType ( true , pData , ulValue , 0 , 0 , 0 , 0 ) ;
}
void
__ubsan_handle_divrem_overflow ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleOverflow ( false , pData , ulLHS , ulRHS , DIVREM_STRING ) ;
}
void
__ubsan_handle_divrem_overflow_abort ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleOverflow ( true , pData , ulLHS , ulRHS , DIVREM_STRING ) ;
}
void
__ubsan_handle_dynamic_type_cache_miss ( struct CDynamicTypeCacheMissData * pData , unsigned long ulPointer , unsigned long ulHash )
{
ASSERT ( pData ) ;
HandleDynamicTypeCacheMiss ( false , pData , ulPointer , ulHash ) ;
}
void
__ubsan_handle_dynamic_type_cache_miss_abort ( struct CDynamicTypeCacheMissData * pData , unsigned long ulPointer , unsigned long ulHash )
{
ASSERT ( pData ) ;
HandleDynamicTypeCacheMiss ( false , pData , ulPointer , ulHash ) ;
}
void
__ubsan_handle_float_cast_overflow ( struct CFloatCastOverflowData * pData , unsigned long ulFrom )
{
ASSERT ( pData ) ;
HandleFloatCastOverflow ( false , pData , ulFrom ) ;
}
void
__ubsan_handle_float_cast_overflow_abort ( struct CFloatCastOverflowData * pData , unsigned long ulFrom )
{
ASSERT ( pData ) ;
HandleFloatCastOverflow ( true , pData , ulFrom ) ;
}
void
__ubsan_handle_function_type_mismatch ( struct CFunctionTypeMismatchData * pData , unsigned long ulFunction )
{
ASSERT ( pData ) ;
HandleFunctionTypeMismatch ( false , pData , ulFunction ) ;
}
void
__ubsan_handle_function_type_mismatch_abort ( struct CFunctionTypeMismatchData * pData , unsigned long ulFunction )
{
ASSERT ( pData ) ;
HandleFunctionTypeMismatch ( false , pData , ulFunction ) ;
}
void
__ubsan_handle_invalid_builtin ( struct CInvalidBuiltinData * pData )
{
ASSERT ( pData ) ;
HandleInvalidBuiltin ( true , pData ) ;
}
void
__ubsan_handle_invalid_builtin_abort ( struct CInvalidBuiltinData * pData )
{
ASSERT ( pData ) ;
HandleInvalidBuiltin ( true , pData ) ;
}
void
__ubsan_handle_load_invalid_value ( struct CInvalidValueData * pData , unsigned long ulValue )
{
ASSERT ( pData ) ;
HandleLoadInvalidValue ( false , pData , ulValue ) ;
}
void
__ubsan_handle_load_invalid_value_abort ( struct CInvalidValueData * pData , unsigned long ulValue )
{
ASSERT ( pData ) ;
HandleLoadInvalidValue ( true , pData , ulValue ) ;
}
void
__ubsan_handle_missing_return ( struct CUnreachableData * pData )
{
ASSERT ( pData ) ;
HandleMissingReturn ( true , pData ) ;
}
void
__ubsan_handle_mul_overflow ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleOverflow ( false , pData , ulLHS , ulRHS , MUL_STRING ) ;
}
void
__ubsan_handle_mul_overflow_abort ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleOverflow ( true , pData , ulLHS , ulRHS , MUL_STRING ) ;
}
void
__ubsan_handle_negate_overflow ( struct COverflowData * pData , unsigned long ulOldValue )
{
ASSERT ( pData ) ;
HandleNegateOverflow ( false , pData , ulOldValue ) ;
}
void
__ubsan_handle_negate_overflow_abort ( struct COverflowData * pData , unsigned long ulOldValue )
{
ASSERT ( pData ) ;
HandleNegateOverflow ( true , pData , ulOldValue ) ;
}
void
__ubsan_handle_nonnull_arg ( struct CNonNullArgData * pData )
{
ASSERT ( pData ) ;
HandleNonnullArg ( false , pData ) ;
}
void
__ubsan_handle_nonnull_arg_abort ( struct CNonNullArgData * pData )
{
ASSERT ( pData ) ;
HandleNonnullArg ( true , pData ) ;
}
void
__ubsan_handle_nonnull_return_v1 ( struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer )
{
ASSERT ( pData ) ;
ASSERT ( pLocationPointer ) ;
HandleNonnullReturn ( false , pData , pLocationPointer ) ;
}
void
__ubsan_handle_nonnull_return_v1_abort ( struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer )
{
ASSERT ( pData ) ;
ASSERT ( pLocationPointer ) ;
HandleNonnullReturn ( true , pData , pLocationPointer ) ;
}
void
__ubsan_handle_nullability_arg ( struct CNonNullArgData * pData )
{
ASSERT ( pData ) ;
HandleNonnullArg ( false , pData ) ;
}
void
__ubsan_handle_nullability_arg_abort ( struct CNonNullArgData * pData )
{
ASSERT ( pData ) ;
HandleNonnullArg ( true , pData ) ;
}
void
__ubsan_handle_nullability_return_v1 ( struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer )
{
ASSERT ( pData ) ;
ASSERT ( pLocationPointer ) ;
HandleNonnullReturn ( false , pData , pLocationPointer ) ;
}
void
__ubsan_handle_nullability_return_v1_abort ( struct CNonNullReturnData * pData , struct CSourceLocation * pLocationPointer )
{
ASSERT ( pData ) ;
ASSERT ( pLocationPointer ) ;
HandleNonnullReturn ( true , pData , pLocationPointer ) ;
}
void
__ubsan_handle_out_of_bounds ( struct COutOfBoundsData * pData , unsigned long ulIndex )
{
ASSERT ( pData ) ;
HandleOutOfBounds ( false , pData , ulIndex ) ;
}
void
__ubsan_handle_out_of_bounds_abort ( struct COutOfBoundsData * pData , unsigned long ulIndex )
{
ASSERT ( pData ) ;
HandleOutOfBounds ( true , pData , ulIndex ) ;
}
void
__ubsan_handle_pointer_overflow ( struct CPointerOverflowData * pData , unsigned long ulBase , unsigned long ulResult )
{
ASSERT ( pData ) ;
HandlePointerOverflow ( false , pData , ulBase , ulResult ) ;
}
void
__ubsan_handle_pointer_overflow_abort ( struct CPointerOverflowData * pData , unsigned long ulBase , unsigned long ulResult )
{
ASSERT ( pData ) ;
HandlePointerOverflow ( true , pData , ulBase , ulResult ) ;
}
void
__ubsan_handle_shift_out_of_bounds ( struct CShiftOutOfBoundsData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleShiftOutOfBounds ( false , pData , ulLHS , ulRHS ) ;
}
void
__ubsan_handle_shift_out_of_bounds_abort ( struct CShiftOutOfBoundsData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleShiftOutOfBounds ( true , pData , ulLHS , ulRHS ) ;
}
void
__ubsan_handle_sub_overflow ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleOverflow ( false , pData , ulLHS , ulRHS , MINUS_STRING ) ;
}
void
__ubsan_handle_sub_overflow_abort ( struct COverflowData * pData , unsigned long ulLHS , unsigned long ulRHS )
{
ASSERT ( pData ) ;
HandleOverflow ( true , pData , ulLHS , ulRHS , MINUS_STRING ) ;
}
void
__ubsan_handle_type_mismatch ( struct CTypeMismatchData * pData , unsigned long ulPointer )
{
ASSERT ( pData ) ;
HandleTypeMismatch ( false , & pData - > mLocation , pData - > mType , pData - > mLogAlignment , pData - > mTypeCheckKind , ulPointer ) ;
}
void
__ubsan_handle_type_mismatch_abort ( struct CTypeMismatchData * pData , unsigned long ulPointer )
{
ASSERT ( pData ) ;
HandleTypeMismatch ( true , & pData - > mLocation , pData - > mType , pData - > mLogAlignment , pData - > mTypeCheckKind , ulPointer ) ;
}
void
__ubsan_handle_type_mismatch_v1 ( struct CTypeMismatchData_v1 * pData , unsigned long ulPointer )
{
ASSERT ( pData ) ;
HandleTypeMismatch ( false , & pData - > mLocation , pData - > mType , __BIT ( pData - > mLogAlignment ) , pData - > mTypeCheckKind , ulPointer ) ;
}
void
__ubsan_handle_type_mismatch_v1_abort ( struct CTypeMismatchData_v1 * pData , unsigned long ulPointer )
{
ASSERT ( pData ) ;
HandleTypeMismatch ( true , & pData - > mLocation , pData - > mType , __BIT ( pData - > mLogAlignment ) , pData - > mTypeCheckKind , ulPointer ) ;
}
void
__ubsan_handle_vla_bound_not_positive ( struct CVLABoundData * pData , unsigned long ulBound )
{
ASSERT ( pData ) ;
HandleVlaBoundNotPositive ( false , pData , ulBound ) ;
}
void
__ubsan_handle_vla_bound_not_positive_abort ( struct CVLABoundData * pData , unsigned long ulBound )
{
ASSERT ( pData ) ;
HandleVlaBoundNotPositive ( true , pData , ulBound ) ;
}
void
__ubsan_get_current_report_data ( const char * * ppOutIssueKind , const char * * ppOutMessage , const char * * ppOutFilename , uint32_t * pOutLine , uint32_t * pOutCol , char * * ppOutMemoryAddr )
{
/*
* Unimplemented .
*
* The __ubsan_on_report ( ) feature is non trivial to implement in a
* shared code between the kernel and userland . It ' s also opening
* new sets of potential problems as we are not expected to slow down
* execution of certain kernel subsystems ( synchronization issues ,
* interrupt handling etc ) .
*
* A proper solution would need probably a lock - free bounded queue built
* with atomic operations with the property of miltiple consumers and
* multiple producers . Maintaining and validating such code is not
* worth the effort .
*
* A legitimate user - besides testing framework - is a debugger plugin
* intercepting reports from the UBSan instrumentation . For such
* scenarios it is better to run the Clang / GCC version .
*/
}
/* Local utility functions */
static void
Report ( bool isFatal , const char * pFormat , . . . )
{
va_list ap ;
ASSERT ( pFormat ) ;
va_start ( ap , pFormat ) ;
# if defined(_KERNEL)
if ( isFatal )
vpanic ( pFormat , ap ) ;
else
vprintf ( pFormat , ap ) ;
# else
if ( ubsan_flags = = - 1 ) {
char buf [ 1024 ] ;
char * p ;
ubsan_flags = UBSAN_STDERR ;
if ( getenv_r ( " LIBC_UBSAN " , buf , sizeof ( buf ) ) ! = - 1 ) {
for ( p = buf ; * p ; p + + ) {
switch ( * p ) {
case ' a ' :
SET ( ubsan_flags , UBSAN_ABORT ) ;
break ;
case ' A ' :
CLR ( ubsan_flags , UBSAN_ABORT ) ;
break ;
case ' e ' :
SET ( ubsan_flags , UBSAN_STDERR ) ;
break ;
case ' E ' :
CLR ( ubsan_flags , UBSAN_STDERR ) ;
break ;
case ' l ' :
SET ( ubsan_flags , UBSAN_SYSLOG ) ;
break ;
case ' L ' :
CLR ( ubsan_flags , UBSAN_SYSLOG ) ;
break ;
case ' o ' :
SET ( ubsan_flags , UBSAN_STDOUT ) ;
break ;
case ' O ' :
CLR ( ubsan_flags , UBSAN_STDOUT ) ;
break ;
default :
break ;
}
}
}
}
// The *v*print* functions can flush the va_list argument.
// Create a local copy for each call to prevent invalid read.
if ( ISSET ( ubsan_flags , UBSAN_STDOUT ) ) {
va_list tmp ;
va_copy ( tmp , ap ) ;
vprintf ( pFormat , tmp ) ;
va_end ( tmp ) ;
fflush ( stdout ) ;
}
if ( ISSET ( ubsan_flags , UBSAN_STDERR ) ) {
va_list tmp ;
va_copy ( tmp , ap ) ;
vfprintf ( stderr , pFormat , tmp ) ;
va_end ( tmp ) ;
fflush ( stderr ) ;
}
if ( ISSET ( ubsan_flags , UBSAN_SYSLOG ) ) {
va_list tmp ;
va_copy ( tmp , ap ) ;
struct syslog_data SyslogData = SYSLOG_DATA_INIT ;
ubsan_vsyslog ( LOG_DEBUG | LOG_USER , & SyslogData , pFormat , tmp ) ;
va_end ( tmp ) ;
}
if ( isFatal | | ISSET ( ubsan_flags , UBSAN_ABORT ) ) {
abort ( ) ;
/* NOTREACHED */
}
# endif
va_end ( ap ) ;
}
static bool
isAlreadyReported ( struct CSourceLocation * pLocation )
{
/*
* This code is shared between libc , kernel and standalone usage .
* It shall work in early bootstrap phase of both of them .
*/
uint32_t siOldValue ;
volatile uint32_t * pLine ;
ASSERT ( pLocation ) ;
pLine = & pLocation - > mLine ;
do {
siOldValue = * pLine ;
} while ( __sync_val_compare_and_swap ( pLine , siOldValue , siOldValue | ACK_REPORTED ) ! = siOldValue ) ;
return ISSET ( siOldValue , ACK_REPORTED ) ;
}
static size_t
zDeserializeTypeWidth ( struct CTypeDescriptor * pType )
{
size_t zWidth = 0 ;
ASSERT ( pType ) ;
switch ( pType - > mTypeKind ) {
case KIND_INTEGER :
zWidth = __BIT ( __SHIFTOUT ( pType - > mTypeInfo , ~ NUMBER_SIGNED_BIT ) ) ;
break ;
case KIND_FLOAT :
zWidth = pType - > mTypeInfo ;
break ;
default :
Report ( true , " UBSan: Unknown variable type %#04 " PRIx16 " \n " , pType - > mTypeKind ) ;
/* NOTREACHED */
}
/* Invalid width will be transformed to 0 */
ASSERT ( zWidth > 0 ) ;
return zWidth ;
}
static void
DeserializeLocation ( char * pBuffer , size_t zBUfferLength , struct CSourceLocation * pLocation )
{
ASSERT ( pLocation ) ;
ASSERT ( pLocation - > mFilename ) ;
snprintf ( pBuffer , zBUfferLength , " %s:% " PRIu32 " :% " PRIu32 , pLocation - > mFilename , pLocation - > mLine & ( uint32_t ) ~ ACK_REPORTED , pLocation - > mColumn ) ;
}
# ifdef __SIZEOF_INT128__
static void
DeserializeUINT128 ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , __uint128_t U128 )
{
char szBuf [ 3 ] ; /* 'XX\0' */
char rgNumber [ sizeof ( ulongest ) ] ;
ssize_t zI ;
memcpy ( rgNumber , & U128 , sizeof ( U128 ) ) ;
strlcpy ( pBuffer , " Undecoded-128-bit-Integer-Type (0x " , zBUfferLength ) ;
# if BYTE_ORDER == LITTLE_ENDIAN
for ( zI = sizeof ( ulongest ) - 1 ; zI > = 0 ; zI - - ) {
# else
for ( zI = 0 ; zI < ( ssize_t ) sizeof ( ulongest ) ; zI + + ) {
# endif
snprintf ( szBuf , sizeof ( szBuf ) , " %02 " PRIx8 , rgNumber [ zI ] ) ;
strlcat ( pBuffer , szBuf , zBUfferLength ) ;
}
strlcat ( pBuffer , " ) " , zBUfferLength ) ;
}
# endif
static void
DeserializeNumberSigned ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , longest L )
{
ASSERT ( pBuffer ) ;
ASSERT ( zBUfferLength > 0 ) ;
ASSERT ( pType ) ;
ASSERT ( ISSET ( pType - > mTypeInfo , NUMBER_SIGNED_BIT ) ) ;
switch ( zDeserializeTypeWidth ( pType ) ) {
default :
ASSERT ( 0 & & " Invalid codepath " ) ;
/* NOTREACHED */
# ifdef __SIZEOF_INT128__
case WIDTH_128 :
DeserializeUINT128 ( pBuffer , zBUfferLength , pType , STATIC_CAST ( __uint128_t , L ) ) ;
break ;
# endif
case WIDTH_64 :
case WIDTH_32 :
case WIDTH_16 :
case WIDTH_8 :
snprintf ( pBuffer , zBUfferLength , " % " PRId64 , STATIC_CAST ( int64_t , L ) ) ;
break ;
}
}
static void
DeserializeNumberUnsigned ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , ulongest L )
{
ASSERT ( pBuffer ) ;
ASSERT ( zBUfferLength > 0 ) ;
ASSERT ( pType ) ;
ASSERT ( ! ISSET ( pType - > mTypeInfo , NUMBER_SIGNED_BIT ) ) ;
switch ( zDeserializeTypeWidth ( pType ) ) {
default :
ASSERT ( 0 & & " Invalid codepath " ) ;
/* NOTREACHED */
# ifdef __SIZEOF_INT128__
case WIDTH_128 :
DeserializeUINT128 ( pBuffer , zBUfferLength , pType , STATIC_CAST ( __uint128_t , L ) ) ;
break ;
# endif
case WIDTH_64 :
case WIDTH_32 :
case WIDTH_16 :
case WIDTH_8 :
snprintf ( pBuffer , zBUfferLength , " % " PRIu64 , STATIC_CAST ( uint64_t , L ) ) ;
break ;
}
}
# ifndef _KERNEL
static void
DeserializeFloatOverPointer ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , unsigned long * pNumber )
{
double D ;
# ifdef __HAVE_LONG_DOUBLE
long double LD ;
# endif
ASSERT ( pBuffer ) ;
ASSERT ( zBUfferLength > 0 ) ;
ASSERT ( pType ) ;
ASSERT ( pNumber ) ;
/*
* This function handles 64 - bit number over a pointer on 32 - bit CPUs .
*/
ASSERT ( ( sizeof ( * pNumber ) * CHAR_BIT < WIDTH_64 ) | | ( zDeserializeTypeWidth ( pType ) > = WIDTH_64 ) ) ;
ASSERT ( sizeof ( D ) = = sizeof ( uint64_t ) ) ;
# ifdef __HAVE_LONG_DOUBLE
ASSERT ( sizeof ( LD ) > sizeof ( uint64_t ) ) ;
# endif
switch ( zDeserializeTypeWidth ( pType ) ) {
# ifdef __HAVE_LONG_DOUBLE
case WIDTH_128 :
case WIDTH_96 :
case WIDTH_80 :
memcpy ( & LD , pNumber , sizeof ( long double ) ) ;
snprintf ( pBuffer , zBUfferLength , " %Lg " , LD ) ;
break ;
# endif
case WIDTH_64 :
memcpy ( & D , pNumber , sizeof ( double ) ) ;
snprintf ( pBuffer , zBUfferLength , " %g " , D ) ;
break ;
}
}
static void
DeserializeFloatInlined ( char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , unsigned long ulNumber )
{
float F ;
double D ;
uint32_t U32 ;
ASSERT ( pBuffer ) ;
ASSERT ( zBUfferLength > 0 ) ;
ASSERT ( pType ) ;
ASSERT ( sizeof ( F ) = = sizeof ( uint32_t ) ) ;
ASSERT ( sizeof ( D ) = = sizeof ( uint64_t ) ) ;
switch ( zDeserializeTypeWidth ( pType ) ) {
case WIDTH_64 :
memcpy ( & D , & ulNumber , sizeof ( double ) ) ;
snprintf ( pBuffer , zBUfferLength , " %g " , D ) ;
break ;
case WIDTH_32 :
/*
* On supported platforms sizeof ( float ) = = sizeof ( uint32_t )
* unsigned long is either 32 or 64 - bit , cast it to 32 - bit
* value in order to call memcpy ( 3 ) in an Endian - aware way .
*/
U32 = STATIC_CAST ( uint32_t , ulNumber ) ;
memcpy ( & F , & U32 , sizeof ( float ) ) ;
snprintf ( pBuffer , zBUfferLength , " %g " , F ) ;
break ;
case WIDTH_16 :
snprintf ( pBuffer , zBUfferLength , " Undecoded-16-bit-Floating-Type (%#04 " PRIx16 " ) " , STATIC_CAST ( uint16_t , ulNumber ) ) ;
break ;
}
}
# endif
static longest
llliGetNumber ( char * szLocation , struct CTypeDescriptor * pType , unsigned long ulNumber )
{
size_t zNumberWidth ;
longest L = 0 ;
ASSERT ( szLocation ) ;
ASSERT ( pType ) ;
zNumberWidth = zDeserializeTypeWidth ( pType ) ;
switch ( zNumberWidth ) {
default :
Report ( true , " UBSan: Unexpected %zu-Bit Type in %s \n " , zNumberWidth , szLocation ) ;
/* NOTREACHED */
case WIDTH_128 :
# ifdef __SIZEOF_INT128__
memcpy ( & L , REINTERPRET_CAST ( longest * , ulNumber ) , sizeof ( longest ) ) ;
# else
Report ( true , " UBSan: Unexpected 128-Bit Type in %s \n " , szLocation ) ;
/* NOTREACHED */
# endif
break ;
case WIDTH_64 :
if ( sizeof ( ulNumber ) * CHAR_BIT < WIDTH_64 ) {
L = * REINTERPRET_CAST ( int64_t * , ulNumber ) ;
} else {
L = STATIC_CAST ( int64_t , STATIC_CAST ( uint64_t , ulNumber ) ) ;
}
break ;
case WIDTH_32 :
L = STATIC_CAST ( int32_t , STATIC_CAST ( uint32_t , ulNumber ) ) ;
break ;
case WIDTH_16 :
L = STATIC_CAST ( int16_t , STATIC_CAST ( uint16_t , ulNumber ) ) ;
break ;
case WIDTH_8 :
L = STATIC_CAST ( int8_t , STATIC_CAST ( uint8_t , ulNumber ) ) ;
break ;
}
return L ;
}
static ulongest
llluGetNumber ( char * szLocation , struct CTypeDescriptor * pType , unsigned long ulNumber )
{
size_t zNumberWidth ;
ulongest UL = 0 ;
ASSERT ( pType ) ;
zNumberWidth = zDeserializeTypeWidth ( pType ) ;
switch ( zNumberWidth ) {
default :
Report ( true , " UBSan: Unexpected %zu-Bit Type in %s \n " , zNumberWidth , szLocation ) ;
/* NOTREACHED */
case WIDTH_128 :
# ifdef __SIZEOF_INT128__
memcpy ( & UL , REINTERPRET_CAST ( ulongest * , ulNumber ) , sizeof ( ulongest ) ) ;
break ;
# else
Report ( true , " UBSan: Unexpected 128-Bit Type in %s \n " , szLocation ) ;
/* NOTREACHED */
# endif
case WIDTH_64 :
if ( sizeof ( ulNumber ) * CHAR_BIT < WIDTH_64 ) {
UL = * REINTERPRET_CAST ( uint64_t * , ulNumber ) ;
break ;
}
/* FALLTHROUGH */
case WIDTH_32 :
/* FALLTHROUGH */
case WIDTH_16 :
/* FALLTHROUGH */
case WIDTH_8 :
UL = ulNumber ;
break ;
}
return UL ;
}
# ifndef _KERNEL
static void
DeserializeNumberFloat ( char * szLocation , char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , unsigned long ulNumber )
{
size_t zNumberWidth ;
ASSERT ( szLocation ) ;
ASSERT ( pBuffer ) ;
ASSERT ( zBUfferLength > 0 ) ;
ASSERT ( pType ) ;
ASSERT ( pType - > mTypeKind = = KIND_FLOAT ) ;
zNumberWidth = zDeserializeTypeWidth ( pType ) ;
switch ( zNumberWidth ) {
default :
Report ( true , " UBSan: Unexpected %zu-Bit Type in %s \n " , zNumberWidth , szLocation ) ;
/* NOTREACHED */
# ifdef __HAVE_LONG_DOUBLE
case WIDTH_128 :
case WIDTH_96 :
case WIDTH_80 :
DeserializeFloatOverPointer ( pBuffer , zBUfferLength , pType , REINTERPRET_CAST ( unsigned long * , ulNumber ) ) ;
break ;
# endif
case WIDTH_64 :
if ( sizeof ( ulNumber ) * CHAR_BIT < WIDTH_64 ) {
DeserializeFloatOverPointer ( pBuffer , zBUfferLength , pType , REINTERPRET_CAST ( unsigned long * , ulNumber ) ) ;
break ;
}
case WIDTH_32 :
case WIDTH_16 :
DeserializeFloatInlined ( pBuffer , zBUfferLength , pType , ulNumber ) ;
break ;
}
}
# endif
static void
DeserializeNumber ( char * szLocation , char * pBuffer , size_t zBUfferLength , struct CTypeDescriptor * pType , unsigned long ulNumber )
{
ASSERT ( szLocation ) ;
ASSERT ( pBuffer ) ;
ASSERT ( zBUfferLength > 0 ) ;
ASSERT ( pType ) ;
switch ( pType - > mTypeKind ) {
case KIND_INTEGER :
if ( ISSET ( pType - > mTypeInfo , NUMBER_SIGNED_BIT ) ) {
longest L = llliGetNumber ( szLocation , pType , ulNumber ) ;
DeserializeNumberSigned ( pBuffer , zBUfferLength , pType , L ) ;
} else {
ulongest UL = llluGetNumber ( szLocation , pType , ulNumber ) ;
DeserializeNumberUnsigned ( pBuffer , zBUfferLength , pType , UL ) ;
}
break ;
case KIND_FLOAT :
# ifdef _KERNEL
Report ( true , " UBSan: Unexpected Float Type in %s \n " , szLocation ) ;
/* NOTREACHED */
# else
DeserializeNumberFloat ( szLocation , pBuffer , zBUfferLength , pType , ulNumber ) ;
# endif
break ;
case KIND_UNKNOWN :
Report ( true , " UBSan: Unknown Type in %s \n " , szLocation ) ;
/* NOTREACHED */
break ;
}
}
static const char *
DeserializeTypeCheckKind ( uint8_t hhuTypeCheckKind )
{
const char * rgczTypeCheckKinds [ ] = {
" load of " ,
" store to " ,
" reference binding to " ,
" member access within " ,
" member call on " ,
" constructor call on " ,
" downcast of " ,
" downcast of " ,
" upcast of " ,
" cast to virtual base of " ,
" _Nonnull binding to " ,
" dynamic operation on "
} ;
ASSERT ( __arraycount ( rgczTypeCheckKinds ) > hhuTypeCheckKind ) ;
return rgczTypeCheckKinds [ hhuTypeCheckKind ] ;
}
static const char *
DeserializeBuiltinCheckKind ( uint8_t hhuBuiltinCheckKind )
{
const char * rgczBuiltinCheckKinds [ ] = {
" ctz() " ,
" clz() "
} ;
ASSERT ( __arraycount ( rgczBuiltinCheckKinds ) > hhuBuiltinCheckKind ) ;
return rgczBuiltinCheckKinds [ hhuBuiltinCheckKind ] ;
}
static const char *
DeserializeCFICheckKind ( uint8_t hhuCFICheckKind )
{
const char * rgczCFICheckKinds [ ] = {
" virtual call " , // CFI_VCALL
" non-virtual call " , // CFI_NVCALL
" base-to-derived cast " , // CFI_DERIVEDCAST
" cast to unrelated type " , // CFI_UNRELATEDCAST
" indirect function call " , // CFI_ICALL
" non-virtual pointer to member function call " , // CFI_NVMFCALL
" virtual pointer to member function call " , // CFI_VMFCALL
} ;
ASSERT ( __arraycount ( rgczCFICheckKinds ) > hhuCFICheckKind ) ;
return rgczCFICheckKinds [ hhuCFICheckKind ] ;
}
static bool
isNegativeNumber ( char * szLocation , struct CTypeDescriptor * pType , unsigned long ulNumber )
{
ASSERT ( szLocation ) ;
ASSERT ( pType ) ;
ASSERT ( pType - > mTypeKind = = KIND_INTEGER ) ;
if ( ! ISSET ( pType - > mTypeInfo , NUMBER_SIGNED_BIT ) )
return false ;
return llliGetNumber ( szLocation , pType , ulNumber ) < 0 ;
}
static bool
isShiftExponentTooLarge ( char * szLocation , struct CTypeDescriptor * pType , unsigned long ulNumber , size_t zWidth )
{
ASSERT ( szLocation ) ;
ASSERT ( pType ) ;
ASSERT ( pType - > mTypeKind = = KIND_INTEGER ) ;
return llluGetNumber ( szLocation , pType , ulNumber ) > = zWidth ;
}