llvm/clang/test/CodeGen/ignore-overflow-pattern.c

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


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