aboutsummaryrefslogblamecommitdiff
path: root/sys/kern/kern_ubsan.c
blob: 93d39103929c086a493a7f63664efab4225dd27b (plain) (tree)
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704



























                                                                              









                                                                                


                      




                                                                            
      



                      









                                                                               























































































































































































                                                                                                             





                                                   































                                                                                                                                                 

                                                                                                                                                                   





























































                                                                                                                                                                                                                    
                                                                                                                                                                        































































































































































































































































































































































                                                                                                                                                                                                                                                       



























                                                                                                                                                           



















                                                                                                        

















                                                                                                                                                             









































































































































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                                                                                                                
/*	$NetBSD: ubsan.c,v 1.3 2018/08/03 16:31:04 kamil Exp $	*/

/*-
 * Copyright (c) 2018 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * The micro UBSan implementation for the userland (uUBSan) and kernel (kUBSan).
 * The uBSSan versions is suitable for inclusion into libc or used standalone
 * with ATF tests.
 *
 * This file due to long symbol names generated by a compiler during the
 * instrumentation process does not follow the KNF style with 80-column limit.
 */

#include <sys/cdefs.h>
#ifdef __FreeBSD__
__FBSDID("$FreeBSD$");
#else
#if defined(_KERNEL)
__KERNEL_RCSID(0, "$NetBSD: ubsan.c,v 1.3 2018/08/03 16:31:04 kamil Exp $");
#else
__RCSID("$NetBSD: ubsan.c,v 1.3 2018/08/03 16:31:04 kamil Exp $");
#endif
#endif

#if defined(_KERNEL)
#include <sys/param.h>
#include <sys/types.h>
#include <sys/limits.h>
#include <sys/systm.h>
#include <machine/_inttypes.h>
#include <machine/stdarg.h>
#define	ASSERT(x) KASSERT(x, ("%s: " __STRING(x) " failed", __func__))
#define	__arraycount(x) nitems(x)
#define	ISSET(x, y)	((x) & (y))
#define	__BIT(x)	((uintmax_t)1 << (uintmax_t)(x))
#define	__LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask))
#define	__SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask))
#else
#if defined(_LIBC)
#include "namespace.h"
#endif
#include <sys/param.h>
#include <assert.h>
#include <inttypes.h>
#include <math.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#if defined(_LIBC)
#include "extern.h"
#define ubsan_vsyslog vsyslog_ss
#define ASSERT(x) _DIAGASSERT(x)
#else
#define ubsan_vsyslog vsyslog_r
#define ASSERT(x) assert(x)
#endif
/* These macros are available in _KERNEL only */
#define SET(t, f)	((t) |= (f))
#define ISSET(t, f)	((t) & (f))
#define CLR(t, f)	((t) &= ~(f))
#endif

#define REINTERPRET_CAST(__dt, __st)	((__dt)(__st))
#define STATIC_CAST(__dt, __st)		((__dt)(__st))

#define ACK_REPORTED	__BIT(31)

#define MUL_STRING	"*"
#define PLUS_STRING	"+"
#define MINUS_STRING	"-"
#define DIVREM_STRING	"divrem"

#define CFI_VCALL		0
#define CFI_NVCALL		1
#define CFI_DERIVEDCAST		2
#define CFI_UNRELATEDCAST	3
#define CFI_ICALL		4
#define CFI_NVMFCALL		5
#define CFI_VMFCALL		6

#define NUMBER_MAXLEN	128
#define LOCATION_MAXLEN	(PATH_MAX + 32 /* ':LINE:COLUMN' */)

#define WIDTH_8		8
#define WIDTH_16	16
#define WIDTH_32	32
#define WIDTH_64	64
#define WIDTH_80	80
#define WIDTH_96	96
#define WIDTH_128	128

#define NUMBER_SIGNED_BIT	1U

#if __SIZEOF_INT128__
typedef __int128 longest;
typedef unsigned __int128 ulongest;
#else
typedef int64_t longest;
typedef uint64_t ulongest;
#endif

#ifndef _KERNEL
static int ubsan_flags = -1;
#define UBSAN_ABORT	__BIT(0)
#define UBSAN_STDOUT	__BIT(1)
#define UBSAN_STDERR	__BIT(2)
#define UBSAN_SYSLOG	__BIT(3)
#endif

/* Undefined Behavior specific defines and structures */

#define KIND_INTEGER	0
#define KIND_FLOAT	1
#define KIND_UNKNOWN	UINT16_MAX

struct CSourceLocation {
	char *mFilename;
	uint32_t mLine;
	uint32_t mColumn;
};

struct CTypeDescriptor {
	uint16_t mTypeKind;
	uint16_t mTypeInfo;
	uint8_t mTypeName[1];
};

struct COverflowData {
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mType;
};

struct CUnreachableData {
	struct CSourceLocation mLocation;
};

struct CCFICheckFailData {
	uint8_t mCheckKind;
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mType;
};

struct CDynamicTypeCacheMissData {
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mType;
	void *mTypeInfo;
	uint8_t mTypeCheckKind;
};

struct CFunctionTypeMismatchData {
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mType;
};

struct CInvalidBuiltinData {
	struct CSourceLocation mLocation;
	uint8_t mKind;
};

struct CInvalidValueData {
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mType;
};

struct CNonNullArgData {
	struct CSourceLocation mLocation;
	struct CSourceLocation mAttributeLocation;
	int mArgIndex;
};

struct CNonNullReturnData {
	struct CSourceLocation mAttributeLocation;
};

