
This commit releases a checker that was developed to a stable level in the Ericsson-internal fork of Clang Static Analyzer. Note that the functionality of this checker overlaps with core.UndefinedBinaryOperatorResult ("UBOR"), but there are several differences between them: (1) UBOR is only triggered when the constant folding performed by the Clang Static Analyzer engine determines that the value of a binary operator expression is undefined; this checker can report issues where the operands are not constants. (2) UBOR has unrelated checks for handling other binary operators, this checker only examines bitwise shifts. (3) This checker has a Pedantic flag and by default does not report expressions (e.g. -2 << 2) that're undefined by the standard but consistently supported in practice. (4) UBOR exhibits buggy behavior in code that involves cast expressions, e.g. void foo(unsigned short s) { if (s == 2) { (void) ((unsigned int) s) << 16; } } Later it would be good to eliminate this overlap (perhaps by deprecating and then eliminating the bitwise shift handling in UBOR), but in my opinion that belongs to separate commits. Differential Revision: https://reviews.llvm.org/D156312 Co-authored-by: Endre Fulop <endre.fulop@sigmatechnology.se>
168 lines
7.4 KiB
C
168 lines
7.4 KiB
C
// RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \
|
|
// RUN: -analyzer-config core.BitwiseShift:Pedantic=true \
|
|
// RUN: -analyzer-output=text -verify=expected,c \
|
|
// RUN: -triple x86_64-pc-linux-gnu -x c %s \
|
|
// RUN: -Wno-shift-count-negative -Wno-shift-negative-value \
|
|
// RUN: -Wno-shift-count-overflow -Wno-shift-overflow \
|
|
// RUN: -Wno-shift-sign-overflow
|
|
//
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \
|
|
// RUN: -analyzer-config core.BitwiseShift:Pedantic=true \
|
|
// RUN: -analyzer-output=text -verify=expected,cxx \
|
|
// RUN: -triple x86_64-pc-linux-gnu -x c++ -std=c++14 %s \
|
|
// RUN: -Wno-shift-count-negative -Wno-shift-negative-value \
|
|
// RUN: -Wno-shift-count-overflow -Wno-shift-overflow \
|
|
// RUN: -Wno-shift-sign-overflow
|
|
|
|
// This test file verifies the pedantic mode of the BitwiseShift checker, which
|
|
// also reports issues that are undefined behavior (according to the standard,
|
|
// under C and in C++ before C++20), but would be accepted by many compilers.
|
|
|
|
// TEST NEGATIVE LEFT OPERAND
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
int negative_left_operand_literal(void) {
|
|
return -2 << 2;
|
|
// expected-warning@-1 {{Left operand is negative in left shift}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the left operand is negative}}
|
|
}
|
|
|
|
int negative_left_operand_symbolic(int left, int right) {
|
|
// expected-note@+2 {{Assuming 'left' is < 0}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (left >= 0)
|
|
return 0;
|
|
return left >> right;
|
|
// expected-warning@-1 {{Left operand is negative in right shift}}
|
|
// expected-note@-2 {{The result of right shift is undefined because the left operand is negative}}
|
|
}
|
|
|
|
int negative_left_operand_compound(short arg) {
|
|
// expected-note@+2 {{Assuming 'arg' is < 0}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (arg >= 0)
|
|
return 0;
|
|
return (arg - 3) << 2;
|
|
// expected-warning@-1 {{Left operand is negative in left shift}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the left operand is negative}}
|
|
}
|
|
|
|
int double_negative(void) {
|
|
// In this case we still report that the right operand is negative, because
|
|
// that's the more "serious" issue:
|
|
return -2 >> -2;
|
|
// expected-warning@-1 {{Right operand is negative in right shift}}
|
|
// expected-note@-2 {{The result of right shift is undefined because the right operand is negative}}
|
|
}
|
|
|
|
int single_unknown_negative(int arg) {
|
|
// In this case just one of the operands will be negative, so we end up
|
|
// reporting the left operand after assuming that the right operand is
|
|
// positive.
|
|
// expected-note@+2 {{Assuming 'arg' is not equal to 0}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (!arg)
|
|
return 0;
|
|
// We're first checking the right operand, record that it must be positive,
|
|
// then report that then the left argument must be negative.
|
|
return -arg << arg;
|
|
// expected-warning@-1 {{Left operand is negative in left shift}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the left operand is negative}}
|
|
}
|
|
|
|
void shift_negative_by_zero(int c) {
|
|
// This seems to be innocent, but the standard (before C++20) clearly implies
|
|
// that this is UB, so we should report it in pedantic mode.
|
|
c = (-1) << 0;
|
|
// expected-warning@-1 {{Left operand is negative in left shift}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the left operand is negative}}
|
|
}
|
|
|
|
// TEST OVERFLOW OF CONCRETE SIGNED LEFT OPERAND
|
|
//===----------------------------------------------------------------------===//
|
|
// (the most complex and least important part of the checker)
|
|
|
|
int concrete_overflow_literal(void) {
|
|
// 27 in binary is 11011 (5 bits), when shifted by 28 bits it becomes
|
|
// 1_10110000_00000000_00000000_00000000
|
|
return 27 << 28;
|
|
// expected-warning@-1 {{The shift '27 << 28' overflows the capacity of 'int'}}
|
|
// cxx-note@-2 {{The shift '27 << 28' is undefined because 'int' can hold only 32 bits (including the sign bit), so 1 bit overflows}}
|
|
// c-note@-3 {{The shift '27 << 28' is undefined because 'int' can hold only 31 bits (excluding the sign bit), so 2 bits overflow}}
|
|
}
|
|
|
|
int concrete_overflow_symbolic(int arg) {
|
|
// 29 in binary is 11101 (5 bits), when shifted by 29 bits it becomes
|
|
// 11_10100000_00000000_00000000_00000000
|
|
|
|
// expected-note@+2 {{Assuming 'arg' is equal to 29}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (arg != 29)
|
|
return 0;
|
|
return arg << arg;
|
|
// expected-warning@-1 {{The shift '29 << 29' overflows the capacity of 'int'}}
|
|
// cxx-note@-2 {{The shift '29 << 29' is undefined because 'int' can hold only 32 bits (including the sign bit), so 2 bits overflow}}
|
|
// c-note@-3 {{The shift '29 << 29' is undefined because 'int' can hold only 31 bits (excluding the sign bit), so 3 bits overflow}}
|
|
}
|
|
|
|
int concrete_overflow_language_difference(void) {
|
|
// 21 in binary is 10101 (5 bits), when shifted by 27 bits it becomes
|
|
// 10101000_00000000_00000000_00000000
|
|
// This does not overflow the 32-bit capacity of int, but reaches the sign
|
|
// bit, which is undefined under C (but accepted in C++ even before C++20).
|
|
return 21 << 27;
|
|
// c-warning@-1 {{The shift '21 << 27' overflows the capacity of 'int'}}
|
|
// c-note@-2 {{The shift '21 << 27' is undefined because 'int' can hold only 31 bits (excluding the sign bit), so 1 bit overflows}}
|
|
}
|
|
|
|
int concrete_overflow_int_min(void) {
|
|
// Another case that's undefined in C but valid in all C++ versions.
|
|
// Note the "represented by 1 bit" special case
|
|
return 1 << 31;
|
|
// c-warning@-1 {{The shift '1 << 31' overflows the capacity of 'int'}}
|
|
// c-note@-2 {{The shift '1 << 31' is undefined because 'int' can hold only 31 bits (excluding the sign bit), so 1 bit overflows}}
|
|
}
|
|
|
|
int concrete_overflow_vague(int arg) {
|
|
// expected-note@+2 {{Assuming 'arg' is > 25}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (arg <= 25)
|
|
return 0;
|
|
return 1024 << arg;
|
|
// expected-warning@-1 {{Left shift of '1024' overflows the capacity of 'int'}}
|
|
// cxx-note@-2 {{Left shift of '1024' is undefined because 'int' can hold only 32 bits (including the sign bit), so some bits overflow}}
|
|
// c-note@-3 {{Left shift of '1024' is undefined because 'int' can hold only 31 bits (excluding the sign bit), so some bits overflow}}
|
|
}
|
|
|
|
int concrete_overflow_vague_only_c(int arg) {
|
|
// A third case that's undefined in C but valid in all C++ versions.
|
|
|
|
// c-note@+2 {{Assuming 'arg' is > 20}}
|
|
// c-note@+1 {{Taking false branch}}
|
|
if (arg <= 20)
|
|
return 0;
|
|
return 1024 << arg;
|
|
// c-warning@-1 {{Left shift of '1024' overflows the capacity of 'int'}}
|
|
// c-note@-2 {{Left shift of '1024' is undefined because 'int' can hold only 31 bits (excluding the sign bit), so some bits overflow}}
|
|
}
|
|
|
|
int concrete_overflow_vague_left(int arg) {
|
|
// This kind of overflow check only handles concrete values on the LHS. With
|
|
// some effort it would be possible to report errors in cases like this; but
|
|
// it's probably a waste of time especially considering that overflows of
|
|
// left shifts became well-defined in C++20.
|
|
|
|
if (arg <= 1024)
|
|
return 0;
|
|
return arg << 25; // no-warning
|
|
}
|
|
|
|
int concrete_overflow_shift_zero(void) {
|
|
// This is legal, even in C.
|
|
// The relevant rule (as paraphrased on cppreference.com) is:
|
|
// "For signed LHS with nonnegative values, the value of LHS << RHS is
|
|
// LHS * 2^RHS if it is representable in the promoted type of lhs, otherwise
|
|
// the behavior is undefined."
|
|
return 0 << 31; // no-warning
|
|
}
|