Merge pull request #3062 from geoffw0/alloc-size

C++: Improve hasUpperBoundsCheck
This commit is contained in:
Jonas Jensen
2020-04-07 19:31:04 +02:00
committed by GitHub
5 changed files with 161 additions and 24 deletions

View File

@@ -24,6 +24,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
| No space for zero terminator (`cpp/no-space-for-terminator`) | More correct results | String arguments to formatting functions are now (usually) expected to be null terminated strings. |
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | Fewer false positive results | Cases where the tainted allocation size is range checked are now more reliably excluded. |
| Overloaded assignment does not return 'this' (`cpp/assignment-does-not-return-this`) | Fewer false positive results | This query no longer reports incorrect results in template classes. |
| Unsafe array for days of the year (`cpp/leap-year/unsafe-array-for-days-of-the-year`) | | This query is no longer run on LGTM. |
| Unsigned comparison to zero (`cpp/unsigned-comparison-zero`) | More correct results | This query now also looks for comparisons of the form `0 <= x`. |

View File

@@ -144,7 +144,17 @@ private predicate writesVariable(StoreInstruction store, Variable var) {
}
/**
* A variable that has any kind of upper-bound check anywhere in the program
* A variable that has any kind of upper-bound check anywhere in the program. This is
* biased towards being inclusive because there are a lot of valid ways of doing an
* upper bounds checks if we don't consider where it occurs, for example:
* ```
* if (x < 10) { sink(x); }
*
* if (10 > y) { sink(y); }
*
* if (z > 10) { z = 10; }
* sink(z);
* ```
*/
// TODO: This coarse overapproximation, ported from the old taint tracking
// library, could be replaced with an actual semantic check that a particular
@@ -153,10 +163,10 @@ private predicate writesVariable(StoreInstruction store, Variable var) {
// previously suppressed by this predicate by coincidence.
private predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
oper.getLeftOperand() = access and
oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
not oper.getRightOperand().getValue() = "0"
not oper.getAnOperand().getValue() = "0"
)
}

View File

@@ -328,14 +328,24 @@ GlobalOrNamespaceVariable globalVarFromId(string id) {
}
/**
* A variable that has any kind of upper-bound check anywhere in the program
* A variable that has any kind of upper-bound check anywhere in the program. This is
* biased towards being inclusive because there are a lot of valid ways of doing an
* upper bounds checks if we don't consider where it occurs, for example:
* ```
* if (x < 10) { sink(x); }
*
* if (10 > y) { sink(y); }
*
* if (z > 10) { z = 10; }
* sink(z);
* ```
*/
private predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
oper.getLeftOperand() = access and
oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
not oper.getRightOperand().getValue() = "0"
not oper.getAnOperand().getValue() = "0"
)
}

View File

@@ -39,12 +39,36 @@ edges
| test.cpp:39:21:39:24 | argv | test.cpp:52:54:52:60 | tainted |
| test.cpp:39:21:39:24 | argv | test.cpp:52:54:52:60 | tainted |
| test.cpp:39:21:39:24 | argv | test.cpp:52:54:52:60 | tainted |
| test.cpp:123:25:123:30 | call to getenv | test.cpp:127:24:127:27 | (size_t)... |
| test.cpp:123:25:123:30 | call to getenv | test.cpp:127:24:127:27 | size |
| test.cpp:123:25:123:30 | call to getenv | test.cpp:127:24:127:27 | size |
| test.cpp:123:25:123:38 | (const char *)... | test.cpp:127:24:127:27 | (size_t)... |
| test.cpp:123:25:123:38 | (const char *)... | test.cpp:127:24:127:27 | size |
| test.cpp:123:25:123:38 | (const char *)... | test.cpp:127:24:127:27 | size |
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:27 | (unsigned long)... |
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:27 | size |
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:27 | size |
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... |
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... |
| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:27 | (unsigned long)... |
| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:27 | size |
| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:27 | size |
| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... |
| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... |
| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:13 | (unsigned long)... |
| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:13 | size |
| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:13 | size |
| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... |
| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... |
| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:13 | (unsigned long)... |
| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:13 | size |
| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:13 | size |
| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... |
| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... |
| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:14 | (unsigned long)... |
| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:14 | size |
| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:14 | size |
| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... |
| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... |
| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:14 | (unsigned long)... |
| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:14 | size |
| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:14 | size |
| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... |
| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... |
nodes
| test.cpp:39:21:39:24 | argv | semmle.label | argv |
| test.cpp:39:21:39:24 | argv | semmle.label | argv |
@@ -80,13 +104,36 @@ nodes
| test.cpp:52:54:52:60 | tainted | semmle.label | tainted |
| test.cpp:52:54:52:60 | tainted | semmle.label | tainted |
| test.cpp:52:54:52:60 | tainted | semmle.label | tainted |
| test.cpp:123:25:123:30 | call to getenv | semmle.label | call to getenv |
| test.cpp:123:25:123:38 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:127:24:127:27 | (size_t)... | semmle.label | (size_t)... |
| test.cpp:127:24:127:27 | (size_t)... | semmle.label | (size_t)... |
| test.cpp:123:18:123:23 | call to getenv | semmle.label | call to getenv |
| test.cpp:123:18:123:31 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:127:24:127:27 | (unsigned long)... | semmle.label | (unsigned long)... |
| test.cpp:127:24:127:27 | (unsigned long)... | semmle.label | (unsigned long)... |
| test.cpp:127:24:127:27 | size | semmle.label | size |
| test.cpp:127:24:127:27 | size | semmle.label | size |
| test.cpp:127:24:127:27 | size | semmle.label | size |
| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... |
| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... |
| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... |
| test.cpp:132:19:132:24 | call to getenv | semmle.label | call to getenv |
| test.cpp:132:19:132:32 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:134:10:134:13 | (unsigned long)... | semmle.label | (unsigned long)... |
| test.cpp:134:10:134:13 | (unsigned long)... | semmle.label | (unsigned long)... |
| test.cpp:134:10:134:13 | size | semmle.label | size |
| test.cpp:134:10:134:13 | size | semmle.label | size |
| test.cpp:134:10:134:13 | size | semmle.label | size |
| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... |
| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... |
| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... |
| test.cpp:138:19:138:24 | call to getenv | semmle.label | call to getenv |
| test.cpp:138:19:138:32 | (const char *)... | semmle.label | (const char *)... |
| test.cpp:142:11:142:14 | (unsigned long)... | semmle.label | (unsigned long)... |
| test.cpp:142:11:142:14 | (unsigned long)... | semmle.label | (unsigned long)... |
| test.cpp:142:11:142:14 | size | semmle.label | size |
| test.cpp:142:11:142:14 | size | semmle.label | size |
| test.cpp:142:11:142:14 | size | semmle.label | size |
| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... |
| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... |
| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... |
#select
| test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
@@ -96,4 +143,9 @@ nodes
| test.cpp:49:17:49:30 | new[] | test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:52:21:52:27 | call to realloc | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:52:35:52:60 | ... * ... | test.cpp:39:21:39:24 | argv | test.cpp:52:54:52:60 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:127:17:127:22 | call to malloc | test.cpp:123:25:123:30 | call to getenv | test.cpp:127:24:127:27 | size | This allocation size is derived from $@ and might overflow | test.cpp:123:25:123:30 | call to getenv | user input (getenv) |
| test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) |
| test.cpp:127:24:127:41 | ... * ... | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:27 | size | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) |
| test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) |
| test.cpp:134:10:134:27 | ... * ... | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:13 | size | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) |
| test.cpp:142:4:142:9 | call to malloc | test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:138:19:138:24 | call to getenv | user input (getenv) |
| test.cpp:142:11:142:28 | ... * ... | test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:14 | size | This allocation size is derived from $@ and might overflow | test.cpp:138:19:138:24 | call to getenv | user input (getenv) |