struct COutOfBoundsData {
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mArrayType;
	struct CTypeDescriptor *mIndexType;
};

struct CPointerOverflowData {
	struct CSourceLocation mLocation;
};

struct CShiftOutOfBoundsData {
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mLHSType;
	struct CTypeDescriptor *mRHSType;
};

struct CTypeMismatchData {
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mType;
	unsigned long mLogAlignment;
	uint8_t mTypeCheckKind;
};

struct CTypeMismatchData_v1 {
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mType;
	uint8_t mLogAlignment;
	uint8_t mTypeCheckKind;
};

struct CVLABoundData {
	struct CSourceLocation mLocation;
	struct CTypeDescriptor *mType;
};

struct CFloatCastOverflowData {
	struct CSourceLocation mLocation;	/* This field exists in this struct since 2015 August 11th */
	struct CTypeDescriptor *mFromType;
	struct CTypeDescriptor *mToType;
};

struct CAlignmentAssumptionData {
	struct CSourceLocation mLocation;
	struct CSourceLocation mAssumptionLocation;
	struct CTypeDescriptor *mType;
};

/* Local utility functions */
static void Report(bool isFatal, const char *pFormat, ...) __printflike(2, 3);
static bool isAlreadyReported(struct CSourceLocation *pLocation);
static size_t zDeserializeTypeWidth(struct CTypeDescriptor *pType);
static void DeserializeLocation(char *pBuffer, size_t zBUfferLength, struct CSourceLocation *pLocation);
#ifdef __SIZEOF_INT128__
static void DeserializeUINT128(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, __uint128_t U128);
#endif
static void DeserializeNumberSigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, longest L);
static void DeserializeNumberUnsigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, ulongest L);
#ifndef _KERNEL
static void DeserializeFloatOverPointer(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long *pNumber);
static void DeserializeFloatInlined(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber);
#endif
static longest llliGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber);
static ulongest llluGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber);
#ifndef _KERNEL
static void DeserializeNumberFloat(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber);
#endif
static void DeserializeNumber(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber);
static const char *DeserializeTypeCheckKind(uint8_t hhuTypeCheckKind);
static const char *DeserializeBuiltinCheckKind(uint8_t hhuBuiltinCheckKind);
static const char *DeserializeCFICheckKind(uint8_t hhuCFICheckKind);
static bool isNegativeNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber);
static bool isShiftExponentTooLarge(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber, size_t zWidth);

/* Unused in this implementation, emitted by the C++ check dynamic type cast. */
intptr_t __ubsan_vptr_type_cache[128];

/* Public symbols used in the instrumentation of the code generation part */
void __ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_alignment_assumption(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);
void __ubsan_handle_alignment_assumption_abort(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);
void __ubsan_handle_builtin_unreachable(struct CUnreachableData *pData);
void __ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer);
void __ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable);
void __ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable);
void __ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
void __ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
void __ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom);
void __ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom);
void __ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
void __ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
void __ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData);
void __ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData);
void __ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulVal);
void __ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulVal);
void __ubsan_handle_missing_return(struct CUnreachableData *pData);
void __ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldVal);
void __ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldVal);
void __ubsan_handle_nonnull_arg(struct CNonNullArgData *pData);
void __ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData);
void __ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
void __ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
void __ubsan_handle_nullability_arg(struct CNonNullArgData *pData);
void __ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData);
void __ubsan_handle_nullability_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
void __ubsan_handle_nullability_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
void __ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex);
void __ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex);
void __ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
void __ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
void __ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS);
void __ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, unsigned long ulPointer);
void __ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, unsigned long ulPointer);
void __ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer);
void __ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer);
void __ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound);
void __ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound);
void __ubsan_get_current_report_data(const char **ppOutIssueKind, const char **ppOutMessage, const char **ppOutFilename, uint32_t *pOutLine, uint32_t *pOutCol, char **ppOutMemoryAddr);

static void HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation);
static void HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue);
static void HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData);
static void HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer);
static void HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound);
static void HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex);
static void HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS);
static void HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue);
static void HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData);
static void HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction);
static void HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer);
static void HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash);
static void HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom);
static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData);
static void HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData);
static void HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer);
static void HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult);
static void HandleAlignmentAssumption(bool isFatal, struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset);

static void
HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation)
{
	char szLocation[LOCATION_MAXLEN];
	char szLHS[NUMBER_MAXLEN];
	char szRHS[NUMBER_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
	DeserializeNumber(szLocation, szLHS, NUMBER_MAXLEN, pData->mType, ulLHS);
	DeserializeNumber(szLocation, szRHS, NUMBER_MAXLEN, pData->mType, ulRHS);

	Report(isFatal, "UBSan: Undefined Behavior in %s, %s integer overflow: %s %s %s cannot be represented in type %s\n",
	       szLocation, ISSET(pData->mType->mTypeInfo, NUMBER_SIGNED_BIT) ? "signed" : "unsigned", szLHS, szOperation, szRHS, pData->mType->mTypeName);
}

static void
HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue)
{
	char szLocation[LOCATION_MAXLEN];
	char szOldValue[NUMBER_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
	DeserializeNumber(szLocation, szOldValue, NUMBER_MAXLEN, pData->mType, ulOldValue);

	Report(isFatal, "UBSan: Undefined Behavior in %s, negation of %s cannot be represented in type %s\n",
	       szLocation, szOldValue, pData->mType->mTypeName);
}

static void
HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData)
{
	char szLocation[LOCATION_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);

	Report(isFatal, "UBSan: Undefined Behavior in %s, calling __builtin_unreachable()\n",
	       szLocation);
}

