mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
Merge pull request #1056 from jbj/SimpleRangeAnalysis-use-after-cast
C++: Fix use-after-cast bug in SimpleRangeAnalysis
This commit is contained in:
@@ -38,7 +38,7 @@ where
|
||||
// a comparison between an enum and zero is always valid because whether
|
||||
// the underlying type of an enum is signed is compiler-dependent
|
||||
not exists (Expr e, ConstantZero z
|
||||
| relOpWithSwap(cmp, e, z, _, _) and
|
||||
| relOpWithSwap(cmp, e.getFullyConverted(), z, _, _) and
|
||||
e.getUnderlyingType() instanceof Enum) and
|
||||
|
||||
// Construct a reason for the message. Something like: x >= 5 and 3 >= y.
|
||||
|
||||
@@ -36,7 +36,7 @@ boolean directionIsLesser(RelationDirection dir) {
|
||||
|
||||
/**
|
||||
* Holds if `rel` is a relational operation (`<`, `>`, `<=` or `>=`)
|
||||
* with left operand `lhs` and right operand `rhs`, described by
|
||||
* with fully-converted children `lhs` and `rhs`, described by
|
||||
* `dir` and `strict`.
|
||||
*
|
||||
* For example, if `rel` is `x < 5` then
|
||||
@@ -47,8 +47,8 @@ predicate relOp(
|
||||
RelationalOperation rel, Expr lhs, Expr rhs,
|
||||
RelationDirection dir, RelationStrictness strict
|
||||
) {
|
||||
lhs = rel.getLeftOperand() and
|
||||
rhs = rel.getRightOperand() and
|
||||
lhs = rel.getLeftOperand().getFullyConverted() and
|
||||
rhs = rel.getRightOperand().getFullyConverted() and
|
||||
((rel instanceof LTExpr and dir = Lesser() and strict = Strict()) or
|
||||
(rel instanceof LEExpr and dir = Lesser() and strict = Nonstrict()) or
|
||||
(rel instanceof GTExpr and dir = Greater() and strict = Strict()) or
|
||||
@@ -57,7 +57,7 @@ predicate relOp(
|
||||
|
||||
/**
|
||||
* Holds if `rel` is a relational operation (`<`, `>`, `<=` or `>=`)
|
||||
* with children `a` and `b`, described by `dir` and `strict`.
|
||||
* with fully-converted children `a` and `b`, described by `dir` and `strict`.
|
||||
*
|
||||
* This allows for the relation to be either as written, or with its
|
||||
* arguments reversed; for example, if `rel` is `x < 5` then both
|
||||
@@ -74,7 +74,7 @@ predicate relOpWithSwap(
|
||||
|
||||
/**
|
||||
* Holds if `rel` is a comparison operation (`<`, `>`, `<=` or `>=`)
|
||||
* with children `a` and `b`, described by `dir` and `strict`, with
|
||||
* with fully-converted children `a` and `b`, described by `dir` and `strict`, with
|
||||
* result `branch`.
|
||||
*
|
||||
* This allows for the relation to be either as written, or with its
|
||||
@@ -95,8 +95,8 @@ predicate relOpWithSwapAndNegate(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `cmp` is an equality operation (`==` or `!=`) with left
|
||||
* operand `lhs`, right operand `rhs`, and `isEQ` is true if `cmp` is an
|
||||
* Holds if `cmp` is an equality operation (`==` or `!=`) with fully-converted
|
||||
* children `lhs` and `rhs`, and `isEQ` is true if `cmp` is an
|
||||
* `==` operation and false if it is an `!=` operation.
|
||||
*
|
||||
* For example, if `rel` is `x == 5` then
|
||||
@@ -104,15 +104,15 @@ predicate relOpWithSwapAndNegate(
|
||||
*/
|
||||
private
|
||||
predicate eqOp(EqualityOperation cmp, Expr lhs, Expr rhs, boolean isEQ) {
|
||||
lhs = cmp.getLeftOperand() and
|
||||
rhs = cmp.getRightOperand() and
|
||||
lhs = cmp.getLeftOperand().getFullyConverted() and
|
||||
rhs = cmp.getRightOperand().getFullyConverted() and
|
||||
((cmp instanceof EQExpr and isEQ = true) or
|
||||
(cmp instanceof NEExpr and isEQ = false))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `cmp` is an equality operation (`==` or `!=`) with operands
|
||||
* `a` and `b`, and `isEQ` is true if `cmp` is an `==` operation and
|
||||
* Holds if `cmp` is an equality operation (`==` or `!=`) with fully-converted
|
||||
* operands `a` and `b`, and `isEQ` is true if `cmp` is an `==` operation and
|
||||
* false if it is an `!=` operation.
|
||||
*
|
||||
* This allows for the equality to be either as written, or with its
|
||||
@@ -127,8 +127,8 @@ predicate eqOpWithSwap(EqualityOperation cmp, Expr a, Expr b, boolean isEQ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `cmp` is an equality operation (`==` or `!=`) with operands
|
||||
* `a` and `b`, `isEQ` is true if `cmp` is an `==` operation and
|
||||
* Holds if `cmp` is an equality operation (`==` or `!=`) with fully-converted
|
||||
* children `a` and `b`, `isEQ` is true if `cmp` is an `==` operation and
|
||||
* false if it is an `!=` operation, and the result is `branch`.
|
||||
*
|
||||
* This allows for the comparison to be either as written, or with its
|
||||
@@ -201,12 +201,28 @@ predicate linearAccessImpl(Expr expr, VariableAccess v, float p, float q) {
|
||||
or
|
||||
// +(p*v+q) == p*v + q
|
||||
exists (UnaryPlusExpr unaryPlusExpr
|
||||
| linearAccess(unaryPlusExpr.getOperand(), v, p, q) and
|
||||
| linearAccess(unaryPlusExpr.getOperand().getFullyConverted(), v, p, q) and
|
||||
expr = unaryPlusExpr)
|
||||
or
|
||||
// (larger_type)(p*v+q) == p*v + q
|
||||
exists (Cast cast, ArithmeticType sourceType, ArithmeticType targetType
|
||||
| linearAccess(cast.getExpr(), v, p, q) and
|
||||
sourceType = cast.getExpr().getType().getUnspecifiedType() and
|
||||
targetType = cast.getType().getUnspecifiedType() and
|
||||
// This allows conversion between signed and unsigned, which is technically
|
||||
// lossy but common enough that we'll just have to assume the user knows
|
||||
// what they're doing.
|
||||
targetType.getSize() >= sourceType.getSize() and
|
||||
expr = cast)
|
||||
or
|
||||
// (p*v+q) == p*v + q
|
||||
exists (ParenthesisExpr paren
|
||||
| linearAccess(paren.getExpr(), v, p, q) and
|
||||
expr = paren)
|
||||
or
|
||||
// -(a*v+b) == -a*v + (-b)
|
||||
exists (UnaryMinusExpr unaryMinusExpr, float a, float b
|
||||
| linearAccess(unaryMinusExpr.getOperand(), v, a, b) and
|
||||
| linearAccess(unaryMinusExpr.getOperand().getFullyConverted(), v, a, b) and
|
||||
expr = unaryMinusExpr and
|
||||
p = -a and
|
||||
q = -b)
|
||||
@@ -270,14 +286,14 @@ predicate cmpWithLinearBound(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `lb` and `ub` are the lower and upper bounds of type `t`
|
||||
* respectively.
|
||||
* Holds if `lb` and `ub` are the lower and upper bounds of the unspecified
|
||||
* type `t`.
|
||||
*
|
||||
* For example, if `t` is a signed 32-bit type then holds if `lb` is
|
||||
* `-2^31` and `ub` is `2^31 - 1`.
|
||||
*/
|
||||
private
|
||||
predicate typeBounds(Type t, float lb, float ub) {
|
||||
predicate typeBounds(ArithmeticType t, float lb, float ub) {
|
||||
exists (IntegralType integralType, float limit
|
||||
| integralType = t and limit = 2.pow(8 * integralType.getSize())
|
||||
| if integralType instanceof BoolType
|
||||
@@ -291,22 +307,22 @@ predicate typeBounds(Type t, float lb, float ub) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the lower bound for type `t`.
|
||||
* Gets the lower bound for the unspecified type `t`.
|
||||
*
|
||||
* For example, if `t` is a signed 32-bit type then the result is
|
||||
* `-2^31`.
|
||||
*/
|
||||
float typeLowerBound(Type t) {
|
||||
float typeLowerBound(ArithmeticType t) {
|
||||
typeBounds(t, result, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the upper bound for type `t`.
|
||||
* Gets the upper bound for the unspecified type `t`.
|
||||
*
|
||||
* For example, if `t` is a signed 32-bit type then the result is
|
||||
* `2^31 - 1`.
|
||||
*/
|
||||
float typeUpperBound(Type t) {
|
||||
float typeUpperBound(ArithmeticType t) {
|
||||
typeBounds(t, _, result)
|
||||
}
|
||||
|
||||
|
||||
@@ -459,3 +459,8 @@
|
||||
| test.cpp:67:13:67:14 | xb | 0.0 |
|
||||
| test.cpp:69:10:69:10 | b | 0.0 |
|
||||
| test.cpp:69:21:69:21 | t | 0.0 |
|
||||
| test.cpp:74:30:74:30 | c | 0.0 |
|
||||
| test.cpp:74:34:74:34 | c | 0.0 |
|
||||
| test.cpp:75:22:75:30 | c_times_2 | 0.0 |
|
||||
| test.cpp:77:5:77:13 | c_times_2 | 0.0 |
|
||||
| test.cpp:79:3:79:11 | c_times_2 | 0.0 |
|
||||
|
||||
@@ -68,3 +68,13 @@ bool test3(bool b, int x, int y) {
|
||||
|
||||
return b || (bool)t;
|
||||
}
|
||||
|
||||
void use_after_cast(unsigned char c)
|
||||
{
|
||||
unsigned short c_times_2 = c + c;
|
||||
if ((unsigned char)c_times_2 == 0)
|
||||
{
|
||||
c_times_2;
|
||||
}
|
||||
c_times_2;
|
||||
}
|
||||
|
||||
@@ -459,3 +459,8 @@
|
||||
| test.cpp:67:13:67:14 | xb | 1.0 |
|
||||
| test.cpp:69:10:69:10 | b | 1.0 |
|
||||
| test.cpp:69:21:69:21 | t | 3.0 |
|
||||
| test.cpp:74:30:74:30 | c | 255.0 |
|
||||
| test.cpp:74:34:74:34 | c | 255.0 |
|
||||
| test.cpp:75:22:75:30 | c_times_2 | 510.0 |
|
||||
| test.cpp:77:5:77:13 | c_times_2 | 510.0 |
|
||||
| test.cpp:79:3:79:11 | c_times_2 | 510.0 |
|
||||
|
||||
@@ -207,7 +207,7 @@ enum my_enum {
|
||||
};
|
||||
|
||||
int myFunction6(enum my_enum e) {
|
||||
if (e < 0) {
|
||||
if (e < 0) { // GOOD (suppressed because it's platform-dependent)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -266,7 +266,57 @@ int negative_zero(double dbl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nan(double x) {
|
||||
typedef unsigned char u8;
|
||||
|
||||
int widening_cast1(u8 c) {
|
||||
if (c == 0) {
|
||||
if ((int)c > 0) { // BAD
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int widening_cast2(u8 c) {
|
||||
if (c <= 10)
|
||||
return -1;
|
||||
else if ((c >= 11) /* BAD */ && (c <= 47))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unsigned_implicit_conversion(unsigned int ui1) {
|
||||
// These two comparisons are supported by the range analysis because the
|
||||
// implicit signedness conversion is on the constants (0 and 5), not on the
|
||||
// variables (ui1).
|
||||
if (ui1 == 0) {
|
||||
if (ui1 >= 5) { // BAD
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int signedness_cast1(u8 c) {
|
||||
if ((signed char)c == 0) {
|
||||
if (c >= 5) { // BAD
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int signedness_cast2(signed char c) {
|
||||
if ((u8)c == 0) {
|
||||
if (c >= 5) { // BAD
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nan1(double x) {
|
||||
if (x < 0.0) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
| PointlessComparison.c:129:12:129:16 | ... > ... | Comparison is always false because a <= 3. |
|
||||
| PointlessComparison.c:197:7:197:11 | ... < ... | Comparison is always false because x >= 0. |
|
||||
| PointlessComparison.c:264:12:264:22 | ... >= ... | Comparison is always true because dbl >= 0 and -0 >= - .... |
|
||||
| PointlessComparison.c:287:14:287:21 | ... >= ... | Comparison is always true because x >= 0. |
|
||||
| PointlessComparison.c:273:9:273:18 | ... > ... | Comparison is always false because c <= 0. |
|
||||
| PointlessComparison.c:283:13:283:19 | ... >= ... | Comparison is always true because c >= 11. |
|
||||
| PointlessComparison.c:294:9:294:16 | ... >= ... | Comparison is always false because ui1 <= 0. |
|
||||
| PointlessComparison.c:303:9:303:14 | ... >= ... | Comparison is always false because c <= 0. |
|
||||
| PointlessComparison.c:312:9:312:14 | ... >= ... | Comparison is always false because c <= 0. |
|
||||
| PointlessComparison.c:337:14:337:21 | ... >= ... | Comparison is always true because x >= 0. |
|
||||
| RegressionTests.cpp:57:7:57:22 | ... <= ... | Comparison is always true because * ... <= 4294967295. |
|
||||
| Templates.cpp:9:10:9:24 | ... <= ... | Comparison is always true because local <= 32767. |
|
||||
|
||||
@@ -103,3 +103,15 @@ void test7(const char *strings) // separated by \0, terminated by \0\0
|
||||
strings += strlen(strings) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void concat_strings(char *buf, size_t buf_len, const char **strings, size_t n_strings) {
|
||||
while (n_strings > 0) {
|
||||
int ret = snprintf(buf, buf_len, "%s", *strings); // GOOD
|
||||
if (ret > buf_len)
|
||||
return;
|
||||
buf_len -= ret;
|
||||
buf += ret;
|
||||
n_strings--;
|
||||
strings++;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user