odin-abl/QcomModulePkg/Library/UbsanLib/UbsanLib.c
2021-07-23 18:06:52 +00:00

458 lines
13 KiB
C

/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
*/
#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiBootServicesTableLib.h>
/* Maximum string length considering 64 bit number */
#define STR_LENGTH 21
enum Kind {
TK_Integer = 0x0000,
TK_Float = 0x0001,
TK_Unknown = 0x00ff,
};
CONST CHAR8 *TypeCheckKind[] = {
"load of",
"store do",
"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"
};
struct TypeDescriptor {
UINT16 TypeKind;
/* Type-specific value providing information
to interpret meaning of Value of this type.
Bit 0 gives Signed/Unsigned information.
Bit 1 - Bit 15 gives length of data type in power of 2.
*/
UINT16 TypeInfo;
CHAR8 TypeName[1];
};
struct SourceLocation {
CONST CHAR8 *FileName;
UINT32 Line;
UINT32 Column;
};
struct OverflowData {
struct SourceLocation Loc;
struct TypeDescriptor *Type;
};
struct OutOfBounds {
struct SourceLocation Loc;
struct TypeDescriptor *ArrayType;
struct TypeDescriptor *IndexType;
};
struct ShiftOutOfBoundsData {
struct SourceLocation Loc;
struct TypeDescriptor *LHSType;
struct TypeDescriptor *RHSType;
};
struct TypeMismatchData {
struct SourceLocation Loc;
struct TypeDescriptor *Type;
UINT32 *Alignment;
UINT8 TypeCheckKind;
};
struct FloatCastOverflowData {
struct TypeDescriptor *FromType;
struct TypeDescriptor *ToType;
};
struct FloatCastOverflowDataV2 {
struct SourceLocation Loc;
struct TypeDescriptor *FromType;
struct TypeDescriptor *ToType;
};
struct UnreachableData {
struct SourceLocation Loc;
};
struct VLABoundData {
struct SourceLocation Loc;
struct TypeDescriptor *Type;
};
struct InvalidValueData {
struct SourceLocation Loc;
struct TypeDescriptor *Type;
};
struct NonNullReturnData {
struct SourceLocation Loc;
struct SourceLocation AttrLoc;
};
STATIC INT32
GetBitLength (UINT16 typeInfo)
{
/* Bit 0 is removed and shifting 1 by number
from Bit 1 - Bit 15 gives length of datatype
*/
return (1 << (typeInfo >> 1));
}
STATIC BOOLEAN
IsSignedInteger (struct TypeDescriptor *type)
{
/* Bit 0 gives whether value is Signed or Unsigned
*/
if (type->TypeKind == TK_Integer) {
return (type->TypeInfo & 1);
}
return FALSE;
}
STATIC VOID
UbsanBegin (struct SourceLocation *Loc)
{
DEBUG ((EFI_D_ERROR, "=========================================\n"));
DEBUG ((EFI_D_ERROR, "UBSAN: Undefined Behavior found in %a:%d:%d\n",
Loc->FileName, Loc->Line, Loc->Column));
}
STATIC VOID UbsanEnd (VOID)
{
DEBUG ((EFI_D_ERROR, "=========================================\n"));
}
STATIC VOID
HandleTypeMismatch (struct TypeMismatchData *data, UINT32 *ptr)
{
UINT32 *Alignment = data->Alignment;
if (!ptr) {
DEBUG ((EFI_D_ERROR, "%a null pointer of type %a\n",
TypeCheckKind[data->TypeCheckKind], data->Type->TypeName));
} else if (Alignment && ((UINTN)ptr & (UINTN) (Alignment - 1))) {
DEBUG ((EFI_D_ERROR, "%a misaligned address 0x%x for type %a, which "
"requires %d byte alignment\n",
TypeCheckKind[data->TypeCheckKind], (VOID *)ptr,
data->Type->TypeName, Alignment));
} else {
DEBUG ((
EFI_D_ERROR,
"%a address 0x%x with insufficient space for an object of type %a\n",
TypeCheckKind[data->TypeCheckKind], (VOID *)ptr, data->Type->TypeName));
}
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_type_mismatch (struct TypeMismatchData *data, UINT32 *ptr)
{
UbsanBegin (&data->Loc);
HandleTypeMismatch (data, ptr);
UbsanEnd ();
}
STATIC VOID
HandleShiftOutOfBounds (struct ShiftOutOfBoundsData *data,
UINT32 lhs,
UINT32 rhs)
{
INT32 LHSVal = (INT32)lhs;
INT32 RHSVal = (INT32)rhs;
INT32 LHSValBitLength = GetBitLength ((UINT16)data->LHSType->TypeInfo);
if (RHSVal < 0) {
DEBUG ((EFI_D_INFO, "shift exponent %d is negative\n", RHSVal));
} else if (RHSVal >= LHSValBitLength) {
DEBUG ((EFI_D_INFO, "shift exponent %d is too large for %d-bit type %a\n",
RHSVal, LHSValBitLength, data->LHSType->TypeName));
} else if (LHSVal < 0) {
DEBUG ((EFI_D_INFO, "left shift of negative value %d\n", LHSVal));
} else {
DEBUG ((EFI_D_INFO,
"left shift of %d by %d places cannot be represented in type %a\n",
LHSVal, RHSVal, data->LHSType->TypeName));
}
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_shift_out_of_bounds (struct ShiftOutOfBoundsData *data,
UINT32 lhs,
UINT32 rhs)
{
UbsanBegin (&data->Loc);
HandleShiftOutOfBounds (data, lhs, rhs);
UbsanEnd ();
}
STATIC VOID
HandleOutOfBounds (struct OutOfBounds *data, UINT32 Index)
{
CHAR8 index_str[STR_LENGTH];
AsciiValueToString (index_str, LEFT_JUSTIFY, Index, sizeof (index_str) - 1);
DEBUG ((EFI_D_INFO, "index %a out of bounds for type %a\n", index_str,
data->ArrayType->TypeName));
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_out_of_bounds (struct OutOfBounds *data, UINT32 Index)
{
UbsanBegin (&data->Loc);
HandleOutOfBounds (data, Index);
UbsanEnd ();
}
STATIC VOID
OverFlowHandler (struct TypeDescriptor *type,
UINT32 lhs,
UINT32 rhs,
CONST CHAR8 *operation)
{
CHAR8 lhs_val_str[STR_LENGTH];
CHAR8 rhs_val_str[STR_LENGTH];
BOOLEAN IsSigned = IsSignedInteger (type);
AsciiValueToString (lhs_val_str, LEFT_JUSTIFY, lhs, sizeof (lhs_val_str) - 1);
AsciiValueToString (rhs_val_str, LEFT_JUSTIFY, rhs, sizeof (rhs_val_str) - 1);
DEBUG ((EFI_D_ERROR,
"%a integer overflow, %a %a %a can't be represented by type %a\n",
IsSigned ? "signed" : "unsigned", lhs_val_str, operation, rhs_val_str,
type->TypeName));
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_add_overflow (struct OverflowData *data, UINT32 lhs, UINT32 rhs)
{
UbsanBegin (&data->Loc);
OverFlowHandler (data->Type, lhs, rhs, "+");
UbsanEnd ();
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_sub_overflow (struct OverflowData *data, UINT32 lhs, UINT32 rhs)
{
UbsanBegin (&data->Loc);
OverFlowHandler (data->Type, lhs, rhs, "-");
UbsanEnd ();
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_mul_overflow (struct OverflowData *data, UINT32 lhs, UINT32 rhs)
{
UbsanBegin (&data->Loc);
OverFlowHandler (data->Type, lhs, rhs, "*");
UbsanEnd ();
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_divrem_overflow (struct OverflowData *data,
UINT32 lhs,
UINT32 rhs)
{
UbsanBegin (&data->Loc);
OverFlowHandler (data->Type, lhs, rhs, "/");
UbsanEnd ();
}
STATIC VOID
HandleNegateOverflow (struct OverflowData *data, UINT32 oldval)
{
CHAR8 old_val_str[STR_LENGTH];
AsciiValueToString (old_val_str, LEFT_JUSTIFY, oldval,
sizeof (old_val_str) - 1);
if (IsSignedInteger (data->Type)) {
DEBUG ((EFI_D_INFO, "negation of %a can't be represented in type %a,"
"cast to unsigned type to negate this value to itself",
old_val_str, data->Type->TypeName));
} else {
DEBUG ((EFI_D_INFO, "negation of %a can't be represented in type %a\n",
old_val_str, data->Type->TypeName));
}
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_negate_overflow (struct OverflowData *data, UINT32 oldval)
{
UbsanBegin (&data->Loc);
HandleNegateOverflow (data, oldval);
UbsanEnd ();
}
STATIC BOOLEAN
LooksLikeFloatCastOverflowDataV1 (VOID *data)
{
UINT8 *FilenameOrTypeDescriptor = data;
INT16 MaybeFromTypeKind =
FilenameOrTypeDescriptor[0] + FilenameOrTypeDescriptor[1];
/*
* For float_cast_overflow, the TypeKind will be either TK_Integer
* (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known,
* adding both bytes will be 0 or 1. If it were a filename, adding
* two printable characters will not yield such a value. Otherwise,
* if one of them is 0xff, this is most likely TK_Unknown type descriptor.
*/
return MaybeFromTypeKind < 2 || FilenameOrTypeDescriptor[0] == TK_Unknown ||
FilenameOrTypeDescriptor[1] == TK_Unknown;
}
STATIC VOID
HandleFloatCastOverflow (VOID *data, UINT32 From)
{
struct TypeDescriptor *FromType, *ToType;
CHAR8 from_type_str[STR_LENGTH];
if (LooksLikeFloatCastOverflowDataV1 (data)) {
struct FloatCastOverflowData *Data = (struct FloatCastOverflowData *)data;
FromType = Data->FromType;
ToType = Data->ToType;
} else {
struct FloatCastOverflowDataV2 *Data = data;
FromType = Data->FromType;
ToType = Data->ToType;
UbsanBegin (&Data->Loc);
}
AsciiValueToString (from_type_str, LEFT_JUSTIFY, From,
sizeof (from_type_str) - 1);
DEBUG ((EFI_D_INFO, "value %a '%a' is outside the range of representable "
"values of type '%a'\n",
from_type_str, FromType->TypeName, ToType->TypeName));
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_float_cast_overflow (VOID *data, UINT32 From)
{
HandleFloatCastOverflow (data, From);
UbsanEnd ();
}
STATIC VOID
HandleBuiltinUnreachable ()
{
DEBUG ((EFI_D_INFO, "execution reached __builtin_unreachable() call\n"));
ASSERT (TRUE);
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_builtin_unreachable (struct UnreachableData *data)
{
UbsanBegin (&data->Loc);
HandleBuiltinUnreachable ();
UbsanEnd ();
}
STATIC VOID
HandleMissingReturn ()
{
DEBUG ((EFI_D_INFO,
"Execution reached the end of a value-returning function without"
"returning a value\n"));
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_missing_return (struct UnreachableData *data)
{
UbsanBegin (&data->Loc);
HandleMissingReturn ();
UbsanEnd ();
}
STATIC VOID
HandleVlaBoundNotPositive (struct VLABoundData *data, UINT32 Bound)
{
CHAR8 bound_str[STR_LENGTH];
AsciiValueToString (bound_str, LEFT_JUSTIFY, Bound, sizeof (bound_str) - 1);
DEBUG ((EFI_D_INFO, "variable length array bound evaluates to"
"non-positive value %a\n",
bound_str));
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_vla_bound_not_positive (struct VLABoundData *data, UINT32 Bound)
{
UbsanBegin (&data->Loc);
HandleVlaBoundNotPositive (data, Bound);
UbsanEnd ();
}
STATIC VOID
HandleLoadInvalidValue (struct InvalidValueData *data, UINT32 val)
{
CHAR8 val_str[STR_LENGTH];
AsciiValueToString (val_str, LEFT_JUSTIFY, val, sizeof (val_str) - 1);
DEBUG ((EFI_D_INFO,
"load of value %a, which is not a valid value for type %a\n", val_str,
data->Type->TypeName));
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_load_invalid_value (struct InvalidValueData *data, UINT32 val)
{
UbsanBegin (&data->Loc);
HandleLoadInvalidValue (data, val);
UbsanEnd ();
}
STATIC VOID
HandleNonnullReturn (struct NonNullReturnData *data)
{
DEBUG ((EFI_D_INFO, "null pointer returned from function declared to never"
"return null\n"));
DEBUG ((EFI_D_INFO, "returns_nonull attributed is specified here %a\n",
data->AttrLoc));
}
__attribute__ ((no_sanitize ("undefined"))) VOID
__ubsan_handle_nonnull_return (struct NonNullReturnData *data)
{
UbsanBegin (&data->Loc);
HandleNonnullReturn (data);
UbsanEnd ();
}