static void
HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer)
{
	char szLocation[LOCATION_MAXLEN];

	ASSERT(mLocation);
	ASSERT(mType);

	if (isAlreadyReported(mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, mLocation);

	if (ulPointer == 0) {
		Report(isFatal, "UBSan: Undefined Behavior in %s, %s null pointer of type %s\n",
		       szLocation, DeserializeTypeCheckKind(mTypeCheckKind), mType->mTypeName);
	} else if ((mLogAlignment - 1) & ulPointer) {
		Report(isFatal, "UBSan: Undefined Behavior in %s, %s misaligned address %p for type %s which requires %ld byte alignment\n",
		       szLocation, DeserializeTypeCheckKind(mTypeCheckKind), REINTERPRET_CAST(void *, ulPointer), mType->mTypeName, mLogAlignment);
	} else {
		Report(isFatal, "UBSan: Undefined Behavior in %s, %s address %p with insufficient space for an object of type %s\n",
		       szLocation, DeserializeTypeCheckKind(mTypeCheckKind), REINTERPRET_CAST(void *, ulPointer), mType->mTypeName);
	}
}

static void
HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound)
{
	char szLocation[LOCATION_MAXLEN];
	char szBound[NUMBER_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
	DeserializeNumber(szLocation, szBound, NUMBER_MAXLEN, pData->mType, ulBound);

	Report(isFatal, "UBSan: Undefined Behavior in %s, variable length array bound value %s <= 0\n",
	       szLocation, szBound);
}

static void
HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex)
{
	char szLocation[LOCATION_MAXLEN];
	char szIndex[NUMBER_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
	DeserializeNumber(szLocation, szIndex, NUMBER_MAXLEN, pData->mIndexType, ulIndex);

	Report(isFatal, "UBSan: Undefined Behavior in %s, index %s is out of range for type %s\n",
	       szLocation, szIndex, pData->mArrayType->mTypeName);
}

static void
HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
{
	char szLocation[LOCATION_MAXLEN];
	char szLHS[NUMBER_MAXLEN];
	char szRHS[NUMBER_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
	DeserializeNumber(szLocation, szLHS, NUMBER_MAXLEN, pData->mLHSType, ulLHS);
	DeserializeNumber(szLocation, szRHS, NUMBER_MAXLEN, pData->mRHSType, ulRHS);

	if (isNegativeNumber(szLocation, pData->mRHSType, ulRHS))
		Report(isFatal, "UBSan: Undefined Behavior in %s, shift exponent %s is negative\n",
		       szLocation, szRHS);
	else if (isShiftExponentTooLarge(szLocation, pData->mRHSType, ulRHS, zDeserializeTypeWidth(pData->mLHSType)))
		Report(isFatal, "UBSan: Undefined Behavior in %s, shift exponent %s is too large for %zu-bit type %s\n",
		       szLocation, szRHS, zDeserializeTypeWidth(pData->mLHSType), pData->mLHSType->mTypeName);
	else if (isNegativeNumber(szLocation, pData->mLHSType, ulLHS))
		Report(isFatal, "UBSan: Undefined Behavior in %s, left shift of negative value %s\n",
		       szLocation, szLHS);
	else
		Report(isFatal, "UBSan: Undefined Behavior in %s, left shift of %s by %s places cannot be represented in type %s\n",
		       szLocation, szLHS, szRHS, pData->mLHSType->mTypeName);
}

static void
HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue)
{
	char szLocation[LOCATION_MAXLEN];
	char szValue[NUMBER_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
	DeserializeNumber(szLocation, szValue, NUMBER_MAXLEN, pData->mType, ulValue);

	Report(isFatal, "UBSan: Undefined Behavior in %s, load of value %s is not a valid value for type %s\n",
	       szLocation, szValue, pData->mType->mTypeName);
}

static void
HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData)
{
	char szLocation[LOCATION_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);

	Report(isFatal, "UBSan: Undefined Behavior in %s, passing zero to %s, which is not a valid argument\n",
	       szLocation, DeserializeBuiltinCheckKind(pData->mKind));
}

static void
HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
{
	char szLocation[LOCATION_MAXLEN];

	/*
	 * There is no a portable C solution to translate an address of a
	 * function to its name. On the cost of getting this routine simple
	 * and portable without ifdefs between the userland and the kernel
	 * just print the address of the function as-is.
	 *
	 * For better diagnostic messages in the userland, users shall use
	 * the full upstream version shipped along with the compiler toolchain.
	 */

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);

	Report(isFatal, "UBSan: Undefined Behavior in %s, call to function %#lx through pointer to incorrect function type %s\n",
	      szLocation, ulFunction, pData->mType->mTypeName);
}

static void
HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer)
{
	char szLocation[LOCATION_MAXLEN];

	/*
	 * This is a minimal implementation without diving into C++
	 * specifics and (Itanium) ABI deserialization.
	 */

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);

	if (pData->mCheckKind == CFI_ICALL || pData->mCheckKind == CFI_VMFCALL) {
		Report(isFatal, "UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx)\n",
		      szLocation, pData->mType->mTypeName, DeserializeCFICheckKind(pData->mCheckKind), ulVtable);
	} else {
		Report(isFatal || FromUnrecoverableHandler, "UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx; %s vtable; from %s handler; Program Counter %#lx; Frame Pointer %#lx)\n",
		      szLocation, pData->mType->mTypeName, DeserializeCFICheckKind(pData->mCheckKind), ulVtable, *bValidVtable ? "valid" : "invalid", *FromUnrecoverableHandler ? "unrecoverable" : "recoverable", *ProgramCounter, *FramePointer);
	}
}

