diff options
author | Roman Lebedev <lebedev.ri@gmail.com> | 2019-10-10 09:25:02 +0000 |
---|---|---|
committer | Roman Lebedev <lebedev.ri@gmail.com> | 2019-10-10 09:25:02 +0000 |
commit | 414670be4ec1c8599fce3a40f5dd4914e9317f55 (patch) | |
tree | 97f1b677136f1dc208a47d2bd78611cac8baaa22 | |
parent | 1b1167f58f9cf653947777493c0fc4fbbaa103c5 (diff) | |
download | clang-414670be4ec1c8599fce3a40f5dd4914e9317f55.tar.gz |
[UBSan][clang][compiler-rt] Applying non-zero offset to nullptr is undefined behaviour
Summary:
Quote from http://eel.is/c++draft/expr.add#4:
```
4 When an expression J that has integral type is added to or subtracted
from an expression P of pointer type, the result has the type of P.
(4.1) If P evaluates to a null pointer value and J evaluates to 0,
the result is a null pointer value.
(4.2) Otherwise, if P points to an array element i of an array object x with n
elements ([dcl.array]), the expressions P + J and J + P
(where J has the value j) point to the (possibly-hypothetical) array
element i+j of x if 0≤i+j≤n and the expression P - J points to the
(possibly-hypothetical) array element i−j of x if 0≤i−j≤n.
(4.3) Otherwise, the behavior is undefined.
```
Therefore, as per the standard, applying non-zero offset to `nullptr`
(or making non-`nullptr` a `nullptr`, by subtracting pointer's integral value
from the pointer itself) is undefined behavior. (*if* `nullptr` is not defined,
i.e. e.g. `-fno-delete-null-pointer-checks` was *not* specified.)
To make things more fun, in C (6.5.6p8), applying *any* offset to null pointer
is undefined, although Clang front-end pessimizes the code by not lowering
that info, so this UB is "harmless".
Since rL369789 (D66608 `[InstCombine] icmp eq/ne (gep inbounds P, Idx..), null -> icmp eq/ne P, null`)
LLVM middle-end uses those guarantees for transformations.
If the source contains such UB's, said code may now be miscompiled.
Such miscompilations were already observed:
* https://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20190826/687838.html
* https://github.com/google/filament/pull/1566
Surprisingly, UBSan does not catch those issues
... until now. This diff teaches UBSan about these UB's.
`getelementpointer inbounds` is a pretty frequent instruction,
so this does have a measurable impact on performance;
I've addressed most of the obvious missing folds (and thus decreased the performance impact by ~5%),
and then re-performed some performance measurements using my [[ https://github.com/darktable-org/rawspeed | RawSpeed ]] benchmark:
(all measurements done with LLVM ToT, the sanitizer never fired.)
* no sanitization vs. existing check: average `+21.62%` slowdown
* existing check vs. check after this patch: average `22.04%` slowdown
* no sanitization vs. this patch: average `48.42%` slowdown
Reviewers: vsk, filcab, rsmith, aaron.ballman, vitalybuka, rjmccall, #sanitizers
Reviewed By: rsmith
Subscribers: kristof.beyls, nickdesaulniers, nikic, ychen, dtzWill, xbolva00, dberris, arphaman, rupprecht, reames, regehr, llvm-commits, cfe-commits
Tags: #clang, #sanitizers, #llvm
Differential Revision: https://reviews.llvm.org/D67122
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@374293 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | docs/ReleaseNotes.rst | 45 | ||||
-rw-r--r-- | docs/UndefinedBehaviorSanitizer.rst | 3 | ||||
-rw-r--r-- | lib/CodeGen/CGExprScalar.cpp | 149 | ||||
-rw-r--r-- | test/CodeGen/catch-nullptr-and-nonzero-offset-blacklist.c | 34 | ||||
-rw-r--r-- | test/CodeGen/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.c | 35 | ||||
-rw-r--r-- | test/CodeGen/catch-nullptr-and-nonzero-offset-when-nullptr-is-defined.c | 66 | ||||
-rw-r--r-- | test/CodeGen/catch-nullptr-and-nonzero-offset.c | 421 | ||||
-rw-r--r-- | test/CodeGen/catch-pointer-overflow-volatile.c | 54 | ||||
-rw-r--r-- | test/CodeGen/catch-pointer-overflow.c | 294 | ||||
-rw-r--r-- | test/CodeGen/ubsan-pointer-overflow.c | 123 | ||||
-rw-r--r-- | test/CodeGen/ubsan-pointer-overflow.m | 193 | ||||
-rw-r--r-- | test/CodeGenCXX/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.cpp | 24 |
12 files changed, 1196 insertions, 245 deletions
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index d6c734a53a..8e9d298c95 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -60,6 +60,16 @@ Improvements to Clang's diagnostics Non-comprehensive list of changes in this release ------------------------------------------------- +* In both C and C++ (C17 ``6.5.6p8``, C++ ``[expr.add]``), pointer arithmetic is + only permitted within arrays. In particular, the behavior of a program is not + defined if it adds a non-zero offset (or in C, any offset) to a null pointer, + or if it forms a null pointer by subtracting an integer from a non-null + pointer, and the LLVM optimizer now uses those guarantees for transformations. + This may lead to unintended behavior in code that performs these operations. + The Undefined Behavior Sanitizer ``-fsanitize=pointer-overflow`` check has + been extended to detect these cases, so that code relying on them can be + detected and fixed. + - For X86 target, -march=skylake-avx512, -march=icelake-client, -march=icelake-server, -march=cascadelake, -march=cooperlake will default to not using 512-bit zmm registers in vectorized code unless 512-bit intrinsics @@ -238,7 +248,40 @@ Static Analyzer Undefined Behavior Sanitizer (UBSan) ------------------------------------ -- ... +- * The ``pointer-overflow`` check was extended added to catch the cases where + a non-zero offset is applied to a null pointer, or the result of + applying the offset is a null pointer. + + .. code-block:: c++ + + #include <cstdint> // for intptr_t + + static char *getelementpointer_inbounds(char *base, unsigned long offset) { + // Potentially UB. + return base + offset; + } + + char *getelementpointer_unsafe(char *base, unsigned long offset) { + // Always apply offset. UB if base is ``nullptr`` and ``offset`` is not + // zero, or if ``base`` is non-``nullptr`` and ``offset`` is + // ``-reinterpret_cast<intptr_t>(base)``. + return getelementpointer_inbounds(base, offset); + } + + char *getelementpointer_safe(char *base, unsigned long offset) { + // Cast pointer to integer, perform usual arithmetic addition, + // and cast to pointer. This is legal. + char *computed = + reinterpret_cast<char *>(reinterpret_cast<intptr_t>(base) + offset); + // If either the pointer becomes non-``nullptr``, or becomes + // ``nullptr``, we must use ``computed`` result. + if (((base == nullptr) && (computed != nullptr)) || + ((base != nullptr) && (computed == nullptr))) + return computed; + // Else we can use ``getelementpointer_inbounds()``. + return getelementpointer_inbounds(base, offset); + } + Core Analysis Improvements ========================== diff --git a/docs/UndefinedBehaviorSanitizer.rst b/docs/UndefinedBehaviorSanitizer.rst index 06d533f493..0f6a42a211 100644 --- a/docs/UndefinedBehaviorSanitizer.rst +++ b/docs/UndefinedBehaviorSanitizer.rst @@ -130,7 +130,8 @@ Available checks are: ``__builtin_object_size``, and consequently may be able to detect more problems at higher optimization levels. - ``-fsanitize=pointer-overflow``: Performing pointer arithmetic which - overflows. + overflows, or where either the old or new pointer value is a null pointer + (or in C, when they both are). - ``-fsanitize=return``: In C++, reaching the end of a value-returning function without returning a value. - ``-fsanitize=returns-nonnull-attribute``: Returning null pointer diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 19b1cedf32..0da7a19deb 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -4542,7 +4542,7 @@ struct GEPOffsetAndOverflow { llvm::Value *OffsetOverflows; }; -/// Evaluate given GEPVal, which must be an inbounds GEP, +/// Evaluate given GEPVal, which is either an inbounds GEP, or a constant, /// and compute the total offset it applies from it's base pointer BasePtr. /// Returns offset in bytes and a boolean flag whether an overflow happened /// during evaluation. @@ -4550,10 +4550,28 @@ static GEPOffsetAndOverflow EmitGEPOffsetInBytes(Value *BasePtr, Value *GEPVal, llvm::LLVMContext &VMContext, CodeGenModule &CGM, CGBuilderTy Builder) { + const auto &DL = CGM.getDataLayout(); + + // The total (signed) byte offset for the GEP. + llvm::Value *TotalOffset = nullptr; + + // Was the GEP already reduced to a constant? + if (isa<llvm::Constant>(GEPVal)) { + // Compute the offset by casting both pointers to integers and subtracting: + // GEPVal = BasePtr + ptr(Offset) <--> Offset = int(GEPVal) - int(BasePtr) + Value *BasePtr_int = + Builder.CreatePtrToInt(BasePtr, DL.getIntPtrType(BasePtr->getType())); + Value *GEPVal_int = + Builder.CreatePtrToInt(GEPVal, DL.getIntPtrType(GEPVal->getType())); + TotalOffset = Builder.CreateSub(GEPVal_int, BasePtr_int); + return {TotalOffset, /*OffsetOverflows=*/Builder.getFalse()}; + } + auto *GEP = cast<llvm::GEPOperator>(GEPVal); + assert(GEP->getPointerOperand() == BasePtr && + "BasePtr must be the the base of the GEP."); assert(GEP->isInBounds() && "Expected inbounds GEP"); - const auto &DL = CGM.getDataLayout(); auto *IntPtrTy = DL.getIntPtrType(GEP->getPointerOperandType()); // Grab references to the signed add/mul overflow intrinsics for intptr_t. @@ -4563,8 +4581,6 @@ static GEPOffsetAndOverflow EmitGEPOffsetInBytes(Value *BasePtr, Value *GEPVal, auto *SMulIntrinsic = CGM.getIntrinsic(llvm::Intrinsic::smul_with_overflow, IntPtrTy); - // The total (signed) byte offset for the GEP. - llvm::Value *TotalOffset = nullptr; // The offset overflow flag - true if the total offset overflows. llvm::Value *OffsetOverflows = Builder.getFalse(); @@ -4635,69 +4651,102 @@ CodeGenFunction::EmitCheckedInBoundsGEP(Value *Ptr, ArrayRef<Value *> IdxList, if (!SanOpts.has(SanitizerKind::PointerOverflow)) return GEPVal; - // If the GEP has already been reduced to a constant, leave it be. - if (isa<llvm::Constant>(GEPVal)) - return GEPVal; + llvm::Type *PtrTy = Ptr->getType(); + + // Perform nullptr-and-offset check unless the nullptr is defined. + bool PerformNullCheck = !NullPointerIsDefined( + Builder.GetInsertBlock()->getParent(), PtrTy->getPointerAddressSpace()); + // Check for overflows unless the GEP got constant-folded, + // and only in the default address space + bool PerformOverflowCheck = + !isa<llvm::Constant>(GEPVal) && PtrTy->getPointerAddressSpace() == 0; - // Only check for overflows in the default address space. - if (GEPVal->getType()->getPointerAddressSpace()) + if (!(PerformNullCheck || PerformOverflowCheck)) return GEPVal; + const auto &DL = CGM.getDataLayout(); + SanitizerScope SanScope(this); + llvm::Type *IntPtrTy = DL.getIntPtrType(PtrTy); GEPOffsetAndOverflow EvaluatedGEP = EmitGEPOffsetInBytes(Ptr, GEPVal, getLLVMContext(), CGM, Builder); - auto *GEP = cast<llvm::GEPOperator>(GEPVal); - - const auto &DL = CGM.getDataLayout(); - auto *IntPtrTy = DL.getIntPtrType(GEP->getPointerOperandType()); + assert((!isa<llvm::Constant>(EvaluatedGEP.TotalOffset) || + EvaluatedGEP.OffsetOverflows == Builder.getFalse()) && + "If the offset got constant-folded, we don't expect that there was an " + "overflow."); auto *Zero = llvm::ConstantInt::getNullValue(IntPtrTy); - // Common case: if the total offset is zero, don't emit a check. - if (EvaluatedGEP.TotalOffset == Zero) + // Common case: if the total offset is zero, and we are using C++ semantics, + // where nullptr+0 is defined, don't emit a check. + if (EvaluatedGEP.TotalOffset == Zero && CGM.getLangOpts().CPlusPlus) return GEPVal; // Now that we've computed the total offset, add it to the base pointer (with // wrapping semantics). - auto *IntPtr = Builder.CreatePtrToInt(GEP->getPointerOperand(), IntPtrTy); + auto *IntPtr = Builder.CreatePtrToInt(Ptr, IntPtrTy); auto *ComputedGEP = Builder.CreateAdd(IntPtr, EvaluatedGEP.TotalOffset); - llvm::SmallVector<std::pair<llvm::Value *, SanitizerMask>, 1> Checks; - - // The GEP is valid if: - // 1) The total offset doesn't overflow, and - // 2) The sign of the difference between the computed address and the base - // pointer matches the sign of the total offset. - llvm::Value *ValidGEP; - auto *NoOffsetOverflow = Builder.CreateNot(EvaluatedGEP.OffsetOverflows); - if (SignedIndices) { - // GEP is computed as `unsigned base + signed offset`, therefore: - // * If offset was positive, then the computed pointer can not be - // [unsigned] less than the base pointer, unless it overflowed. - // * If offset was negative, then the computed pointer can not be - // [unsigned] greater than the bas pointere, unless it overflowed. - auto *PosOrZeroValid = Builder.CreateICmpUGE(ComputedGEP, IntPtr); - auto *PosOrZeroOffset = - Builder.CreateICmpSGE(EvaluatedGEP.TotalOffset, Zero); - llvm::Value *NegValid = Builder.CreateICmpULT(ComputedGEP, IntPtr); - ValidGEP = Builder.CreateSelect(PosOrZeroOffset, PosOrZeroValid, NegValid); - } else if (!IsSubtraction) { - // GEP is computed as `unsigned base + unsigned offset`, therefore the - // computed pointer can not be [unsigned] less than base pointer, - // unless there was an overflow. - // Equivalent to `@llvm.uadd.with.overflow(%base, %offset)`. - ValidGEP = Builder.CreateICmpUGE(ComputedGEP, IntPtr); - } else { - // GEP is computed as `unsigned base - unsigned offset`, therefore the - // computed pointer can not be [unsigned] greater than base pointer, - // unless there was an overflow. - // Equivalent to `@llvm.usub.with.overflow(%base, sub(0, %offset))`. - ValidGEP = Builder.CreateICmpULE(ComputedGEP, IntPtr); - } - ValidGEP = Builder.CreateAnd(ValidGEP, NoOffsetOverflow); - Checks.emplace_back(ValidGEP, SanitizerKind::PointerOverflow); + llvm::SmallVector<std::pair<llvm::Value *, SanitizerMask>, 2> Checks; + + if (PerformNullCheck) { + // In C++, if the base pointer evaluates to a null pointer value, + // the only valid pointer this inbounds GEP can produce is also + // a null pointer, so the offset must also evaluate to zero. + // Likewise, if we have non-zero base pointer, we can not get null pointer + // as a result, so the offset can not be -intptr_t(BasePtr). + // In other words, both pointers are either null, or both are non-null, + // or the behaviour is undefined. + // + // C, however, is more strict in this regard, and gives more + // optimization opportunities: in C, additionally, nullptr+0 is undefined. + // So both the input to the 'gep inbounds' AND the output must not be null. + auto *BaseIsNotNullptr = Builder.CreateIsNotNull(Ptr); + auto *ResultIsNotNullptr = Builder.CreateIsNotNull(ComputedGEP); + auto *Valid = + CGM.getLangOpts().CPlusPlus + ? Builder.CreateICmpEQ(BaseIsNotNullptr, ResultIsNotNullptr) + : Builder.CreateAnd(BaseIsNotNullptr, ResultIsNotNullptr); + Checks.emplace_back(Valid, SanitizerKind::PointerOverflow); + } + + if (PerformOverflowCheck) { + // The GEP is valid if: + // 1) The total offset doesn't overflow, and + // 2) The sign of the difference between the computed address and the base + // pointer matches the sign of the total offset. + llvm::Value *ValidGEP; + auto *NoOffsetOverflow = Builder.CreateNot(EvaluatedGEP.OffsetOverflows); + if (SignedIndices) { + // GEP is computed as `unsigned base + signed offset`, therefore: + // * If offset was positive, then the computed pointer can not be + // [unsigned] less than the base pointer, unless it overflowed. + // * If offset was negative, then the computed pointer can not be + // [unsigned] greater than the bas pointere, unless it overflowed. + auto *PosOrZeroValid = Builder.CreateICmpUGE(ComputedGEP, IntPtr); + auto *PosOrZeroOffset = + Builder.CreateICmpSGE(EvaluatedGEP.TotalOffset, Zero); + llvm::Value *NegValid = Builder.CreateICmpULT(ComputedGEP, IntPtr); + ValidGEP = + Builder.CreateSelect(PosOrZeroOffset, PosOrZeroValid, NegValid); + } else if (!IsSubtraction) { + // GEP is computed as `unsigned base + unsigned offset`, therefore the + // computed pointer can not be [unsigned] less than base pointer, + // unless there was an overflow. + // Equivalent to `@llvm.uadd.with.overflow(%base, %offset)`. + ValidGEP = Builder.CreateICmpUGE(ComputedGEP, IntPtr); + } else { + // GEP is computed as `unsigned base - unsigned offset`, therefore the + // computed pointer can not be [unsigned] greater than base pointer, + // unless there was an overflow. + // Equivalent to `@llvm.usub.with.overflow(%base, sub(0, %offset))`. + ValidGEP = Builder.CreateICmpULE(ComputedGEP, IntPtr); + } + ValidGEP = Builder.CreateAnd(ValidGEP, NoOffsetOverflow); + Checks.emplace_back(ValidGEP, SanitizerKind::PointerOverflow); + } assert(!Checks.empty() && "Should have produced some checks."); diff --git a/test/CodeGen/catch-nullptr-and-nonzero-offset-blacklist.c b/test/CodeGen/catch-nullptr-and-nonzero-offset-blacklist.c new file mode 100644 index 0000000000..3cf53c86de --- /dev/null +++ b/test/CodeGen/catch-nullptr-and-nonzero-offset-blacklist.c @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-NULL-IS-INVALID-PTR +// RUN: %clang_cc1 -x c -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-NULL-IS-VALID-PTR + +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-NULL-IS-INVALID-PTR +// RUN: %clang_cc1 -x c++ -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-NULL-IS-VALID-PTR + +#ifdef __cplusplus +extern "C" { +#endif + +// CHECK-LABEL: @baseline +char *baseline(char *base, unsigned long offset) { + // CHECK: call void @__ubsan_handle_pointer_overflow( + return base + offset; +} + +// CHECK-LABEL: @blacklist_0 +__attribute__((no_sanitize("undefined"))) char *blacklist_0(char *base, unsigned long offset) { + return base + offset; +} + +// CHECK-LABEL: @blacklist_1 +__attribute__((no_sanitize("pointer-overflow"))) char *blacklist_1(char *base, unsigned long offset) { + return base + offset; +} + +// CHECK-LABEL: @ignore_non_default_address_space +__attribute__((address_space(1))) char *ignore_non_default_address_space(__attribute__((address_space(1))) char *base, unsigned long offset) { + return base + offset; +} + +#ifdef __cplusplus +} +#endif diff --git a/test/CodeGen/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.c b/test/CodeGen/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.c new file mode 100644 index 0000000000..09d644a859 --- /dev/null +++ b/test/CodeGen/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -x c -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s + +// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct S { + int x, y; +}; + +// CHECK-LABEL: define i64 @{{.*}}get_offset_of_y_naively{{.*}}( +uintptr_t get_offset_of_y_naively() { + // CHECK: [[ENTRY:.*]]: + // CHECK-NEXT: ret i64 ptrtoint (i32* getelementptr (i32, i32* null, i32 1) to i64) + // CHECK-NEXT: } + return ((uintptr_t)(&(((struct S *)0)->y))); +} + +// CHECK-LABEL: define i64 @{{.*}}get_offset_of_y_via_builtin{{.*}}( +uintptr_t get_offset_of_y_via_builtin() { + // CHECK: [[ENTRY:.*]]: + // CHECK-NEXT: ret i64 4 + // CHECK-NEXT: } + return __builtin_offsetof(struct S, y); +} + +#ifdef __cplusplus +} +#endif diff --git a/test/CodeGen/catch-nullptr-and-nonzero-offset-when-nullptr-is-defined.c b/test/CodeGen/catch-nullptr-and-nonzero-offset-when-nullptr-is-defined.c new file mode 100644 index 0000000000..3f1e2250d3 --- /dev/null +++ b/test/CodeGen/catch-nullptr-and-nonzero-offset-when-nullptr-is-defined.c @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -x c -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLNOTOK,CHECK-SANITIZE-NULLNOTOK-C,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLNOTOK,CHECK-SANITIZE-NULLNOTOK-C,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLNOTOK,CHECK-SANITIZE-NULLNOTOK-C,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE + +// RUN: %clang_cc1 -x c -fno-delete-null-pointer-checks -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLOK,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE +// RUN: %clang_cc1 -x c -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLOK,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER +// RUN: %clang_cc1 -x c -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLOK,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE + +// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLNOTOK,CHECK-SANITIZE-NULLNOTOK-CPP,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLNOTOK,CHECK-SANITIZE-NULLNOTOK-CPP,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLNOTOK,CHECK-SANITIZE-NULLNOTOK-CPP,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE + +// RUN: %clang_cc1 -x c++ -fno-delete-null-pointer-checks -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c++ -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLOK,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE +// RUN: %clang_cc1 -x c++ -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLOK,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER +// RUN: %clang_cc1 -x c++ -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-NULLOK,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE + +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 15 } } + +#ifdef __cplusplus +extern "C" { +#endif + +char *add_unsigned(char *base, unsigned long offset) { + // CHECK: define i8* @add_unsigned(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[OFFSET_RELOADED]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NULLNOTOK-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NULLNOTOK-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-NULLNOTOK-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NULLNOTOK-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NULLNOTOK-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NULLNOTOK-NEXT: br i1 %[[GEP_IS_OKAY]], + // CHECK-SANITIZE-NULLOK-NEXT: br i1 %[[GEP_DID_NOT_OVERFLOW]], + // CHECK-SANITIZE-SAME: label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] +#line 100 + return base + offset; +} + +#ifdef __cplusplus +} +#endif diff --git a/test/CodeGen/catch-nullptr-and-nonzero-offset.c b/test/CodeGen/catch-nullptr-and-nonzero-offset.c new file mode 100644 index 0000000000..60283e76dd --- /dev/null +++ b/test/CodeGen/catch-nullptr-and-nonzero-offset.c @@ -0,0 +1,421 @@ +// RUN: %clang_cc1 -x c -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE,CHECK-SANITIZE-C,CHECK-SANITIZE-ANYRECOVER-C,CHECK-SANITIZE-NORECOVER-C,CHECK-SANITIZE-UNREACHABLE-C +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER,CHECK-SANITIZE-C,CHECK-SANITIZE-ANYRECOVER-C,CHECK-SANITIZE-RECOVER-C +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE,CHECK-SANITIZE-C,CHECK-SANITIZE-TRAP-C,CHECK-SANITIZE-UNREACHABLE-C + +// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE,CHECK-SANITIZE-CPP,CHECK-SANITIZE-ANYRECOVER-CPP,CHECK-SANITIZE-NORECOVER-CPP,CHECK-SANITIZE-UNREACHABLE-CPP +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER,CHECK-SANITIZE-CPP,CHECK-SANITIZE-ANYRECOVER-CPP,CHECK-SANITIZE-RECOVER-CPP +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE,CHECK-SANITIZE-CPP,CHECK-SANITIZE-TRAP-CPP,CHECK-SANITIZE-UNREACHABLE-CPP + +// In C++/LLVM IR, if the base pointer evaluates to a null pointer value, +// the only valid pointer this inbounds GEP can produce is also a null pointer. +// Likewise, if we have non-zero base pointer, we can not get null pointer +// as a result, so the offset can not be -int(BasePtr). + +// So in other words, the offset can not change "null status" of the pointer, +// as in if the pointer was null, it can not become non-null, and vice versa, +// if it was non-null, it can not become null. + +// In C, however, offsetting null pointer is completely undefined, even by 0. + +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-C-DAG: @[[LINE_200:.*]] = {{.*}}, i32 200, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_300:.*]] = {{.*}}, i32 300, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_400:.*]] = {{.*}}, i32 400, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_500:.*]] = {{.*}}, i32 500, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-C-DAG: @[[LINE_600:.*]] = {{.*}}, i32 600, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_700:.*]] = {{.*}}, i32 700, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_800:.*]] = {{.*}}, i32 800, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_900:.*]] = {{.*}}, i32 900, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-C-DAG: @[[LINE_1000:.*]] = {{.*}}, i32 1000, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1100:.*]] = {{.*}}, i32 1100, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1200:.*]] = {{.*}}, i32 1200, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1300:.*]] = {{.*}}, i32 1300, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-C-DAG: @[[LINE_1400:.*]] = {{.*}}, i32 1400, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1500:.*]] = {{.*}}, i32 1500, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1600:.*]] = {{.*}}, i32 1600, i32 15 } } + +#ifdef __cplusplus +extern "C" { +#endif + +char *var_var(char *base, unsigned long offset) { + // CHECK: define i8* @var_var(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[OFFSET_RELOADED]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] +#line 100 + return base + offset; +} + +char *var_zero(char *base) { + // CHECK: define i8* @var_zero(i8* %[[BASE:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 0 + // CHECK-SANITIZE-C-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[COMPUTED_GEP_IS_UGE_BASE]], !nosanitize + // CHECK-SANITIZE-C-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-C: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-C-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_200]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-C-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_200]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-C-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-C-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE-C: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] + static const unsigned long offset = 0; +#line 200 + return base + offset; +} + +char *var_one(char *base) { + // CHECK: define i8* @var_one(i8* %[[BASE:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 1 + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[COMPUTED_GEP_IS_UGE_BASE]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_300]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_300]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] + static const unsigned long offset = 1; +#line 300 + return base + offset; +} + +char *var_allones(char *base) { + // CHECK: define i8* @var_allones(i8* %[[BASE:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 -1 + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], -1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[COMPUTED_GEP_IS_UGE_BASE]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_400]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_400]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] + static const unsigned long offset = -1; +#line 400 + return base + offset; +} + +//------------------------------------------------------------------------------ + +char *nullptr_var(unsigned long offset) { + // CHECK: define i8* @nullptr_var(i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* null, i64 %[[OFFSET_RELOADED]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 0, %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 false, %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 false, %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_500]] to i8*), i64 0, i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_500]] to i8*), i64 0, i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] + static char *const base = (char *)0; +#line 500 + return base + offset; +} + +char *nullptr_zero() { + // CHECK: define i8* @nullptr_zero() + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-SANITIZE-C-NEXT: br i1 false, label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-C: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-C-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_600]] to i8*), i64 0, i64 0) + // CHECK-SANITIZE-RECOVER-C-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_600]] to i8*), i64 0, i64 0) + // CHECK-SANITIZE-TRAP-C-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-C-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE-C: [[CONT]]: + // CHECK-NEXT: ret i8* null + static char *const base = (char *)0; + static const unsigned long offset = 0; +#line 600 + return base + offset; +} + +char *nullptr_one_BAD() { + // CHECK: define i8* @nullptr_one_BAD() + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-SANITIZE-C-NEXT: br i1 false, label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-CPP-NEXT: br i1 icmp eq (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* null, i64 1) to i64), i64 0), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_700]] to i8*), i64 0, i64 ptrtoint (i8* getelementptr inbounds (i8, i8* null, i64 1) to i64)) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_700]] to i8*), i64 0, i64 ptrtoint (i8* getelementptr inbounds (i8, i8* null, i64 1) to i64)) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* getelementptr inbounds (i8, i8* null, i64 1) + static char *const base = (char *)0; + static const unsigned long offset = 1; +#line 700 + return base + offset; +} + +char *nullptr_allones_BAD() { + // CHECK: define i8* @nullptr_allones_BAD() + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-SANITIZE-C-NEXT: br i1 false, label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-CPP-NEXT: br i1 icmp eq (i64 mul (i64 ptrtoint (i8* getelementptr (i8, i8* null, i32 1) to i64), i64 -1), i64 0), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_800]] to i8*), i64 0, i64 mul (i64 ptrtoint (i8* getelementptr (i8, i8* null, i32 1) to i64), i64 -1)) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_800]] to i8*), i64 0, i64 mul (i64 ptrtoint (i8* getelementptr (i8, i8* null, i32 1) to i64), i64 -1)) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* getelementptr inbounds (i8, i8* null, i64 -1) + static char *const base = (char *)0; + static const unsigned long offset = -1; +#line 800 + return base + offset; +} + +//------------------------------------------------------------------------------ + +char *one_var(unsigned long offset) { + // CHECK: define i8* @one_var(i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* inttoptr (i64 1 to i8*), i64 %[[OFFSET_RELOADED]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 1, %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 icmp ne (i8* inttoptr (i64 1 to i8*), i8* null), %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 icmp ne (i8* inttoptr (i64 1 to i8*), i8* null), %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_900]] to i8*), i64 1, i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_900]] to i8*), i64 1, i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] + static char *const base = (char *)1; +#line 900 + return base + offset; +} + +char *one_zero() { + // CHECK: define i8* @one_zero() + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-SANITIZE-C-NEXT: br i1 icmp ne (i8* inttoptr (i64 1 to i8*), i8* null), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-C: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-C-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1000]] to i8*), i64 1, i64 1) + // CHECK-SANITIZE-RECOVER-C-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1000]] to i8*), i64 1, i64 1) + // CHECK-SANITIZE-TRAP-C-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-C-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE-C: [[CONT]]: + // CHECK-NEXT: ret i8* inttoptr (i64 1 to i8*) + static char *const base = (char *)1; + static const unsigned long offset = 0; +#line 1000 + return base + offset; +} + +char *one_one_OK() { + // CHECK: define i8* @one_one_OK() + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-SANITIZE-C-NEXT: br i1 and (i1 icmp ne (i8* inttoptr (i64 1 to i8*), i8* null), i1 icmp ne (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 1) to i64), i64 1), i64 1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-CPP-NEXT: br i1 xor (i1 icmp eq (i8* inttoptr (i64 1 to i8*), i8* null), i1 icmp ne (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 1) to i64), i64 1), i64 1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1100]] to i8*), i64 1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 1) to i64), i64 1), i64 1)) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1100]] to i8*), i64 1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 1) to i64), i64 1), i64 1)) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 1) + static char *const base = (char *)1; + static const unsigned long offset = 1; +#line 1100 + return base + offset; +} + +char *one_allones_BAD() { + // CHECK: define i8* @one_allones_BAD() + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-SANITIZE-C-NEXT: br i1 and (i1 icmp ne (i8* inttoptr (i64 1 to i8*), i8* null), i1 icmp ne (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 -1) to i64), i64 1), i64 1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-CPP-NEXT: br i1 xor (i1 icmp eq (i8* inttoptr (i64 1 to i8*), i8* null), i1 icmp ne (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 -1) to i64), i64 1), i64 1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1200]] to i8*), i64 1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 -1) to i64), i64 1), i64 1)) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1200]] to i8*), i64 1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 -1) to i64), i64 1), i64 1)) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* getelementptr inbounds (i8, i8* inttoptr (i64 1 to i8*), i64 -1) + static char *const base = (char *)1; + static const unsigned long offset = -1; +#line 1200 + return base + offset; +} + +//------------------------------------------------------------------------------ + +char *allones_var(unsigned long offset) { + // CHECK: define i8* @allones_var(i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* inttoptr (i64 -1 to i8*), i64 %[[OFFSET_RELOADED]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 -1, %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 icmp ne (i8* inttoptr (i64 -1 to i8*), i8* null), %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 icmp ne (i8* inttoptr (i64 -1 to i8*), i8* null), %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], -1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1300]] to i8*), i64 -1, i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1300]] to i8*), i64 -1, i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] + static char *const base = (char *)-1; +#line 1300 + return base + offset; +} + +char *allones_zero_OK() { + // CHECK: define i8* @allones_zero_OK() + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-SANITIZE-C-NEXT: br i1 icmp ne (i8* inttoptr (i64 -1 to i8*), i8* null), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-C: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-C-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1400]] to i8*), i64 -1, i64 -1) + // CHECK-SANITIZE-RECOVER-C-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1400]] to i8*), i64 -1, i64 -1) + // CHECK-SANITIZE-TRAP-C-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-C-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE-C: [[CONT]]: + // CHECK-NEXT: ret i8* inttoptr (i64 -1 to i8*) + static char *const base = (char *)-1; + static const unsigned long offset = 0; +#line 1400 + return base + offset; +} + +char *allones_one_BAD() { + // CHECK: define i8* @allones_one_BAD() + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-SANITIZE-C-NEXT: br i1 and (i1 icmp ne (i8* inttoptr (i64 -1 to i8*), i8* null), i1 icmp ne (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 1) to i64), i64 -1), i64 -1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-CPP-NEXT: br i1 xor (i1 icmp eq (i8* inttoptr (i64 -1 to i8*), i8* null), i1 icmp ne (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 1) to i64), i64 -1), i64 -1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1500]] to i8*), i64 -1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 1) to i64), i64 -1), i64 -1)) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1500]] to i8*), i64 -1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 1) to i64), i64 -1), i64 -1)) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 1) + static char *const base = (char *)-1; + static const unsigned long offset = 1; +#line 1500 + return base + offset; +} + +char *allones_allones_OK() { + // CHECK: define i8* @allones_allones_OK() + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-SANITIZE-C-NEXT: br i1 and (i1 icmp ne (i8* inttoptr (i64 -1 to i8*), i8* null), i1 icmp ne (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 -1) to i64), i64 -1), i64 -1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE-CPP-NEXT: br i1 xor (i1 icmp eq (i8* inttoptr (i64 -1 to i8*), i8* null), i1 icmp ne (i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 -1) to i64), i64 -1), i64 -1), i64 0)), label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_1600]] to i8*), i64 -1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 -1) to i64), i64 -1), i64 -1)) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_1600]] to i8*), i64 -1, i64 add (i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 -1) to i64), i64 -1), i64 -1)) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* getelementptr inbounds (i8, i8* inttoptr (i64 -1 to i8*), i64 -1) + static char *const base = (char *)-1; + static const unsigned long offset = -1; +#line 1600 + return base + offset; +} + +#ifdef __cplusplus +} +#endif diff --git a/test/CodeGen/catch-pointer-overflow-volatile.c b/test/CodeGen/catch-pointer-overflow-volatile.c new file mode 100644 index 0000000000..138a3b660f --- /dev/null +++ b/test/CodeGen/catch-pointer-overflow-volatile.c @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -x c -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-C,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-C,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-C,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE + +// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-CPP,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-CPP,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-CPP,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE + +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 15 } } + +#ifdef __cplusplus +extern "C" { +#endif + +char *volatile_ptr(char *volatile base, unsigned long offset) { + // CHECK: define i8* @volatile_ptr(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store volatile i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load volatile i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[OFFSET_RELOADED]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] +#line 100 + return base + offset; +} + +#ifdef __cplusplus +} +#endif diff --git a/test/CodeGen/catch-pointer-overflow.c b/test/CodeGen/catch-pointer-overflow.c new file mode 100644 index 0000000000..19115e91ff --- /dev/null +++ b/test/CodeGen/catch-pointer-overflow.c @@ -0,0 +1,294 @@ +// RUN: %clang_cc1 -x c -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-C,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-C,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER +// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-C,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE + +// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-NOSANITIZE +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-CPP,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-CPP,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-CPP,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE + +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_200:.*]] = {{.*}}, i32 200, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_300:.*]] = {{.*}}, i32 300, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_400:.*]] = {{.*}}, i32 400, i32 15 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_500:.*]] = {{.*}}, i32 500, i32 7 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_600:.*]] = {{.*}}, i32 600, i32 7 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_700:.*]] = {{.*}}, i32 700, i32 3 } } +// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_800:.*]] = {{.*}}, i32 800, i32 3 } } + +#ifdef __cplusplus +extern "C" { +#endif + +char *add_unsigned(char *base, unsigned long offset) { + // CHECK: define i8* @add_unsigned(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[OFFSET_RELOADED]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_100]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] +#line 100 + return base + offset; +} + +char *sub_unsigned(char *base, unsigned long offset) { + // CHECK: define i8* @sub_unsigned(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[IDX_NEG:.*]] = sub i64 0, %[[OFFSET_RELOADED]] + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[IDX_NEG]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[IDX_NEG]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_ULE_BASE:.*]] = icmp ule i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_ULE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_200]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_200]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] +#line 200 + return base - offset; +} + +char *add_signed(char *base, signed long offset) { + // CHECK: define i8* @add_signed(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[OFFSET_RELOADED]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[POSORZEROVALID:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[POSORZEROOFFSET:.*]] = icmp sge i64 %[[COMPUTED_OFFSET]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[NEGVALID:.*]] = icmp ult i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[APPLYING_OFFSET_DID_NOT_OVERFLOW:.*]] = select i1 %[[POSORZEROOFFSET]], i1 %[[POSORZEROVALID]], i1 %[[NEGVALID]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[APPLYING_OFFSET_DID_NOT_OVERFLOW]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_300]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_300]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] +#line 300 + return base + offset; +} + +char *sub_signed(char *base, signed long offset) { + // CHECK: define i8* @sub_signed(i8* %[[BASE:.*]], i64 %[[OFFSET:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: store i64 %[[OFFSET]], i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, i64* %[[OFFSET_ADDR]], align 8 + // CHECK-NEXT: %[[IDX_NEG:.*]] = sub i64 0, %[[OFFSET_RELOADED]] + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i64 %[[IDX_NEG]] + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[IDX_NEG]]), !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], %[[COMPUTED_OFFSET]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[COMPUTED_OFFSET_OVERFLOWED]], true, !nosanitize + // CHECK-SANITIZE-NEXT: %[[POSORZEROVALID:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[POSORZEROOFFSET:.*]] = icmp sge i64 %[[COMPUTED_OFFSET]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[NEGVALID:.*]] = icmp ult i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[APPLYING_OFFSET_DID_NOT_OVERFLOW:.*]] = select i1 %[[POSORZEROOFFSET]], i1 %[[POSORZEROVALID]], i1 %[[NEGVALID]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[APPLYING_OFFSET_DID_NOT_OVERFLOW]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_400]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_400]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: ret i8* %[[ADD_PTR]] +#line 400 + return base - offset; +} + +char *postinc(char *base) { + // CHECK: define i8* @postinc(i8* %[[BASE:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i32 1 + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[COMPUTED_GEP_IS_UGE_BASE]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_500]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_500]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: store i8* %[[ADD_PTR]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: ret i8* %[[ADD_PTR_RELOADED]] +#line 500 + base++; + return base; +} + +char *postdec(char *base) { + // CHECK: define i8* @postdec(i8* %[[BASE:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i32 -1 + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], -1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_ULE_BASE:.*]] = icmp ule i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[COMPUTED_GEP_IS_ULE_BASE]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_600]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_600]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: store i8* %[[ADD_PTR]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: ret i8* %[[ADD_PTR_RELOADED]] +#line 600 + base--; + return base; +} + +char *preinc(char *base) { + // CHECK: define i8* @preinc(i8* %[[BASE:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i32 1 + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], 1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[COMPUTED_GEP_IS_UGE_BASE]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_700]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_700]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: store i8* %[[ADD_PTR]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: ret i8* %[[ADD_PTR_RELOADED]] +#line 700 + ++base; + return base; +} + +char *predec(char *base) { + // CHECK: define i8* @predec(i8* %[[BASE:.*]]) + // CHECK-NEXT: [[ENTRY:.*]]: + // CHECK-NEXT: %[[BASE_ADDR:.*]] = alloca i8*, align 8 + // CHECK-NEXT: store i8* %[[BASE]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[BASE_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[BASE_RELOADED]], i32 -1 + // CHECK-SANITIZE-NEXT: %[[BASE_RELOADED_INT:.*]] = ptrtoint i8* %[[BASE_RELOADED]] to i64, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 %[[BASE_RELOADED_INT]], -1, !nosanitize + // CHECK-SANITIZE-NEXT: %[[BASE_IS_NOT_NULLPTR:.*]] = icmp ne i8* %[[BASE_RELOADED]], null, !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize + // CHECK-SANITIZE-C-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = and i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-CPP-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[BASE_IS_NOT_NULLPTR]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_ULE_BASE:.*]] = icmp ule i64 %[[COMPUTED_GEP]], %[[BASE_RELOADED_INT]], !nosanitize + // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[COMPUTED_GEP_IS_ULE_BASE]], !nosanitize + // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize + // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]: + // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(i8* bitcast ({ {{{.*}}} }* @[[LINE_800]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(i8* bitcast ({ {{{.*}}} }* @[[LINE_800]] to i8*), i64 %[[BASE_RELOADED_INT]], i64 %[[COMPUTED_GEP]]) + // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize + // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize + // CHECK-SANITIZE: [[CONT]]: + // CHECK-NEXT: store i8* %[[ADD_PTR]], i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: %[[ADD_PTR_RELOADED:.*]] = load i8*, i8** %[[BASE_ADDR]], align 8 + // CHECK-NEXT: ret i8* %[[ADD_PTR_RELOADED]] +#line 800 + --base; + return base; +} + +#ifdef __cplusplus +} +#endif diff --git a/test/CodeGen/ubsan-pointer-overflow.c b/test/CodeGen/ubsan-pointer-overflow.c new file mode 100644 index 0000000000..7934442ad9 --- /dev/null +++ b/test/CodeGen/ubsan-pointer-overflow.c @@ -0,0 +1,123 @@ +// RUN: %clang_cc1 -x c -triple x86_64-apple-darwin10 -w -emit-llvm -o - %s -fsanitize=pointer-overflow | FileCheck %s --check-prefixes=CHECK,CHECK-C +// RUN: %clang_cc1 -x c++ -triple x86_64-apple-darwin10 -w -emit-llvm -o - %s -fsanitize=pointer-overflow | FileCheck %s --check-prefixes=CHECK,CHECK-CPP + +#ifdef __cplusplus +extern "C" { +#endif + +// CHECK-LABEL: define void @fixed_len_array +void fixed_len_array(int k) { + // CHECK: getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* [[ARR:%.*]], i64 0, i64 [[IDXPROM:%.*]] + // CHECK-NEXT: [[SMUL:%.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 40, i64 [[IDXPROM]]), !nosanitize + // CHECK-NEXT: [[SMULOFLOW:%.*]] = extractvalue { i64, i1 } [[SMUL]], 1, !nosanitize + // CHECK-NEXT: [[SMULVAL:%.*]] = extractvalue { i64, i1 } [[SMUL]], 0, !nosanitize + // CHECK-NEXT: [[BASE:%.*]] = ptrtoint [10 x [10 x i32]]* [[ARR]] to i64, !nosanitize + // CHECK-NEXT: [[COMPGEP:%.*]] = add i64 [[BASE]], [[SMULVAL]], !nosanitize + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}}, i64 [[BASE]], i64 [[COMPGEP]]){{.*}}, !nosanitize + + // CHECK: getelementptr inbounds [10 x i32], [10 x i32]* {{.*}}, i64 0, i64 [[IDXPROM1:%.*]] + // CHECK-NEXT: @llvm.smul.with.overflow.i64(i64 4, i64 [[IDXPROM1]]), !nosanitize + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + + int arr[10][10]; + arr[k][k]; +} + +// CHECK-LABEL: define void @variable_len_array +void variable_len_array(int n, int k) { + // CHECK: getelementptr inbounds i32, i32* {{.*}}, i64 [[IDXPROM:%.*]] + // CHECK-NEXT: @llvm.smul.with.overflow.i64(i64 4, i64 [[IDXPROM]]), !nosanitize + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + + // CHECK: getelementptr inbounds i32, i32* {{.*}}, i64 [[IDXPROM1:%.*]] + // CHECK-NEXT: @llvm.smul.with.overflow.i64(i64 4, i64 [[IDXPROM1]]), !nosanitize + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + + int arr[n][n]; + arr[k][k]; +} + +// CHECK-LABEL: define void @pointer_array +void pointer_array(int **arr, int k) { + // CHECK: @llvm.smul.with.overflow.i64(i64 8, i64 {{.*}}), !nosanitize + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + + // CHECK: @llvm.smul.with.overflow.i64(i64 4, i64 {{.*}}), !nosanitize + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + + arr[k][k]; +} + +// CHECK-LABEL: define void @pointer_array_unsigned_indices +void pointer_array_unsigned_indices(int **arr, unsigned k) { + // CHECK: icmp uge + // CHECK-NOT: select + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + // CHECK: icmp uge + // CHECK-NOT: select + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + arr[k][k]; +} + +// CHECK-LABEL: define void @pointer_array_mixed_indices +void pointer_array_mixed_indices(int **arr, int i, unsigned j) { + // CHECK: select + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + // CHECK-NOT: select + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + arr[i][j]; +} + +struct S1 { + int pad1; + union { + char leaf; + struct S1 *link; + } u; + struct S1 *arr; +}; + +// TODO: Currently, structure GEPs are not checked, so there are several +// potentially unsafe GEPs here which we don't instrument. +// +// CHECK-LABEL: define void @struct_index +void struct_index(struct S1 *p) { + // CHECK: getelementptr inbounds %struct.S1, %struct.S1* [[P:%.*]], i64 10 + // CHECK-NEXT: [[BASE:%.*]] = ptrtoint %struct.S1* [[P]] to i64, !nosanitize + // CHECK-NEXT: [[COMPGEP:%.*]] = add i64 [[BASE]], 240, !nosanitize + // CHECK: select + // CHECK: @__ubsan_handle_pointer_overflow{{.*}} i64 [[BASE]], i64 [[COMPGEP]]) {{.*}}, !nosanitize + + // CHECK-NOT: @__ubsan_handle_pointer_overflow + + p->arr[10].u.link->u.leaf; +} + +typedef void (*funcptr_t)(void); + +// CHECK-LABEL: define void @function_pointer_arith +void function_pointer_arith(funcptr_t *p, int k) { + // CHECK: add i64 {{.*}}, 8, !nosanitize + // CHECK-NOT: select + // CHECK: @__ubsan_handle_pointer_overflow{{.*}} + ++p; + + // CHECK: @llvm.smul.with.overflow.i64(i64 8, i64 {{.*}}), !nosanitize + // CHECK: select + // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} + p + k; +} + +// CHECK-LABEL: define void @dont_emit_checks_for_no_op_GEPs +// CHECK-C: __ubsan_handle_pointer_overflow +// CHECK-CPP-NOT: __ubsan_handle_pointer_overflow +void dont_emit_checks_for_no_op_GEPs(char *p) { + &p[0]; + + int arr[10][10]; + &arr[0][0]; +} + +#ifdef __cplusplus +} +#endif diff --git a/test/CodeGen/ubsan-pointer-overflow.m b/test/CodeGen/ubsan-pointer-overflow.m index 977e245838..d48e22579a 100644 --- a/test/CodeGen/ubsan-pointer-overflow.m +++ b/test/CodeGen/ubsan-pointer-overflow.m @@ -1,189 +1,5 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -w -emit-llvm -o - %s -fsanitize=pointer-overflow | FileCheck %s -// CHECK-LABEL: define void @unary_arith -void unary_arith(char *p) { - // CHECK: [[BASE:%.*]] = ptrtoint i8* {{.*}} to i64, !nosanitize - // CHECK-NEXT: [[COMPGEP:%.*]] = add i64 [[BASE]], 1, !nosanitize - // CHECK-NEXT: [[POSVALID:%.*]] = icmp uge i64 [[COMPGEP]], [[BASE]], !nosanitize - // CHECK-NEXT: br i1 [[POSVALID]]{{.*}}, !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}}, i64 [[BASE]], i64 [[COMPGEP]]){{.*}}, !nosanitize - ++p; - - // CHECK: ptrtoint i8* {{.*}} to i64, !nosanitize - // CHECK-NEXT: [[COMPGEP:%.*]] = add i64 {{.*}}, -1, !nosanitize - // CHECK: [[NEGVALID:%.*]] = icmp ule i64 [[COMPGEP]], {{.*}}, !nosanitize - // CHECK-NOT: select - // CHECK: br i1 [[NEGVALID]]{{.*}}, !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - --p; - - // CHECK: icmp uge i64 - // CHECK-NOT: select - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - p++; - - // CHECK: icmp ule i64 - // CHECK-NOT: select - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - p--; -} - -// CHECK-LABEL: define void @binary_arith -void binary_arith(char *p, int i) { - // CHECK: [[SMUL:%.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %{{.*}}), !nosanitize - // CHECK-NEXT: [[SMULOFLOW:%.*]] = extractvalue { i64, i1 } [[SMUL]], 1, !nosanitize - // CHECK-NEXT: [[SMULVAL:%.*]] = extractvalue { i64, i1 } [[SMUL]], 0, !nosanitize - // CHECK-NEXT: [[BASE:%.*]] = ptrtoint i8* {{.*}} to i64, !nosanitize - // CHECK-NEXT: [[COMPGEP:%.*]] = add i64 [[BASE]], [[SMULVAL]], !nosanitize - // CHECK-NEXT: [[OFFSETVALID:%.*]] = xor i1 [[SMULOFLOW]], true, !nosanitize - // CHECK-NEXT: [[POSVALID:%.*]] = icmp uge i64 [[COMPGEP]], [[BASE]], !nosanitize - // CHECK-NEXT: [[POSOFFSET:%.*]] = icmp sge i64 [[SMULVAL]], 0, !nosanitize - // CHECK-NEXT: [[NEGVALID:%.*]] = icmp ult i64 [[COMPGEP]], [[BASE]], !nosanitize - // CHECK-NEXT: [[DIFFVALID:%.*]] = select i1 [[POSOFFSET]], i1 [[POSVALID]], i1 [[NEGVALID]], !nosanitize - // CHECK: [[VALID:%.*]] = and i1 [[DIFFVALID]], [[OFFSETVALID]], !nosanitize - // CHECK-NEXT: br i1 [[VALID]]{{.*}}, !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}}, i64 [[BASE]], i64 [[COMPGEP]]){{.*}}, !nosanitize - p + i; - - // CHECK: [[OFFSET:%.*]] = sub i64 0, {{.*}} - // CHECK-NEXT: getelementptr inbounds {{.*}} [[OFFSET]] - // CHECK: select - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - p - i; -} - -// CHECK-LABEL: define void @binary_arith_unsigned -void binary_arith_unsigned(char *p, unsigned i) { - // CHECK: [[SMUL:%.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %{{.*}}), !nosanitize - // CHECK-NEXT: [[SMULOFLOW:%.*]] = extractvalue { i64, i1 } [[SMUL]], 1, !nosanitize - // CHECK-NEXT: [[SMULVAL:%.*]] = extractvalue { i64, i1 } [[SMUL]], 0, !nosanitize - // CHECK-NEXT: [[BASE:%.*]] = ptrtoint i8* {{.*}} to i64, !nosanitize - // CHECK-NEXT: [[COMPGEP:%.*]] = add i64 [[BASE]], [[SMULVAL]], !nosanitize - // CHECK-NEXT: [[OFFSETVALID:%.*]] = xor i1 [[SMULOFLOW]], true, !nosanitize - // CHECK-NEXT: [[POSVALID:%.*]] = icmp uge i64 [[COMPGEP]], [[BASE]], !nosanitize - // CHECK: [[VALID:%.*]] = and i1 [[POSVALID]], [[OFFSETVALID]], !nosanitize - // CHECK-NEXT: br i1 [[VALID]]{{.*}}, !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}}, i64 [[BASE]], i64 [[COMPGEP]]){{.*}}, !nosanitize - p + i; - - // CHECK: [[OFFSET:%.*]] = sub i64 0, {{.*}} - // CHECK-NEXT: getelementptr inbounds {{.*}} [[OFFSET]] - // CHECK: icmp ule i64 - // CHECK-NOT: select - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - p - i; -} - -// CHECK-LABEL: define void @fixed_len_array -void fixed_len_array(int k) { - // CHECK: getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* [[ARR:%.*]], i64 0, i64 [[IDXPROM:%.*]] - // CHECK-NEXT: [[SMUL:%.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 40, i64 [[IDXPROM]]), !nosanitize - // CHECK-NEXT: [[SMULOFLOW:%.*]] = extractvalue { i64, i1 } [[SMUL]], 1, !nosanitize - // CHECK-NEXT: [[SMULVAL:%.*]] = extractvalue { i64, i1 } [[SMUL]], 0, !nosanitize - // CHECK-NEXT: [[BASE:%.*]] = ptrtoint [10 x [10 x i32]]* [[ARR]] to i64, !nosanitize - // CHECK-NEXT: [[COMPGEP:%.*]] = add i64 [[BASE]], [[SMULVAL]], !nosanitize - // CHECK-NEXT: [[OFFSETVALID:%.*]] = xor i1 [[SMULOFLOW]], true, !nosanitize - // CHECK-NEXT: [[POSVALID:%.*]] = icmp uge i64 [[COMPGEP]], [[BASE]], !nosanitize - // CHECK-NEXT: [[POSOFFSET:%.*]] = icmp sge i64 [[SMULVAL]], 0, !nosanitize - // CHECK-NEXT: [[NEGVALID:%.*]] = icmp ult i64 [[COMPGEP]], [[BASE]], !nosanitize - // CHECK-NEXT: [[DIFFVALID:%.*]] = select i1 [[POSOFFSET]], i1 [[POSVALID]], i1 [[NEGVALID]], !nosanitize - // CHECK: [[VALID:%.*]] = and i1 [[DIFFVALID]], [[OFFSETVALID]], !nosanitize - // CHECK-NEXT: br i1 [[VALID]]{{.*}}, !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}}, i64 [[BASE]], i64 [[COMPGEP]]){{.*}}, !nosanitize - - // CHECK: getelementptr inbounds [10 x i32], [10 x i32]* {{.*}}, i64 0, i64 [[IDXPROM1:%.*]] - // CHECK-NEXT: @llvm.smul.with.overflow.i64(i64 4, i64 [[IDXPROM1]]), !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - - int arr[10][10]; - arr[k][k]; -} - -// CHECK-LABEL: define void @variable_len_array -void variable_len_array(int n, int k) { - // CHECK: getelementptr inbounds i32, i32* {{.*}}, i64 [[IDXPROM:%.*]] - // CHECK-NEXT: @llvm.smul.with.overflow.i64(i64 4, i64 [[IDXPROM]]), !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - - // CHECK: getelementptr inbounds i32, i32* {{.*}}, i64 [[IDXPROM1:%.*]] - // CHECK-NEXT: @llvm.smul.with.overflow.i64(i64 4, i64 [[IDXPROM1]]), !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - - int arr[n][n]; - arr[k][k]; -} - -// CHECK-LABEL: define void @pointer_array -void pointer_array(int **arr, int k) { - // CHECK: @llvm.smul.with.overflow.i64(i64 8, i64 {{.*}}), !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - - // CHECK: @llvm.smul.with.overflow.i64(i64 4, i64 {{.*}}), !nosanitize - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - - arr[k][k]; -} - -// CHECK-LABEL: define void @pointer_array_unsigned_indices -void pointer_array_unsigned_indices(int **arr, unsigned k) { - // CHECK: icmp uge - // CHECK-NOT: select - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - // CHECK: icmp uge - // CHECK-NOT: select - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - arr[k][k]; -} - -// CHECK-LABEL: define void @pointer_array_mixed_indices -void pointer_array_mixed_indices(int **arr, int i, unsigned j) { - // CHECK: select - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - // CHECK-NOT: select - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - arr[i][j]; -} - -struct S1 { - int pad1; - union { - char leaf; - struct S1 *link; - } u; - struct S1 *arr; -}; - -// TODO: Currently, structure GEPs are not checked, so there are several -// potentially unsafe GEPs here which we don't instrument. -// -// CHECK-LABEL: define void @struct_index -void struct_index(struct S1 *p) { - // CHECK: getelementptr inbounds %struct.S1, %struct.S1* [[P:%.*]], i64 10 - // CHECK-NEXT: [[BASE:%.*]] = ptrtoint %struct.S1* [[P]] to i64, !nosanitize - // CHECK-NEXT: [[COMPGEP:%.*]] = add i64 [[BASE]], 240, !nosanitize - // CHECK: select - // CHECK: @__ubsan_handle_pointer_overflow{{.*}} i64 [[BASE]], i64 [[COMPGEP]]) {{.*}}, !nosanitize - - // CHECK-NOT: @__ubsan_handle_pointer_overflow - - p->arr[10].u.link->u.leaf; -} - -typedef void (*funcptr_t)(void); - -// CHECK-LABEL: define void @function_pointer_arith -void function_pointer_arith(funcptr_t *p, int k) { - // CHECK: add i64 {{.*}}, 8, !nosanitize - // CHECK-NOT: select - // CHECK: @__ubsan_handle_pointer_overflow{{.*}} - ++p; - - // CHECK: @llvm.smul.with.overflow.i64(i64 8, i64 {{.*}}), !nosanitize - // CHECK: select - // CHECK: call void @__ubsan_handle_pointer_overflow{{.*}} - p + k; -} - // CHECK-LABEL: define void @variable_len_array_arith void variable_len_array_arith(int n, int k) { int vla[n]; @@ -209,12 +25,3 @@ void objc_id(id *p) { // CHECK: @__ubsan_handle_pointer_overflow{{.*}} p++; } - -// CHECK-LABEL: define void @dont_emit_checks_for_no_op_GEPs -// CHECK-NOT: __ubsan_handle_pointer_overflow -void dont_emit_checks_for_no_op_GEPs(char *p) { - &p[0]; - - int arr[10][10]; - &arr[0][0]; -} diff --git a/test/CodeGenCXX/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.cpp b/test/CodeGenCXX/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.cpp new file mode 100644 index 0000000000..9dc8b90b5d --- /dev/null +++ b/test/CodeGenCXX/catch-nullptr-and-nonzero-offset-in-offsetof-idiom.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -x c++ -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s +// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s + +#include <stdint.h> + +struct S { + int x, y; +}; + +// CHECK-LABEL: define i64 @{{.*}}get_offset_of_y_naively{{.*}}( +uintptr_t get_offset_of_y_naively() { + // CHECK: [[ENTRY:.*]]: + // CHECK-NEXT: ret i64 ptrtoint (i32* getelementptr (i32, i32* null, i32 1) to i64) + // CHECK-NEXT: } + return ((uintptr_t)(&(((S *)nullptr)->y))); +} + +// CHECK-LABEL: define i64 @{{.*}}get_offset_of_y_via_builtin{{.*}}( +uintptr_t get_offset_of_y_via_builtin() { + // CHECK: [[ENTRY:.*]]: + // CHECK-NEXT: ret i64 4 + // CHECK-NEXT: } + return __builtin_offsetof(S, y); +} |