Index: lib/ubsan/ubsan_diag.h =================================================================== --- lib/ubsan/ubsan_diag.h +++ lib/ubsan/ubsan_diag.h @@ -169,7 +169,7 @@ }; private: - static const unsigned MaxArgs = 5; + static const unsigned MaxArgs = 7; static const unsigned MaxRanges = 1; /// The arguments which have been added to this diagnostic so far. Index: lib/ubsan/ubsan_handlers.cc =================================================================== --- lib/ubsan/ubsan_handlers.cc +++ lib/ubsan/ubsan_handlers.cc @@ -102,6 +102,75 @@ Die(); } +/// \brief Check if a signed integer arithmetic operation will overflow. +static bool checkForSignedOverflow(const Value &L, const char *Operator, + const Value &R) { + SIntMax LHS = L.getSIntValue(); //< Ln = LHS if LHS < 0, otherwise Lp = LHS + SIntMax RHS = R.getSIntValue(); //< Rn = RHS if RHS < 0, otherwise Rp = RHS + SIntMax UpperLimit = + (SIntMax(1) << (L.getType().getIntegerBitWidth() - 1)) - 1; //< U + SIntMax LowerLimit = -UpperLimit - 1; //< L + + switch (Operator[0]) { + case '+': { + if (LHS > 0 && RHS > 0) + return RHS > (UpperLimit - LHS); // Lp + Rp > U => Rp > U - Lp + else if (LHS < 0 && RHS < 0) + return RHS < (LowerLimit - LHS); // Ln + Rn < L => Rn < L - Ln + return false; // (Lp + Rn) and (Ln + Rp) can't overflow. + } + case '-': { + if (LHS > 0 && RHS < 0) + return LHS > (UpperLimit + RHS); // Lp - Rn > U => Lp > U + Rn + else if (LHS < 0 && RHS > 0) + return LHS < (LowerLimit + RHS); // Ln - Rp < L => Ln < L + Rp + return false; // (Lp - Rp) and (Ln - Rn) can't overflow. + } + case '*': { + if (LHS > 0 && RHS > 0) + return LHS > (UpperLimit / RHS); // Lp * Rp > U => Lp > U/Rp + else if (LHS < 0 && RHS < 0) + return LHS < (UpperLimit / RHS); // Ln * Rn > U => Ln < U/Rn + else if (LHS > 0 && RHS < 0) + return LHS > (LowerLimit / RHS); // Lp * Rn < L => Lp > L/Rn + else if (LHS < 0 && RHS > 0) + return LHS < (LowerLimit / RHS); // Ln * Rp < L => Ln < L/Rp + return false; // Multiplication by zero can't overflow. + } + default: + UNREACHABLE("unexpected arithmetic operator!"); + } +} + +/// \brief Check if an unsigned integer arithmetic operation will overflow. +static bool checkForUnsignedOverflow(const Value &L, const char *Operator, + const Value &R) { + UIntMax LHS = L.getUIntValue(); //< L + UIntMax RHS = R.getUIntValue(); //< R + UIntMax UpperLimit = ~UIntMax(0) >> (8 * sizeof(UIntMax) - + L.getType().getIntegerBitWidth()); //< U + + switch (Operator[0]) { + case '+': + return LHS > (UpperLimit - RHS); // L + R > U => L > U - R + case '-': + return LHS < RHS; // L - R < 0 => L < R + case '*': + return (RHS == 0) ? false + : (LHS > UpperLimit / RHS); // L * R > U => L > U/R + default: + UNREACHABLE("unexpected arithmetic operator!"); + } +} + +/// \brief Check if an integer arithmetic operation will overflow. +static bool hasIntegerOverflow(bool IsSigned, const Value &L, + const char *Operator, const Value &R) { + if (IsSigned) + return checkForSignedOverflow(L, Operator, R); + return checkForUnsignedOverflow(L, Operator, R); +} + /// \brief Common diagnostic emission for various forms of integer overflow. static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS, const char *Operator, const Value &RHSVal, @@ -116,10 +185,14 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, "%0 integer overflow: " - "%1 %2 %3 cannot be represented in type %4") - << (IsSigned ? "signed" : "unsigned") - << Value(Data->Type, LHS) << Operator << RHSVal << Data->Type; + Value LHSVal{Data->Type, LHS}; + bool WouldOverflow = hasIntegerOverflow(IsSigned, LHSVal, Operator, RHSVal); + + Diag(Loc, DL_Error, "%0%1 integer overflow: " + "%2 %3 %4 cannot be represented in type %5%6") + << (WouldOverflow ? "" : "possible ") + << (IsSigned ? "signed" : "unsigned") << LHSVal << Operator << RHSVal + << Data->Type << (WouldOverflow ? "" : " (values optimized away)"); } #define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \ Index: test/ubsan/TestCases/Integer/Inputs/undef-overflow.input.ll =================================================================== --- /dev/null +++ test/ubsan/TestCases/Integer/Inputs/undef-overflow.input.ll @@ -0,0 +1,67 @@ +; This file is used to generate undef-overflow.bc. To update the bitcode file, +; first edit this file and then run: +; +; opt undef-overflow.input.ll -o undef-overflow.bc + +@.src = private unnamed_addr constant [8 x i8] c"undef.c\00", align 1 + +;; Test overflow detection on 8-bit signed/unsigned integers. +@c_i8 = private unnamed_addr constant { i16, i16, [5 x i8] } { i16 0, i16 7, [5 x i8] c"'i8'\00" } +@c_u8 = private unnamed_addr constant { i16, i16, [5 x i8] } { i16 0, i16 6, [5 x i8] c"'u8'\00" } + +;; We need duplicate typeinfo data to work around report deduplication... +@ti_i8_1 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 1 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_2 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 2 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_3 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 3 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_4 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 4 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_5 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 5 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_6 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 6 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_7 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 7 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_8 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 8 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_9 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 9 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_10 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 10 }, { i16, i16, [5 x i8] }* @c_i8 } +@ti_i8_11 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 11 }, { i16, i16, [5 x i8] }* @c_i8 } + +@ti_u8_1 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 12 }, { i16, i16, [5 x i8] }* @c_u8 } +@ti_u8_2 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 13 }, { i16, i16, [5 x i8] }* @c_u8 } +@ti_u8_3 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 14 }, { i16, i16, [5 x i8] }* @c_u8 } +@ti_u8_4 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 15 }, { i16, i16, [5 x i8] }* @c_u8 } +@ti_u8_5 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 16 }, { i16, i16, [5 x i8] }* @c_u8 } +@ti_u8_6 = private unnamed_addr global { { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* } { { [8 x i8]*, i32, i32 } { [8 x i8]* @.src, i32 1, i32 17 }, { i16, i16, [5 x i8] }* @c_u8 } + +define i32 @main() { + ;; Test that signed overflow isn't reported. + call void @__ubsan_handle_add_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_1 to i8*), i64 0, i64 0), !nosanitize !0 + call void @__ubsan_handle_sub_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_2 to i8*), i64 0, i64 0), !nosanitize !0 + call void @__ubsan_handle_mul_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_3 to i8*), i64 0, i64 0), !nosanitize !0 + + ;; Test that unsigned overflow isn't reported. + call void @__ubsan_handle_add_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_u8_1 to i8*), i64 0, i64 0), !nosanitize !0 + call void @__ubsan_handle_sub_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_u8_2 to i8*), i64 0, i64 0), !nosanitize !0 + call void @__ubsan_handle_mul_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_u8_3 to i8*), i64 0, i64 0), !nosanitize !0 + + ;; Test that signed overflow *is* reported. + call void @__ubsan_handle_add_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_4 to i8*), i64 127, i64 1), !nosanitize !0 + call void @__ubsan_handle_add_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_5 to i8*), i64 -128, i64 -1), !nosanitize !0 + + call void @__ubsan_handle_sub_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_6 to i8*), i64 127, i64 -1), !nosanitize !0 + call void @__ubsan_handle_sub_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_7 to i8*), i64 -128, i64 1), !nosanitize !0 + + call void @__ubsan_handle_mul_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_8 to i8*), i64 127, i64 2), !nosanitize !0 + call void @__ubsan_handle_mul_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_9 to i8*), i64 127, i64 -2), !nosanitize !0 + call void @__ubsan_handle_mul_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_10 to i8*), i64 -2, i64 127), !nosanitize !0 + call void @__ubsan_handle_mul_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_i8_11 to i8*), i64 -2, i64 -127), !nosanitize !0 + + ;; Test that unsigned overflow *is* reported. + call void @__ubsan_handle_add_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_u8_4 to i8*), i64 255, i64 1), !nosanitize !0 + call void @__ubsan_handle_sub_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_u8_5 to i8*), i64 1, i64 2), !nosanitize !0 + call void @__ubsan_handle_mul_overflow(i8* bitcast ({ { [8 x i8]*, i32, i32 }, { i16, i16, [5 x i8] }* }* @ti_u8_6 to i8*), i64 128, i64 2), !nosanitize !0 + + ret i32 0 +} + +declare void @__ubsan_handle_add_overflow(i8*, i64, i64) +declare void @__ubsan_handle_sub_overflow(i8*, i64, i64) +declare void @__ubsan_handle_mul_overflow(i8*, i64, i64) + +!0 = !{} Index: test/ubsan/TestCases/Integer/undef-overflow.cpp =================================================================== --- /dev/null +++ test/ubsan/TestCases/Integer/undef-overflow.cpp @@ -0,0 +1,25 @@ +// RUN: %clangxx -Wno-override-module -fsanitize=signed-integer-overflow,unsigned-integer-overflow %S/Inputs/undef-overflow.bc -o %t && %run %t 2>&1 | FileCheck %s + +// The size of ubsan's ValueHandle is architecture-dependent. This test depends +// on LLVM IR to get decent coverage, which in turn depends on the size of the +// ValueHandle. Restrict the test to one architecture to avoid breakage. +// REQUIRES: x86_64-target-arch + +// CHECK: possible signed integer overflow: 0 + 0 cannot be represented in type 'i8' (values optimized away) +// CHECK: possible signed integer overflow: 0 - 0 cannot be represented in type 'i8' (values optimized away) +// CHECK: possible signed integer overflow: 0 * 0 cannot be represented in type 'i8' (values optimized away) +// CHECK: possible unsigned integer overflow: 0 + 0 cannot be represented in type 'u8' (values optimized away) +// CHECK: possible unsigned integer overflow: 0 - 0 cannot be represented in type 'u8' (values optimized away) +// CHECK: possible unsigned integer overflow: 0 * 0 cannot be represented in type 'u8' (values optimized away) + +// CHECK: signed integer overflow: 127 + 1 cannot be represented in type 'i8' +// CHECK: signed integer overflow: -128 + -1 cannot be represented in type 'i8' +// CHECK: signed integer overflow: 127 - -1 cannot be represented in type 'i8' +// CHECK: signed integer overflow: -128 - 1 cannot be represented in type 'i8' +// CHECK: signed integer overflow: 127 * 2 cannot be represented in type 'i8' +// CHECK: signed integer overflow: 127 * -2 cannot be represented in type 'i8' +// CHECK: signed integer overflow: -2 * 127 cannot be represented in type 'i8' +// CHECK: signed integer overflow: -2 * -127 cannot be represented in type 'i8' +// CHECK: unsigned integer overflow: 255 + 1 cannot be represented in type 'u8' +// CHECK: unsigned integer overflow: 1 - 2 cannot be represented in type 'u8' +// CHECK: unsigned integer overflow: 128 * 2 cannot be represented in type 'u8'