static void
HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash)
{
#if 0
	char szLocation[LOCATION_MAXLEN];

	/*
	 * Unimplemented.
	 *
	 * This UBSan handler is special as the check has to be impelemented
	 * in an implementation. In order to handle it there is need to
	 * introspect into C++ ABI internals (RTTI) and use low-level
	 * C++ runtime interfaces.
	 */

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);

	Report(isFatal, "UBSan: Undefined Behavior in %s, %s address %#lx which might not point to an object of type %s\n"
	      szLocation, DeserializeTypeCheckKind(pData->mTypeCheckKind), ulPointer, pData->mType);
#endif
}

static void
HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom)
{
	char szLocation[LOCATION_MAXLEN];
	char szFrom[NUMBER_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
	DeserializeNumber(szLocation, szFrom, NUMBER_MAXLEN, pData->mFromType, ulFrom);

	Report(isFatal, "UBSan: Undefined Behavior in %s, %s (of type %s) is outside the range of representable values of type %s\n",
	       szLocation, szFrom, pData->mFromType->mTypeName, pData->mToType->mTypeName);
}

static void
HandleMissingReturn(bool isFatal, struct CUnreachableData *pData)
{
	char szLocation[LOCATION_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);

	Report(isFatal, "UBSan: Undefined Behavior in %s, execution reached the end of a value-returning function without returning a value\n",
	       szLocation);
}

static void
HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData)
{
	char szLocation[LOCATION_MAXLEN];
	char szAttributeLocation[LOCATION_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);
	if (pData->mAttributeLocation.mFilename)
		DeserializeLocation(szAttributeLocation, LOCATION_MAXLEN, &pData->mAttributeLocation);
	else
		szAttributeLocation[0] = '\0';

	Report(isFatal, "UBSan: Undefined Behavior in %s, null pointer passed as argument %d, which is declared to never be null%s%s\n",
	       szLocation, pData->mArgIndex, pData->mAttributeLocation.mFilename ? ", nonnull/_Nonnull specified in " : "", szAttributeLocation);
}

static void
HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{
	char szLocation[LOCATION_MAXLEN];
	char szAttributeLocation[LOCATION_MAXLEN];

	ASSERT(pData);
	ASSERT(pLocationPointer);

	if (isAlreadyReported(pLocationPointer))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, pLocationPointer);
	if (pData->mAttributeLocation.mFilename)
		DeserializeLocation(szAttributeLocation, LOCATION_MAXLEN, &pData->mAttributeLocation);
	else
		szAttributeLocation[0] = '\0';

	Report(isFatal, "UBSan: Undefined Behavior in %s, null pointer returned from function declared to never return null%s%s\n",
	       szLocation, pData->mAttributeLocation.mFilename ? ", nonnull/_Nonnull specified in " : "", szAttributeLocation);
}

static void
HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
{
	char szLocation[LOCATION_MAXLEN];

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);

	Report(isFatal, "UBSan: Undefined Behavior in %s, pointer expression with base %#lx overflowed to %#lx\n",
	       szLocation, ulBase, ulResult);
}

static void
HandleAlignmentAssumption(bool isFatal, struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
{
	char szLocation[LOCATION_MAXLEN];
	char szAssumptionLocation[LOCATION_MAXLEN];
	unsigned long ulRealPointer;

	ASSERT(pData);

	if (isAlreadyReported(&pData->mLocation))
		return;

	DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation);

	ulRealPointer = ulPointer - ulOffset;

	if (pData->mAssumptionLocation.mFilename != NULL) {
		DeserializeLocation(szAssumptionLocation, LOCATION_MAXLEN,
		    &pData->mAssumptionLocation);
		Report(isFatal, "UBSan: Undefined Behavior in %s, alignment assumption of %#lx for pointer %#lx (offset %#lx), asumption made in %s\n",
		    szLocation, ulAlignment, ulRealPointer, ulOffset,
		    szAssumptionLocation);
	} else {
		Report(isFatal, "UBSan: Undefined Behavior in %s, alignment assumption of %#lx for pointer %#lx (offset %#lx)\n",
		    szLocation, ulAlignment, ulRealPointer, ulOffset);
	}
}

/* Definions of public symbols emitted by the instrumentation code */
void
__ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleOverflow(false, pData, ulLHS, ulRHS, PLUS_STRING);
}

void
__ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleOverflow(true, pData, ulLHS, ulRHS, PLUS_STRING);
}

void
__ubsan_handle_alignment_assumption(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
{

	ASSERT(pData);

	HandleAlignmentAssumption(false, pData, ulPointer, ulAlignment, ulOffset);
}

void
__ubsan_handle_alignment_assumption_abort(struct CAlignmentAssumptionData *pData, unsigned long ulPointer, unsigned long ulAlignment, unsigned long ulOffset)
{

	ASSERT(pData);

	HandleAlignmentAssumption(true, pData, ulPointer, ulAlignment, ulOffset);
}

void
__ubsan_handle_builtin_unreachable(struct CUnreachableData *pData)
{

	ASSERT(pData);

	HandleBuiltinUnreachable(true, pData);
}

void
__ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer)
{

	ASSERT(pData);

	HandleCFIBadType(false, pData, ulVtable, &bValidVtable, &FromUnrecoverableHandler, &ProgramCounter, &FramePointer);
}

void
__ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable)
{

	ASSERT(pData);

	HandleCFIBadType(false, pData, ulValue, 0, 0, 0, 0);
}

