// 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; }