View File

@@ -39,10 +39,10 @@ int main(int argc, char **argv) {
int tainted = atoi(argv[1]);
MyStruct *arr1 = (MyStruct *)malloc(sizeof(MyStruct)); // GOOD
MyStruct *arr2 = (MyStruct *)malloc(tainted); // BAD
MyStruct *arr2 = (MyStruct *)malloc(tainted); // DUBIOUS (not multiplied by anything)
MyStruct *arr3 = (MyStruct *)malloc(tainted * sizeof(MyStruct)); // BAD
MyStruct *arr4 = (MyStruct *)malloc(getTainted() * sizeof(MyStruct)); // BAD [NOT DETECTED]
MyStruct *arr5 = (MyStruct *)malloc(sizeof(MyStruct) + tainted); // BAD [NOT DETECTED]
MyStruct *arr5 = (MyStruct *)malloc(sizeof(MyStruct) + tainted); // DUBIOUS (not multiplied by anything)
int size = tainted * 8;
char *chars1 = (char *)malloc(size); // BAD
@@ -52,7 +52,7 @@ int main(int argc, char **argv) {
arr1 = (MyStruct *)realloc(arr1, sizeof(MyStruct) * tainted); // BAD
size = 8;
chars3 = new char[size]; // GOOD [FALSE POSITIVE]
chars3 = new char[size]; // GOOD
return 0;
}
@@ -120,9 +120,73 @@ int bounded(int x, int limit) {
}
void open_file_bounded () {
int size = size = atoi(getenv("USER"));
int size = atoi(getenv("USER"));
int bounded_size = bounded(size, MAX_SIZE);
int* a = (int*)malloc(bounded_size); // GOOD
int* b = (int*)malloc(size); // BAD
}
int* a = (int*)malloc(bounded_size * sizeof(int)); // GOOD
int* b = (int*)malloc(size * sizeof(int)); // BAD
}
void more_bounded_tests() {
{
int size = atoi(getenv("USER"));
malloc(size * sizeof(int)); // BAD
}
{
int size = atoi(getenv("USER"));
if (size > 0)
{
malloc(size * sizeof(int)); // BAD
}
}
{
int size = atoi(getenv("USER"));
if (size < 100)
{
malloc(size * sizeof(int)); // BAD [NOT DETECTED]
}
}
{
int size = atoi(getenv("USER"));
if ((size > 0) && (size < 100))
{
malloc(size * sizeof(int)); // GOOD
}
}
{
int size = atoi(getenv("USER"));
if ((100 > size) && (0 < size))
{
malloc(size * sizeof(int)); // GOOD
}
}
{
int size = atoi(getenv("USER"));
malloc(size * sizeof(int)); // BAD [NOT DETECTED]
if ((size > 0) && (size < 100))
{
// ...
}
}
{
int size = atoi(getenv("USER"));
if (size > 100)
{
malloc(size * sizeof(int)); // BAD [NOT DETECTED]
}
}
}