void
__ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable)
{

	ASSERT(pData);

	HandleCFIBadType(true, pData, ulValue, 0, 0, 0, 0);
}

void
__ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleOverflow(false, pData, ulLHS, ulRHS, DIVREM_STRING);
}

void
__ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleOverflow(true, pData, ulLHS, ulRHS, DIVREM_STRING);
}

void
__ubsan_handle_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash)
{

	ASSERT(pData);

	HandleDynamicTypeCacheMiss(false, pData, ulPointer, ulHash);
}

void
__ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash)
{

	ASSERT(pData);

	HandleDynamicTypeCacheMiss(false, pData, ulPointer, ulHash);
}

void
__ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom)
{

	ASSERT(pData);

	HandleFloatCastOverflow(false, pData, ulFrom);
}

void
__ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom)
{

	ASSERT(pData);

	HandleFloatCastOverflow(true, pData, ulFrom);
}

void
__ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
{

	ASSERT(pData);

	HandleFunctionTypeMismatch(false, pData, ulFunction);
}

void
__ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction)
{

	ASSERT(pData);

	HandleFunctionTypeMismatch(false, pData, ulFunction);
}

void
__ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData)
{

	ASSERT(pData);

	HandleInvalidBuiltin(true, pData);
}

void
__ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData)
{

	ASSERT(pData);

	HandleInvalidBuiltin(true, pData);
}

void
__ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulValue)
{

	ASSERT(pData);

	HandleLoadInvalidValue(false, pData, ulValue);
}

void
__ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulValue)
{

	ASSERT(pData);

	HandleLoadInvalidValue(true, pData, ulValue);
}

void
__ubsan_handle_missing_return(struct CUnreachableData *pData)
{

	ASSERT(pData);

	HandleMissingReturn(true, pData);
}

void
__ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleOverflow(false, pData, ulLHS, ulRHS, MUL_STRING);
}

void
__ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleOverflow(true, pData, ulLHS, ulRHS, MUL_STRING);
}

void
__ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldValue)
{

	ASSERT(pData);

	HandleNegateOverflow(false, pData, ulOldValue);
}

void
__ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldValue)
{

	ASSERT(pData);

	HandleNegateOverflow(true, pData, ulOldValue);
}

void
__ubsan_handle_nonnull_arg(struct CNonNullArgData *pData)
{

	ASSERT(pData);

	HandleNonnullArg(false, pData);
}

void
__ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData)
{

	ASSERT(pData);

	HandleNonnullArg(true, pData);
}

void
__ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{

	ASSERT(pData);
	ASSERT(pLocationPointer);

	HandleNonnullReturn(false, pData, pLocationPointer);
}

void
__ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{

	ASSERT(pData);
	ASSERT(pLocationPointer);

	HandleNonnullReturn(true, pData, pLocationPointer);
}

void
__ubsan_handle_nullability_arg(struct CNonNullArgData *pData)
{

	ASSERT(pData);

	HandleNonnullArg(false, pData);
}

void
__ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData)
{

	ASSERT(pData);

	HandleNonnullArg(true, pData);
}

void
__ubsan_handle_nullability_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{

	ASSERT(pData);
	ASSERT(pLocationPointer);

	HandleNonnullReturn(false, pData, pLocationPointer);
}

void
__ubsan_handle_nullability_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer)
{

	ASSERT(pData);
	ASSERT(pLocationPointer);

	HandleNonnullReturn(true, pData, pLocationPointer);
}

void
__ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex)
{

	ASSERT(pData);

	HandleOutOfBounds(false, pData, ulIndex);
}

void
__ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex)
{

	ASSERT(pData);

	HandleOutOfBounds(true, pData, ulIndex);
}

void
__ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
{

	ASSERT(pData);

	HandlePointerOverflow(false, pData, ulBase, ulResult);
}

void
__ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult)
{

	ASSERT(pData);

	HandlePointerOverflow(true, pData, ulBase, ulResult);
}

void
__ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleShiftOutOfBounds(false, pData, ulLHS, ulRHS);
}

void
__ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleShiftOutOfBounds(true, pData, ulLHS, ulRHS);
}

void
__ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleOverflow(false, pData, ulLHS, ulRHS, MINUS_STRING);
}

void
__ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS)
{

	ASSERT(pData);

	HandleOverflow(true, pData, ulLHS, ulRHS, MINUS_STRING);
}

void
__ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, unsigned long ulPointer)
{

	ASSERT(pData);

	HandleTypeMismatch(false, &pData->mLocation, pData->mType, pData->mLogAlignment, pData->mTypeCheckKind, ulPointer);
}

void
__ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, unsigned long ulPointer)
{

	ASSERT(pData);

	HandleTypeMismatch(true, &pData->mLocation, pData->mType, pData->mLogAlignment, pData->mTypeCheckKind, ulPointer);
}

void
__ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer)
{

	ASSERT(pData);

	HandleTypeMismatch(false, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer);
}

void
__ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer)
{

	ASSERT(pData);

	HandleTypeMismatch(true, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer);
}

void
__ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound)
{

	ASSERT(pData);

	HandleVlaBoundNotPositive(false, pData, ulBound);
}

void
__ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound)
{

	ASSERT(pData);

	HandleVlaBoundNotPositive(true, pData, ulBound);
}

