Files
codeql/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/BadAdditionOverflowCheck/test.cpp
Ziemowit Laski ad5aa182df [CPP-434] When analyzing overflow, discard any explicit casts.
Use the simple range analysis library to detect which
          additions may in fact overflow.
2019-10-22 15:21:30 -07:00

153 lines
4.3 KiB
C++

// Test for BadAdditionOverflowCheck.
bool checkOverflow1(unsigned short a, unsigned short b) {
return (a + b < a); // BAD: comparison always false (due to promotion).
}
// Test for BadAdditionOverflowCheck.
bool checkOverflow2(unsigned short a, unsigned short b) {
return ((unsigned short)(a + b) < a); // GOOD
}
// Test for PointlessSelfComparison.
bool selfCmp1(int x) {
return (x == (int)x); // BAD: always returns true.
}
// Test for PointlessSelfComparison.
bool selfCmp2(int x) {
return (x == (char)x); // GOOD: test that the cast does not overflow.
}
// Test for PointlessSelfComparison.
bool isnan(double x) {
return (x != x); // GOOD: NaN test.
}
// Tests for ComparisonWithCancelingSubExpr.
void cmpWithCancelingVar1(unsigned short x, unsigned short y, unsigned short z) {
bool b;
b = x + y < x + z; // BAD: x can be canceled
b = x + y - x < z; // BAD: x can be canceled
b = 2*x + y < 2*x + z; // BAD: x can be canceled
b = 3*x + y - 2*x < z + x; // BAD: x can be canceled
b = (-x) - (+x) < z - 2*x; // BAD: x can be canceled
}
// Tests for ComparisonWithCancelingSubExpr.
void cmpWithCancelingVar2(int x, int y, int z) {
bool b;
b = x + y < x + z; // GOOD: x cannot be canceled (possible overflow)
b = x + y - x < z; // GOOD: x cannot be canceled (possible overflow)
b = 3*x + y - 2*x < z + x; // GOOD: x cannot be canceled (possible overflow)
}
struct MyStruct {
unsigned short a;
unsigned short b;
};
// Regression test for false positive results for comparisons involving
// structure fields.
bool checkOverflow3(MyStruct *x, MyStruct *y, MyStruct *z) {
return (x->a + y->b < z->a); // GOOD: `x->a` is not the same as `z->a`.
}
// Regression test for false positive results for comparisons involving
// structure fields.
bool selfCmp2(MyStruct* x, MyStruct* y) {
return (x->a == (int)(y->a)); // GOOD: `x->a` is not the same as `y->a`.
}
// Regression test for false positive result for an isNaN check in a
// template method.
template <typename T>
struct is_nan
{
static bool call(T n)
{
return (n != n) ? true : false; // GOOD: does something useful for `is_nan<double>`.
}
};
// Regression test for false positive result.
bool cmpWithCancelingVar3(int x) {
return (x < 2*x); // GOOD: x cannot be canceled (possible overflow)
}
bool selfCmp3(unsigned short x) {
x++;
return (x == (unsigned short)x); // BAD: always returns true.
}
bool selfCmp4(int x) {
while (x == x) // BAD: always returns true.
{
x = x + 1;
}
}
bool selfCmp5(int x) {
while (x == x) // BAD: always returns true. [NOT DETECTED]
{
x++;
}
}
// This test is an example of code that used to lead to confusing results
// from `BadOverflowAdditionCheck.ql`. The problem was that the query was
// trying to be too clever by using range analysis. The overflow check here
// can never fail, because `a < 0xFFFF`, so the query identified it as a
// bad overflow check. But if you just look at the types, it is a perfectly
// good overflow check. We shouldn't penalize people for coding
// defensively.
bool checkOverflow3(unsigned int a, unsigned short b) {
if (a >= 0xFFFF) {
return false;
}
return (a + b < a); // GOOD: b is automatically promoted to unsigned int
}
// We imagine that the next two lines come from a platform-specific header.
typedef unsigned long long size_t;
#define u64 unsigned long long
int isSmallEnough(unsigned long long x) {
// The cast is to the same syntactic type, and there is no macro involved.
// That makes the cast redundant, and therefore the comparison is redundant.
if ((unsigned long long)x != x) { // BAD
return 0;
}
// These comparisons are pointless on the platform where this test runs, but
// code like this is often written to have an effect on some platforms and
// get compiled away on others.
return x == (size_t)x && x == (u64)x; // GOOD
}
#define markRange(str, x, y) \
if ((x) == (y)) { \
str[x] = '^'; \
} else { \
int i; \
str[x] = '<'; \
for (i = x + 1; i < y; i++) { \
str[i] = '-'; \
} \
str[y] = '>'; \
}
void useMarkRange(int offs) {
char buffer[100];
markRange(buffer, 10, 20);
markRange(buffer, 30, 30);
markRange(buffer, offs, offs + 10);
markRange(buffer, offs, offs); // GOOD (comparison is in the macro)
}
#define MY_MACRO(x) (x)
void myMacroTest(int x) {
MY_MACRO(x == x); // BAD
}