1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-undefined-ignore-overflow-pattern=all %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-undefined-ignore-overflow-pattern=all -fwrapv %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-undefined-ignore-overflow-pattern=add-signed-overflow-test,add-unsigned-overflow-test %s -emit-llvm -o - | FileCheck %s --check-prefix=ADD
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-undefined-ignore-overflow-pattern=negated-unsigned-const %s -emit-llvm -o - | FileCheck %s --check-prefix=NEGATE
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-undefined-ignore-overflow-pattern=unsigned-post-decr-while %s -emit-llvm -o - | FileCheck %s --check-prefix=WHILE
// Ensure some common overflow-dependent or overflow-prone code patterns don't
// trigger the overflow sanitizers. In many cases, overflow warnings caused by
// these patterns are seen as "noise" and result in users turning off
// sanitization all together.
// A pattern like "if (a + b < a)" simply checks for overflow and usually means
// the user is trying to handle it gracefully.
// Similarly, a pattern resembling "while (i--)" is extremely common and
// warning on its inevitable overflow can be seen as superfluous. Do note that
// using "i" in future calculations can be tricky because it will still
// wrap-around.
// Another common pattern that, in some cases, is found to be too noisy is
// unsigned negation, for example:
// unsigned long A = -1UL;
// Skip over parts of the IR containing this file's name.
// CHECK: source_filename = {{.*}}
// Ensure we don't see anything about handling overflow before the tests below.
// CHECK-NOT: handle{{.*}}overflow
extern unsigned a, b, c;
extern int u, v;
extern unsigned some(void);
// ADD-LABEL: @basic_commutativity
// WHILE-LABEL: @basic_commutativity
// NEGATE-LABEL: @basic_commutativity
// WHILE: handler.add_overflow
// NEGATE: handler.add_overflow
// ADD-NOT: handler.add_overflow
void basic_commutativity(void) {
if (a + b < a)
c = 9;
if (a + b < b)
c = 9;
if (b + a < b)
c = 9;
if (b + a < a)
c = 9;
if (a > a + b)
c = 9;
if (a > b + a)
c = 9;
if (b > a + b)
c = 9;
if (b > b + a)
c = 9;
if (u + v < u)
c = 9;
}
// ADD-LABEL: @arguments_and_commutativity
// WHILE-LABEL: @arguments_and_commutativity
// NEGATE-LABEL: @arguments_and_commutativity
// WHILE: handler.add_overflow
// NEGATE: handler.add_overflow
// ADD-NOT: handler.add_overflow
void arguments_and_commutativity(unsigned V1, unsigned V2) {
if (V1 + V2 < V1)
c = 9;
if (V1 + V2 < V2)
c = 9;
if (V2 + V1 < V2)
c = 9;
if (V2 + V1 < V1)
c = 9;
if (V1 > V1 + V2)
c = 9;
if (V1 > V2 + V1)
c = 9;
if (V2 > V1 + V2)
c = 9;
if (V2 > V2 + V1)
c = 9;
}
// ADD-LABEL: @pointers
// WHILE-LABEL: @pointers
// NEGATE-LABEL: @pointers
// WHILE: handler.add_overflow
// NEGATE: handler.add_overflow
// ADD-NOT: handler.add_overflow
void pointers(unsigned *P1, unsigned *P2, unsigned V1) {
if (*P1 + *P2 < *P1)
c = 9;
if (*P1 + V1 < V1)
c = 9;
if (V1 + *P2 < *P2)
c = 9;
}
struct OtherStruct {
unsigned foo, bar;
};
struct MyStruct {
unsigned base, offset;
struct OtherStruct os;
};
extern struct MyStruct ms;
// ADD-LABEL: @structs
// WHILE-LABEL: @structs
// NEGATE-LABEL: @structs
// WHILE: handler.add_overflow
// NEGATE: handler.add_overflow
// ADD-NOT: handler.add_overflow
void structs(void) {
if (ms.base + ms.offset < ms.base)
c = 9;
}
// ADD-LABEL: @nestedstructs
// WHILE-LABEL: @nestedstructs
// NEGATE-LABEL: @nestedstructs
// WHILE: handler.add_overflow
// NEGATE: handler.add_overflow
// ADD-NOT: handler.add_overflow
void nestedstructs(void) {
if (ms.os.foo + ms.os.bar < ms.os.foo)
c = 9;
}
// ADD-LABEL: @constants
// WHILE-LABEL: @constants
// NEGATE-LABEL: @constants
// WHILE: handler.add_overflow
// NEGATE: handler.add_overflow
// ADD-NOT: handler.add_overflow
// Normally, this would be folded into a simple call to the overflow handler
// and a store. Excluding this pattern results in just a store.
void constants(void) {
unsigned base = 4294967295;
unsigned offset = 1;
if (base + offset < base)
c = 9;
}
// ADD-LABEL: @common_while
// NEGATE-LABEL: @common_while
// WHILE-LABEL: @common_while
// ADD: usub.with.overflow
// NEGATE: usub.with.overflow
// WHILE: %dec = add i32 %0, -1
void common_while(unsigned i) {
// This post-decrement usually causes overflow sanitizers to trip on the very
// last operation.
while (i--) {
some();
}
}
// ADD-LABEL: @negation
// NEGATE-LABEL: @negation
// WHILE-LABEL @negation
// ADD: negate_overflow
// NEGATE-NOT: negate_overflow
// WHILE: negate_overflow
// Normally, these assignments would trip the unsigned overflow sanitizer.
void negation(void) {
#define SOME -1UL
unsigned long A = -1UL;
unsigned long B = -2UL;
unsigned long C = -SOME;
(void)A;(void)B;(void)C;
}
// ADD-LABEL: @function_call
// WHILE-LABEL: @function_call
// NEGATE-LABEL: @function_call
// WHILE: handler.add_overflow
// NEGATE: handler.add_overflow
// ADD-NOT: handler.add_overflow
void function_call(void) {
if (b + some() < b)
c = 9;
}
|