void
__ubsan_get_current_report_data(const char **ppOutIssueKind, const char **ppOutMessage, const char **ppOutFilename, uint32_t *pOutLine, uint32_t *pOutCol, char **ppOutMemoryAddr)
{
	/*
	 * Unimplemented.
	 *
	 * The __ubsan_on_report() feature is non trivial to implement in a
	 * shared code between the kernel and userland. It's also opening
	 * new sets of potential problems as we are not expected to slow down
	 * execution of certain kernel subsystems (synchronization issues,
	 * interrupt handling etc).
	 *
	 * A proper solution would need probably a lock-free bounded queue built
	 * with atomic operations with the property of miltiple consumers and
	 * multiple producers. Maintaining and validating such code is not
	 * worth the effort.
	 *
	 * A legitimate user - besides testing framework - is a debugger plugin
	 * intercepting reports from the UBSan instrumentation. For such
	 * scenarios it is better to run the Clang/GCC version.
	 */
}

/* Local utility functions */

static void
Report(bool isFatal, const char *pFormat, ...)
{
	va_list ap;

	ASSERT(pFormat);

	va_start(ap, pFormat);
#if defined(_KERNEL)
	if (isFatal)
		vpanic(pFormat, ap);
	else
		vprintf(pFormat, ap);
#else
	if (ubsan_flags == -1) {
		char buf[1024];
		char *p;

		ubsan_flags = UBSAN_STDERR;

		if (getenv_r("LIBC_UBSAN", buf, sizeof(buf)) != -1) {
			for (p = buf; *p; p++) {
				switch (*p) {
				case 'a':
					SET(ubsan_flags, UBSAN_ABORT);
					break;
				case 'A':
					CLR(ubsan_flags, UBSAN_ABORT);
					break;
				case 'e':
					SET(ubsan_flags, UBSAN_STDERR);
					break;
				case 'E':
					CLR(ubsan_flags, UBSAN_STDERR);
					break;
				case 'l':
					SET(ubsan_flags, UBSAN_SYSLOG);
					break;
				case 'L':
					CLR(ubsan_flags, UBSAN_SYSLOG);
					break;
				case 'o':
					SET(ubsan_flags, UBSAN_STDOUT);
					break;
				case 'O':
					CLR(ubsan_flags, UBSAN_STDOUT);
					break;
				default:
					break;
				}
			}
		}
	}

	// The *v*print* functions can flush the va_list argument.
	// Create a local copy for each call to prevent invalid read.
	if (ISSET(ubsan_flags, UBSAN_STDOUT)) {
		va_list tmp;
		va_copy(tmp, ap);
		vprintf(pFormat, tmp);
		va_end(tmp);
		fflush(stdout);
	}
	if (ISSET(ubsan_flags, UBSAN_STDERR)) {
		va_list tmp;
		va_copy(tmp, ap);
		vfprintf(stderr, pFormat, tmp);
		va_end(tmp);
		fflush(stderr);
	}
	if (ISSET(ubsan_flags, UBSAN_SYSLOG)) {
		va_list tmp;
		va_copy(tmp, ap);
		struct syslog_data SyslogData = SYSLOG_DATA_INIT;
		ubsan_vsyslog(LOG_DEBUG | LOG_USER, &SyslogData, pFormat, tmp);
		va_end(tmp);
	}
	if (isFatal || ISSET(ubsan_flags, UBSAN_ABORT)) {
		abort();
		/* NOTREACHED */
	}
#endif
	va_end(ap);
}

static bool
isAlreadyReported(struct CSourceLocation *pLocation)
{
	/*
	 * This code is shared between libc, kernel and standalone usage.
	 * It shall work in early bootstrap phase of both of them.
	 */

	uint32_t siOldValue;
	volatile uint32_t *pLine;

	ASSERT(pLocation);

	pLine = &pLocation->mLine;

	do {
		siOldValue = *pLine;
	} while (__sync_val_compare_and_swap(pLine, siOldValue, siOldValue | ACK_REPORTED) != siOldValue);

	return ISSET(siOldValue, ACK_REPORTED);
}

static size_t
zDeserializeTypeWidth(struct CTypeDescriptor *pType)
{
	size_t zWidth = 0;

	ASSERT(pType);

	switch (pType->mTypeKind) {
	case KIND_INTEGER:
		zWidth = __BIT(__SHIFTOUT(pType->mTypeInfo, ~NUMBER_SIGNED_BIT));
		break;
	case KIND_FLOAT:
		zWidth = pType->mTypeInfo;
		break;
	default:
		Report(true, "UBSan: Unknown variable type %#04" PRIx16 "\n", pType->mTypeKind);
		/* NOTREACHED */
	}

	/* Invalid width will be transformed to 0 */
	ASSERT(zWidth > 0);

	return zWidth;
}

static void
DeserializeLocation(char *pBuffer, size_t zBUfferLength, struct CSourceLocation *pLocation)
{

	ASSERT(pLocation);
	ASSERT(pLocation->mFilename);

	snprintf(pBuffer, zBUfferLength, "%s:%" PRIu32 ":%" PRIu32, pLocation->mFilename, pLocation->mLine & (uint32_t)~ACK_REPORTED, pLocation->mColumn);
}

#ifdef __SIZEOF_INT128__
static void
DeserializeUINT128(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, __uint128_t U128)
{
	char szBuf[3]; /* 'XX\0' */
	char rgNumber[sizeof(ulongest)];
	ssize_t zI;

	memcpy(rgNumber, &U128, sizeof(U128));

	strlcpy(pBuffer, "Undecoded-128-bit-Integer-Type (0x", zBUfferLength);
#if BYTE_ORDER == LITTLE_ENDIAN
	for (zI = sizeof(ulongest) - 1; zI >= 0; zI--) {
#else
	for (zI = 0; zI < (ssize_t)sizeof(ulongest); zI++) {
#endif
		snprintf(szBuf, sizeof(szBuf), "%02" PRIx8, rgNumber[zI]);
		strlcat(pBuffer, szBuf, zBUfferLength);
	}
	strlcat(pBuffer, ")", zBUfferLength);
}
#endif

static void
DeserializeNumberSigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, longest L)
{

	ASSERT(pBuffer);
	ASSERT(zBUfferLength > 0);
	ASSERT(pType);
	ASSERT(ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT));

	switch (zDeserializeTypeWidth(pType)) {
	default:
		ASSERT(0 && "Invalid codepath");
		/* NOTREACHED */
#ifdef __SIZEOF_INT128__
	case WIDTH_128:
		DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L));
		break;
#endif
	case WIDTH_64:
	case WIDTH_32:
	case WIDTH_16:
	case WIDTH_8:
		snprintf(pBuffer, zBUfferLength, "%" PRId64, STATIC_CAST(int64_t, L));
		break;
	}
}

static void
DeserializeNumberUnsigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, ulongest L)
{

	ASSERT(pBuffer);
	ASSERT(zBUfferLength > 0);
	ASSERT(pType);
	ASSERT(!ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT));

	switch (zDeserializeTypeWidth(pType)) {
	default:
		ASSERT(0 && "Invalid codepath");
		/* NOTREACHED */
#ifdef __SIZEOF_INT128__
	case WIDTH_128:
		DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L));
		break;
#endif
	case WIDTH_64:
	case WIDTH_32:
	case WIDTH_16:
	case WIDTH_8:
		snprintf(pBuffer, zBUfferLength, "%" PRIu64, STATIC_CAST(uint64_t, L));
		break;
	}
}

#ifndef _KERNEL
static void
DeserializeFloatOverPointer(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long *pNumber)
{
	double D;
#ifdef __HAVE_LONG_DOUBLE
	long double LD;
#endif

	ASSERT(pBuffer);
	ASSERT(zBUfferLength > 0);
	ASSERT(pType);
	ASSERT(pNumber);
	/*
	 * This function handles 64-bit number over a pointer on 32-bit CPUs.
	 */
	ASSERT((sizeof(*pNumber) * CHAR_BIT < WIDTH_64) || (zDeserializeTypeWidth(pType) >= WIDTH_64));
	ASSERT(sizeof(D) == sizeof(uint64_t));
#ifdef __HAVE_LONG_DOUBLE
	ASSERT(sizeof(LD) > sizeof(uint64_t));
#endif

	switch (zDeserializeTypeWidth(pType)) {
#ifdef __HAVE_LONG_DOUBLE
	case WIDTH_128:
	case WIDTH_96:
	case WIDTH_80:
		memcpy(&LD, pNumber, sizeof(long double));
		snprintf(pBuffer, zBUfferLength, "%Lg", LD);
		break;
#endif
	case WIDTH_64:
		memcpy(&D, pNumber, sizeof(double));
		snprintf(pBuffer, zBUfferLength, "%g", D);
		break;
	}
}

static void
DeserializeFloatInlined(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
	float F;
	double D;
	uint32_t U32;

	ASSERT(pBuffer);
	ASSERT(zBUfferLength > 0);
	ASSERT(pType);
	ASSERT(sizeof(F) == sizeof(uint32_t));
	ASSERT(sizeof(D) == sizeof(uint64_t));

	switch (zDeserializeTypeWidth(pType)) {
	case WIDTH_64:
		memcpy(&D, &ulNumber, sizeof(double));
		snprintf(pBuffer, zBUfferLength, "%g", D);
		break;
	case WIDTH_32:
		/*
		 * On supported platforms sizeof(float)==sizeof(uint32_t)
		 * unsigned long is either 32 or 64-bit, cast it to 32-bit
		 * value in order to call memcpy(3) in an Endian-aware way.
		 */
		U32 = STATIC_CAST(uint32_t, ulNumber);
		memcpy(&F, &U32, sizeof(float));
		snprintf(pBuffer, zBUfferLength, "%g", F);
		break;
	case WIDTH_16:
		snprintf(pBuffer, zBUfferLength, "Undecoded-16-bit-Floating-Type (%#04" PRIx16 ")", STATIC_CAST(uint16_t, ulNumber));
		break;
	}
}
#endif

static longest
llliGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
	size_t zNumberWidth;
	longest L = 0;

	ASSERT(szLocation);
	ASSERT(pType);

	zNumberWidth = zDeserializeTypeWidth(pType);
	switch (zNumberWidth) {
	default:
		Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation);
		/* NOTREACHED */
	case WIDTH_128:
#ifdef __SIZEOF_INT128__
		memcpy(&L, REINTERPRET_CAST(longest *, ulNumber), sizeof(longest));
#else
		Report(true, "UBSan: Unexpected 128-Bit Type in %s\n", szLocation);
		/* NOTREACHED */
#endif
		break;
	case WIDTH_64:
		if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) {
			L = *REINTERPRET_CAST(int64_t *, ulNumber);
		} else {
			L = STATIC_CAST(int64_t, STATIC_CAST(uint64_t, ulNumber));
		}
		break;
	case WIDTH_32:
		L = STATIC_CAST(int32_t, STATIC_CAST(uint32_t, ulNumber));
		break;
	case WIDTH_16:
		L = STATIC_CAST(int16_t, STATIC_CAST(uint16_t, ulNumber));
		break;
	case WIDTH_8:
		L = STATIC_CAST(int8_t, STATIC_CAST(uint8_t, ulNumber));
		break;
	}

	return L;
}

static ulongest
llluGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
	size_t zNumberWidth;
	ulongest UL = 0;

	ASSERT(pType);

	zNumberWidth = zDeserializeTypeWidth(pType);
	switch (zNumberWidth) {
	default:
		Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation);
		/* NOTREACHED */
	case WIDTH_128:
#ifdef __SIZEOF_INT128__
		memcpy(&UL, REINTERPRET_CAST(ulongest *, ulNumber), sizeof(ulongest));
		break;
#else
		Report(true, "UBSan: Unexpected 128-Bit Type in %s\n", szLocation);
		/* NOTREACHED */
#endif
	case WIDTH_64:
		if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) {
			UL = *REINTERPRET_CAST(uint64_t *, ulNumber);
			break;
		}
		/* FALLTHROUGH */
	case WIDTH_32:
		/* FALLTHROUGH */
	case WIDTH_16:
		/* FALLTHROUGH */
	case WIDTH_8:
		UL = ulNumber;
		break;
	}

	return UL;
}

#ifndef _KERNEL
static void
DeserializeNumberFloat(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber)
{
	size_t zNumberWidth;

	ASSERT(szLocation);
	ASSERT(pBuffer);
	ASSERT(zBUfferLength > 0);
	ASSERT(pType);
	ASSERT(pType->mTypeKind == KIND_FLOAT);

	zNumberWidth = zDeserializeTypeWidth(pType);
	switch (zNumberWidth) {
	default:
		Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation);
		/* NOTREACHED */
#ifdef __HAVE_LONG_DOUBLE
	case WIDTH_128:
	case WIDTH_96:
	case WIDTH_80:
		DeserializeFloatOverPointer(pBuffer, zBUfferLength, pType, REINTERPRET_CAST(unsigned long *, ulNumber));
		break;
#endif
	case WIDTH_64:
		if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) {
			DeserializeFloatOverPointer(pBuffer, zBUfferLength, pType, REINTERPRET_CAST(unsigned long *, ulNumber));
			break;
		}
	case WIDTH_32:
	case WIDTH_16:
		DeserializeFloatInlined(pBuffer, zBUfferLength, pType, ulNumber);
		break;
	}
}
#endif

static void
DeserializeNumber(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber)
{

	ASSERT(szLocation);
	ASSERT(pBuffer);
	ASSERT(zBUfferLength > 0);
	ASSERT(pType);

	switch(pType->mTypeKind) {
	case KIND_INTEGER:
		if (ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)) {
			longest L = llliGetNumber(szLocation, pType, ulNumber);
			DeserializeNumberSigned(pBuffer, zBUfferLength, pType, L);
		} else {
			ulongest UL = llluGetNumber(szLocation, pType, ulNumber);
			DeserializeNumberUnsigned(pBuffer, zBUfferLength, pType, UL);
		}
		break;
	case KIND_FLOAT:
#ifdef _KERNEL
		Report(true, "UBSan: Unexpected Float Type in %s\n", szLocation);
		/* NOTREACHED */
#else
		DeserializeNumberFloat(szLocation, pBuffer, zBUfferLength, pType, ulNumber);
#endif
		break;
	case KIND_UNKNOWN:
		Report(true, "UBSan: Unknown Type in %s\n", szLocation);
		/* NOTREACHED */
		break;
	}
}

static const char *
DeserializeTypeCheckKind(uint8_t hhuTypeCheckKind)
{
	const char *rgczTypeCheckKinds[] = {
		"load of",
		"store to",
		"reference binding to",
		"member access within",
		"member call on",
		"constructor call on",
		"downcast of",
		"downcast of",
		"upcast of",
		"cast to virtual base of",
		"_Nonnull binding to",
		"dynamic operation on"
	};

	ASSERT(__arraycount(rgczTypeCheckKinds) > hhuTypeCheckKind);

	return rgczTypeCheckKinds[hhuTypeCheckKind];
}

static const char *
DeserializeBuiltinCheckKind(uint8_t hhuBuiltinCheckKind)
{
	const char *rgczBuiltinCheckKinds[] = {
		"ctz()",
		"clz()"
	};

	ASSERT(__arraycount(rgczBuiltinCheckKinds) > hhuBuiltinCheckKind);

	return rgczBuiltinCheckKinds[hhuBuiltinCheckKind];
}

static const char *
DeserializeCFICheckKind(uint8_t hhuCFICheckKind)
{
	const char *rgczCFICheckKinds[] = {
		"virtual call",					// CFI_VCALL
		"non-virtual call",				// CFI_NVCALL
		"base-to-derived cast",				// CFI_DERIVEDCAST
		"cast to unrelated type",			// CFI_UNRELATEDCAST
		"indirect function call",			// CFI_ICALL
		"non-virtual pointer to member function call",	// CFI_NVMFCALL
		"virtual pointer to member function call",	// CFI_VMFCALL
	};

	ASSERT(__arraycount(rgczCFICheckKinds) > hhuCFICheckKind);

	return rgczCFICheckKinds[hhuCFICheckKind];
}

static bool
isNegativeNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber)
{

	ASSERT(szLocation);
	ASSERT(pType);
	ASSERT(pType->mTypeKind == KIND_INTEGER);

	if (!ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT))
		return false;

	return llliGetNumber(szLocation, pType, ulNumber) < 0;
}

static bool
isShiftExponentTooLarge(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber, size_t zWidth)
{

	ASSERT(szLocation);
	ASSERT(pType);
	ASSERT(pType->mTypeKind == KIND_INTEGER);

	return llluGetNumber(szLocation, pType, ulNumber) >= zWidth;
}