diff --git a/.gitignore b/.gitignore index 3e907c5..18dcd40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .cache +*.html *.testproj *.lock.yaml *.lock.yml @@ -6,4 +7,4 @@ cpp-runtime-values-db exercises-tests/*/test.c settings.json -.DS_Store \ No newline at end of file +.DS_Store diff --git a/README.md b/README.md index d92f997..721c83c 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,30 @@ This workshop is based on a significantly simplified and modified version of the ## Setup Instructions - Install [Visual Studio Code](https://code.visualstudio.com/). + - Install the [CodeQL extension for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code/). + - Install the latest version of the [CodeQL CLI](https://github.com/github/codeql-cli-binaries/releases). + - Clone this repository: ```bash git clone https://github.com/kraiouchkine/codeql-workshop-runtime-values-c ``` -- Install the CodeQL pack dependencies using the command `CodeQL: Install Pack Dependencies` and select `exercises`, `solutions`, `exercises-tests`, and `solutions-tests` from the list of packs. -- If you have CodeQL on your PATH, build the database using `build-database.sh` and load the database with the VS Code CodeQL extension. + +- Install the CodeQL pack dependencies using the command `CodeQL: Install Pack + Dependencies` and select `exercises`, `solutions`, `exercises-tests`, `session`, + `session-db` and `solutions-tests` from the list of packs. + +- If you have CodeQL on your PATH, build the database using `build-database.sh` + and load the database with the VS Code CodeQL extension. It is at + `session-db/cpp-runtime-values-db`. - Alternatively, you can download [this pre-built database](https://drive.google.com/file/d/1N8TYJ6f4E33e6wuyorWHZHVCHBZy8Bhb/view?usp=sharing). + +- If you do **not** have CodeQL on your PATH, build the database using the unit + test sytem. Choose the `TESTING` tab in VS Code, run the + `session-db/DB/db.qlref` test. The test will fail, but it leaves a usable CodeQL + database in `session-db/DB/DB.testproj`. + - :exclamation:Important:exclamation:: Run `initialize-qltests.sh` to initialize the tests. Otherwise, you will not be able to run the QLTests in `exercises-tests`. ## Introduction @@ -63,12 +78,22 @@ This workshop is not intended to be a complete analysis that is useful for real- The goal of this workshop is rather to demonstrate the building blocks of analyzing run-time values and how to apply those building blocks to modelling a common class of vulnerability. A more comprehensive and production-appropriate example is the [OutOfBounds.qll library](https://github.com/github/codeql-coding-standards/blob/main/c/common/src/codingstandards/c/OutOfBounds.qll) from the [CodeQL Coding Standards repository](https://github.com/github/codeql-coding-standards). -## Exercises +## Session/Workshop notes +Unlike the the [exercises](#org3b74422) which use the *collection* of test +problems in `exercises-test`, a workshop follows `session/session.ql` and uses a +*single* database built from a single, larger segment of code. + + +## Exercises +These exercises use the collection of test problems in `exercises-test`. + ### Exercise 1 In the first exercise we are going to start by modelling a dynamic allocation with `malloc` and an access to that allocated buffer with an array expression. The goal of this exercise is to then output the array access, buffer, array size, and buffer offset. The [first test-case](solutions-tests/Exercise1/test.c) is a simple one, as both the allocation size and array offsets are constants. +For this exercise, connect the allocation(s), the array accesses, and the sizes in each. + Run the query and ensure that you have three results. #### Hints @@ -76,7 +101,14 @@ Run the query and ensure that you have three results. 2. Use `DataFlow::localExprFlow()` to relate the allocated buffer to the array base. ### Exercise 2 -This exercise uses the same C source code, duplicated for the test [here](solutions-tests/Exercise2/test.c). +This exercise uses the same C source code with an addition: a constant array size +propagated [via a variable](solutions-tests/Exercise2/test.c). + +Hints: +1. start with plain `from...where...select` query. +2. use + `elementSize = access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize()` +2. convert your query to predicate or use classes as outlined below, if desired. #### Task 1 With the basic elements of the analysis in place, refactor the query into two classes: `AllocationCall` and `ArrayAccess`. The `AllocationCall` class should model a call to `malloc` and the `ArrayAccess` class should model an array access expression (`ArrayExpr`). @@ -90,6 +122,8 @@ Use local data-flow analysis to complete the `getSourceConstantExpr` predicate. ### Exercise 3 This exercise has slightly more C source code [here](solutions-tests/Exercise3/test.c). +Note: the `test_const_branch` has `buf[100]` with size == 100 + Running the query from Exercise 2 against the database yields a significant number of missing or incorrect results. The reason is that although great at identifying compile-time constants and their use, data-flow analysis is not always the right tool for identifying the *range* of values an `Expr` might have, particularly when multiple potential constants might flow to an `Expr`. The CodeQL standard library several mechanisms for addressing this problem; in the remainder of this workshop we will explore two of them: `SimpleRangeAnalysis` and, later, `GlobalValueNumbering`. @@ -110,6 +144,12 @@ Implement the `isOffsetOutOfBoundsConstant` predicate to check if the array offs You should now have five results in the test (six in the built database). ### Exercise 4 +Note: We *could* evolve this code to handle `size`s inside conditionals using +guards on data flow. But this amounts to implementing a small interpreter. +The range analysis library already handles conditional branches; we don't +have to use guards on data flow -- don't implement your own interpreter if you can +use the library. + Again, a slight longer C [source snippet](solutions-tests/Exercise4/test.c). A common issue with the `SimpleRangeAnalysis` library is handling of cases where the bounds are undeterminable at compile-time on one or more paths. For example, even though certain branches have clearly defined bounds, the range analysis library will define the `upperBound` and `lowerBound` of `val` as `INT_MIN` and `INT_MAX` respectively: @@ -164,4 +204,40 @@ Do not compute the GVN of the entire array index expression; use the base of an Exclude duplicate results by only reporting `isOffsetOutOfBoundsGVN` for `access`/`source` pairs that are not already reported by `isOffsetOutOfBoundsConstant`. -You should now see thirteen results. \ No newline at end of file +You should now see thirteen results. + +Some notes: + +Global value numbering only knows that runtime values are equal; they are not +comparable (`<, >, <=` etc.), and the *actual* value is not known. +Reference: https://codeql.github.com/docs/codeql-language-guides/hash-consing-and-value-numbering/ + + +In the query, look for and use *relative* values between allocation and use. To +do this, use GVN. +This is the case in + + void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) + { + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT + } + +Range analyis won't bound `sz * x * y`, so switch to global value numbering. + +Global value numbering finds expressions that are known to have the same runtime +value, independent of structure. To get the Global Value Number in CodeQL: + + ... + globalValueNumber(e) = globalValueNumber(sizeExpr) and + e != sizeExpr + ... + +We can use global value numbering to identify common values as first step, but for +expressions like + + buf[sz * x * y - 1]; // COMPLIANT + +we have to "evaluate" the expressions -- or at least bound them. diff --git a/build-database.sh b/build-database.sh index fad5699..fb3141c 100755 --- a/build-database.sh +++ b/build-database.sh @@ -1,3 +1,4 @@ -SRCDIR=$(pwd)/solutions-tests/Exercise6 -DB=$(pwd)/cpp-runtime-values-db -codeql database create --language=cpp -s "$SRCDIR" -j 8 -v $DB --command="clang -fsyntax-only -Wno-unused-value $SRCDIR/test.c" \ No newline at end of file +#!/bin/bash +SRCDIR=$(pwd)/session-db +DB=$SRCDIR/cpp-runtime-values-db +codeql database create --language=cpp -s "$SRCDIR" -j 8 -v $DB --command="clang -fsyntax-only -Wno-unused-value $SRCDIR/DB/db.c" diff --git a/codeql-workshop-runtime-values-c.code-workspace b/codeql-workshop-runtime-values-c.code-workspace index 23f4f79..8acda77 100644 --- a/codeql-workshop-runtime-values-c.code-workspace +++ b/codeql-workshop-runtime-values-c.code-workspace @@ -5,6 +5,7 @@ } ], "settings": { - "sarif-viewer.connectToGithubCodeScanning": "off" + "sarif-viewer.connectToGithubCodeScanning": "off", + "codeQL.runningQueries.autoSave": true } } \ No newline at end of file diff --git a/library/RuntimeValues.qll b/library/RuntimeValues.qll new file mode 100644 index 0000000..c3a37da --- /dev/null +++ b/library/RuntimeValues.qll @@ -0,0 +1,16 @@ +import cpp + +bindingset[expr] +int getExprOffsetValue(Expr expr, Expr base) { + result = expr.(AddExpr).getRightOperand().getValue().toInt() and + base = expr.(AddExpr).getLeftOperand() + or + result = -expr.(SubExpr).getRightOperand().getValue().toInt() and + base = expr.(SubExpr).getLeftOperand() + or + // currently only AddExpr and SubExpr are supported: else, fall-back to 0 + not expr instanceof AddExpr and + not expr instanceof SubExpr and + base = expr and + result = 0 +} diff --git a/library/qlpack.yml b/library/qlpack.yml new file mode 100644 index 0000000..6629dc7 --- /dev/null +++ b/library/qlpack.yml @@ -0,0 +1,6 @@ +--- +library: true +name: library +version: 0.0.1 +dependencies: + codeql/cpp-all: 0.6.1 \ No newline at end of file diff --git a/session-db/DB/db.c b/session-db/DB/db.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-db/DB/db.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-db/DB/db.expected b/session-db/DB/db.expected new file mode 100644 index 0000000..6ee8899 --- /dev/null +++ b/session-db/DB/db.expected @@ -0,0 +1,3 @@ +| test.c:4:5:4:10 | access to array | 0 | test.c:3:17:3:22 | call to malloc | 100 | +| test.c:5:5:5:11 | access to array | 99 | test.c:3:17:3:22 | call to malloc | 100 | +| test.c:6:5:6:12 | access to array | 100 | test.c:3:17:3:22 | call to malloc | 100 | diff --git a/session-db/DB/db.qlref b/session-db/DB/db.qlref new file mode 100644 index 0000000..21829a4 --- /dev/null +++ b/session-db/DB/db.qlref @@ -0,0 +1 @@ +session.ql \ No newline at end of file diff --git a/session-db/qlpack.yml b/session-db/qlpack.yml new file mode 100644 index 0000000..f98375c --- /dev/null +++ b/session-db/qlpack.yml @@ -0,0 +1,8 @@ +--- +library: false +name: session-db +version: 0.0.1 +dependencies: + "session": "*" +extractor: cpp +tests: . diff --git a/session-tests/Example1/example1.expected b/session-tests/Example1/example1.expected new file mode 100644 index 0000000..99dad7a --- /dev/null +++ b/session-tests/Example1/example1.expected @@ -0,0 +1,12 @@ +| test.c:7:17:7:22 | call to malloc | test.c:8:5:8:10 | access to array | 0 | test.c:8:9:8:9 | 0 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:9:5:9:11 | access to array | 99 | test.c:9:9:9:10 | 99 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:10:5:10:12 | access to array | 100 | test.c:10:9:10:11 | 100 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:17:5:17:10 | access to array | 0 | test.c:17:9:17:9 | 0 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:18:5:18:11 | access to array | 99 | test.c:18:9:18:10 | 99 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:20:5:20:12 | access to array | 100 | test.c:20:9:20:11 | 100 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:35:5:35:10 | access to array | 0 | test.c:35:9:35:9 | 0 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:36:5:36:11 | access to array | 99 | test.c:36:9:36:10 | 99 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:38:5:38:12 | access to array | 100 | test.c:38:9:38:11 | 100 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:65:5:65:10 | access to array | 0 | test.c:65:9:65:9 | 0 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:66:5:66:12 | access to array | 100 | test.c:66:9:66:11 | 100 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:67:5:67:12 | access to array | 200 | test.c:67:9:67:11 | 200 | 100 | test.c:7:24:7:26 | 100 | diff --git a/session-tests/Example1/example1.qlref b/session-tests/Example1/example1.qlref new file mode 100644 index 0000000..71b7202 --- /dev/null +++ b/session-tests/Example1/example1.qlref @@ -0,0 +1 @@ +example1.ql diff --git a/session-tests/Example1/test.c b/session-tests/Example1/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example1/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example2/example2.expected b/session-tests/Example2/example2.expected new file mode 100644 index 0000000..d6650b2 --- /dev/null +++ b/session-tests/Example2/example2.expected @@ -0,0 +1,3 @@ +| test.c:7:17:7:22 | call to malloc | test.c:8:5:8:10 | access to array | 0 | test.c:8:9:8:9 | 0 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:9:5:9:11 | access to array | 99 | test.c:9:9:9:10 | 99 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:10:5:10:12 | access to array | 100 | test.c:10:9:10:11 | 100 | 100 | test.c:7:24:7:26 | 100 | diff --git a/session-tests/Example2/example2.qlref b/session-tests/Example2/example2.qlref new file mode 100644 index 0000000..ab91f7f --- /dev/null +++ b/session-tests/Example2/example2.qlref @@ -0,0 +1 @@ +example2.ql diff --git a/session-tests/Example2/test.c b/session-tests/Example2/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example2/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example3/example3.expected b/session-tests/Example3/example3.expected new file mode 100644 index 0000000..4679be3 --- /dev/null +++ b/session-tests/Example3/example3.expected @@ -0,0 +1,12 @@ +| test.c:7:17:7:22 | call to malloc | test.c:8:5:8:10 | access to array | 0 | test.c:8:9:8:9 | 0 | +| test.c:7:17:7:22 | call to malloc | test.c:9:5:9:11 | access to array | 99 | test.c:9:9:9:10 | 99 | +| test.c:7:17:7:22 | call to malloc | test.c:10:5:10:12 | access to array | 100 | test.c:10:9:10:11 | 100 | +| test.c:16:17:16:22 | call to malloc | test.c:17:5:17:10 | access to array | 0 | test.c:17:9:17:9 | 0 | +| test.c:16:17:16:22 | call to malloc | test.c:18:5:18:11 | access to array | 99 | test.c:18:9:18:10 | 99 | +| test.c:16:17:16:22 | call to malloc | test.c:20:5:20:12 | access to array | 100 | test.c:20:9:20:11 | 100 | +| test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | 0 | test.c:35:9:35:9 | 0 | +| test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | 99 | test.c:36:9:36:10 | 99 | +| test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | 100 | test.c:38:9:38:11 | 100 | +| test.c:63:17:63:22 | call to malloc | test.c:65:5:65:10 | access to array | 0 | test.c:65:9:65:9 | 0 | +| test.c:63:17:63:22 | call to malloc | test.c:66:5:66:12 | access to array | 100 | test.c:66:9:66:11 | 100 | +| test.c:63:17:63:22 | call to malloc | test.c:67:5:67:12 | access to array | 200 | test.c:67:9:67:11 | 200 | diff --git a/session-tests/Example3/example3.qlref b/session-tests/Example3/example3.qlref new file mode 100644 index 0000000..949ef16 --- /dev/null +++ b/session-tests/Example3/example3.qlref @@ -0,0 +1 @@ +example3.ql diff --git a/session-tests/Example3/test.c b/session-tests/Example3/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example3/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example4/example4.expected b/session-tests/Example4/example4.expected new file mode 100644 index 0000000..88b6955 --- /dev/null +++ b/session-tests/Example4/example4.expected @@ -0,0 +1,15 @@ +| test.c:7:17:7:22 | call to malloc | test.c:8:5:8:10 | access to array | 0 | test.c:8:9:8:9 | 0 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:9:5:9:11 | access to array | 99 | test.c:9:9:9:10 | 99 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:10:5:10:12 | access to array | 100 | test.c:10:9:10:11 | 100 | 100 | test.c:7:24:7:26 | 100 | +| test.c:16:17:16:22 | call to malloc | test.c:17:5:17:10 | access to array | 0 | test.c:17:9:17:9 | 0 | 100 | test.c:15:26:15:28 | 100 | +| test.c:16:17:16:22 | call to malloc | test.c:18:5:18:11 | access to array | 99 | test.c:18:9:18:10 | 99 | 100 | test.c:15:26:15:28 | 100 | +| test.c:16:17:16:22 | call to malloc | test.c:20:5:20:12 | access to array | 100 | test.c:20:9:20:11 | 100 | 100 | test.c:15:26:15:28 | 100 | +| test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | 0 | test.c:35:9:35:9 | 0 | 100 | test.c:26:39:26:41 | 100 | +| test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | 0 | test.c:35:9:35:9 | 0 | 200 | test.c:26:45:26:47 | 200 | +| test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | 99 | test.c:36:9:36:10 | 99 | 100 | test.c:26:39:26:41 | 100 | +| test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | 99 | test.c:36:9:36:10 | 99 | 200 | test.c:26:45:26:47 | 200 | +| test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | 100 | test.c:38:9:38:11 | 100 | 100 | test.c:26:39:26:41 | 100 | +| test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | 100 | test.c:38:9:38:11 | 100 | 200 | test.c:26:45:26:47 | 200 | +| test.c:63:17:63:22 | call to malloc | test.c:65:5:65:10 | access to array | 0 | test.c:65:9:65:9 | 0 | 200 | test.c:55:22:55:24 | 200 | +| test.c:63:17:63:22 | call to malloc | test.c:66:5:66:12 | access to array | 100 | test.c:66:9:66:11 | 100 | 200 | test.c:55:22:55:24 | 200 | +| test.c:63:17:63:22 | call to malloc | test.c:67:5:67:12 | access to array | 200 | test.c:67:9:67:11 | 200 | 200 | test.c:55:22:55:24 | 200 | diff --git a/session-tests/Example4/example4.qlref b/session-tests/Example4/example4.qlref new file mode 100644 index 0000000..b14578e --- /dev/null +++ b/session-tests/Example4/example4.qlref @@ -0,0 +1 @@ +example4.ql diff --git a/session-tests/Example4/test.c b/session-tests/Example4/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example4/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example4a/example4a.expected b/session-tests/Example4a/example4a.expected new file mode 100644 index 0000000..88b6955 --- /dev/null +++ b/session-tests/Example4a/example4a.expected @@ -0,0 +1,15 @@ +| test.c:7:17:7:22 | call to malloc | test.c:8:5:8:10 | access to array | 0 | test.c:8:9:8:9 | 0 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:9:5:9:11 | access to array | 99 | test.c:9:9:9:10 | 99 | 100 | test.c:7:24:7:26 | 100 | +| test.c:7:17:7:22 | call to malloc | test.c:10:5:10:12 | access to array | 100 | test.c:10:9:10:11 | 100 | 100 | test.c:7:24:7:26 | 100 | +| test.c:16:17:16:22 | call to malloc | test.c:17:5:17:10 | access to array | 0 | test.c:17:9:17:9 | 0 | 100 | test.c:15:26:15:28 | 100 | +| test.c:16:17:16:22 | call to malloc | test.c:18:5:18:11 | access to array | 99 | test.c:18:9:18:10 | 99 | 100 | test.c:15:26:15:28 | 100 | +| test.c:16:17:16:22 | call to malloc | test.c:20:5:20:12 | access to array | 100 | test.c:20:9:20:11 | 100 | 100 | test.c:15:26:15:28 | 100 | +| test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | 0 | test.c:35:9:35:9 | 0 | 100 | test.c:26:39:26:41 | 100 | +| test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | 0 | test.c:35:9:35:9 | 0 | 200 | test.c:26:45:26:47 | 200 | +| test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | 99 | test.c:36:9:36:10 | 99 | 100 | test.c:26:39:26:41 | 100 | +| test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | 99 | test.c:36:9:36:10 | 99 | 200 | test.c:26:45:26:47 | 200 | +| test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | 100 | test.c:38:9:38:11 | 100 | 100 | test.c:26:39:26:41 | 100 | +| test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | 100 | test.c:38:9:38:11 | 100 | 200 | test.c:26:45:26:47 | 200 | +| test.c:63:17:63:22 | call to malloc | test.c:65:5:65:10 | access to array | 0 | test.c:65:9:65:9 | 0 | 200 | test.c:55:22:55:24 | 200 | +| test.c:63:17:63:22 | call to malloc | test.c:66:5:66:12 | access to array | 100 | test.c:66:9:66:11 | 100 | 200 | test.c:55:22:55:24 | 200 | +| test.c:63:17:63:22 | call to malloc | test.c:67:5:67:12 | access to array | 200 | test.c:67:9:67:11 | 200 | 200 | test.c:55:22:55:24 | 200 | diff --git a/session-tests/Example4a/example4a.qlref b/session-tests/Example4a/example4a.qlref new file mode 100644 index 0000000..d588285 --- /dev/null +++ b/session-tests/Example4a/example4a.qlref @@ -0,0 +1 @@ +example4a.ql diff --git a/session-tests/Example4a/test.c b/session-tests/Example4a/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example4a/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example5/example5.expected b/session-tests/Example5/example5.expected new file mode 100644 index 0000000..079512f --- /dev/null +++ b/session-tests/Example5/example5.expected @@ -0,0 +1,32 @@ +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:8:5:8:10 | access to array | test.c:8:9:8:9 | 0 | 0.0 | +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:9:5:9:11 | access to array | test.c:9:9:9:10 | 99 | 99.0 | +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:10:5:10:12 | access to array | test.c:10:9:10:11 | 100 | 100.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:17:5:17:10 | access to array | test.c:17:9:17:9 | 0 | 0.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:18:5:18:11 | access to array | test.c:18:9:18:10 | 99 | 99.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:19:5:19:17 | access to array | test.c:19:9:19:16 | ... - ... | 99.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:20:5:20:12 | access to array | test.c:20:9:20:11 | 100 | 100.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:21:5:21:13 | access to array | test.c:21:9:21:12 | size | 100.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | test.c:35:9:35:9 | 0 | 0.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | test.c:36:9:36:10 | 99 | 99.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:37:5:37:17 | access to array | test.c:37:9:37:16 | ... - ... | 299.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | test.c:38:9:38:11 | 100 | 100.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | 300.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | 198.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:44:9:44:21 | access to array | test.c:44:13:44:20 | ... + ... | 199.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:45:9:45:21 | access to array | test.c:45:13:45:20 | ... + ... | 200.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | test.c:35:9:35:9 | 0 | 0.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | test.c:36:9:36:10 | 99 | 99.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:37:5:37:17 | access to array | test.c:37:9:37:16 | ... - ... | 299.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | test.c:38:9:38:11 | 100 | 100.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | 300.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | 198.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:44:9:44:21 | access to array | test.c:44:13:44:20 | ... + ... | 199.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:45:9:45:21 | access to array | test.c:45:13:45:20 | ... + ... | 200.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:65:5:65:10 | access to array | test.c:65:9:65:9 | 0 | 0.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:66:5:66:12 | access to array | test.c:66:9:66:11 | 100 | 100.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:67:5:67:12 | access to array | test.c:67:9:67:11 | 200 | 200.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:68:5:68:23 | access to array | test.c:68:9:68:22 | ... - ... | 1.8446744073709552E19 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:69:5:69:19 | access to array | test.c:69:9:69:18 | alloc_size | 1.8446744073709552E19 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:73:9:73:23 | access to array | test.c:73:13:73:22 | alloc_size | 198.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:74:9:74:27 | access to array | test.c:74:13:74:26 | ... + ... | 199.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:75:9:75:27 | access to array | test.c:75:13:75:26 | ... + ... | 200.0 | diff --git a/session-tests/Example5/example5.qlref b/session-tests/Example5/example5.qlref new file mode 100644 index 0000000..5419e8b --- /dev/null +++ b/session-tests/Example5/example5.qlref @@ -0,0 +1 @@ +example5.ql diff --git a/session-tests/Example5/test.c b/session-tests/Example5/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example5/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example6/example6.expected b/session-tests/Example6/example6.expected new file mode 100644 index 0000000..e6f2ac1 --- /dev/null +++ b/session-tests/Example6/example6.expected @@ -0,0 +1,32 @@ +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:8:5:8:10 | access to array | test.c:8:9:8:9 | 0 | 0.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:9:5:9:11 | access to array | test.c:9:9:9:10 | 99 | 99.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:10:5:10:12 | access to array | test.c:10:9:10:11 | 100 | 100.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:17:5:17:10 | access to array | test.c:17:9:17:9 | 0 | 0.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:18:5:18:11 | access to array | test.c:18:9:18:10 | 99 | 99.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:19:5:19:17 | access to array | test.c:19:9:19:16 | ... - ... | 99.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:20:5:20:12 | access to array | test.c:20:9:20:11 | 100 | 100.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:21:5:21:13 | access to array | test.c:21:9:21:12 | size | 100.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | test.c:35:9:35:9 | 0 | 0.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | test.c:36:9:36:10 | 99 | 99.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:37:5:37:17 | access to array | test.c:37:9:37:16 | ... - ... | 299.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | test.c:38:9:38:11 | 100 | 100.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | 300.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | 198.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:44:9:44:21 | access to array | test.c:44:13:44:20 | ... + ... | 199.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:45:9:45:21 | access to array | test.c:45:13:45:20 | ... + ... | 200.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | test.c:35:9:35:9 | 0 | 0.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | test.c:36:9:36:10 | 99 | 99.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:37:5:37:17 | access to array | test.c:37:9:37:16 | ... - ... | 299.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | test.c:38:9:38:11 | 100 | 100.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | 300.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | 198.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:44:9:44:21 | access to array | test.c:44:13:44:20 | ... + ... | 199.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:45:9:45:21 | access to array | test.c:45:13:45:20 | ... + ... | 200.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:65:5:65:10 | access to array | test.c:65:9:65:9 | 0 | 0.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:66:5:66:12 | access to array | test.c:66:9:66:11 | 100 | 100.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:67:5:67:12 | access to array | test.c:67:9:67:11 | 200 | 200.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:68:5:68:23 | access to array | test.c:68:9:68:22 | ... - ... | 1.8446744073709552E19 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:69:5:69:19 | access to array | test.c:69:9:69:18 | alloc_size | 1.8446744073709552E19 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:73:9:73:23 | access to array | test.c:73:13:73:22 | alloc_size | 198.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:74:9:74:27 | access to array | test.c:74:13:74:26 | ... + ... | 199.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:75:9:75:27 | access to array | test.c:75:13:75:26 | ... + ... | 200.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | diff --git a/session-tests/Example6/example6.qlref b/session-tests/Example6/example6.qlref new file mode 100644 index 0000000..2c38a28 --- /dev/null +++ b/session-tests/Example6/example6.qlref @@ -0,0 +1 @@ +example6.ql diff --git a/session-tests/Example6/test.c b/session-tests/Example6/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example6/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example7/example7.expected b/session-tests/Example7/example7.expected new file mode 100644 index 0000000..b2a1ce7 --- /dev/null +++ b/session-tests/Example7/example7.expected @@ -0,0 +1,32 @@ +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:8:5:8:10 | access to array | test.c:8:9:8:9 | 0 | 0.0 | 100 | file://:0:0:0:0 | char | 100 | 0.0 | +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:9:5:9:11 | access to array | test.c:9:9:9:10 | 99 | 99.0 | 100 | file://:0:0:0:0 | char | 100 | 99.0 | +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:10:5:10:12 | access to array | test.c:10:9:10:11 | 100 | 100.0 | 100 | file://:0:0:0:0 | char | 100 | 100.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:17:5:17:10 | access to array | test.c:17:9:17:9 | 0 | 0.0 | 100 | file://:0:0:0:0 | char | 100 | 0.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:18:5:18:11 | access to array | test.c:18:9:18:10 | 99 | 99.0 | 100 | file://:0:0:0:0 | char | 100 | 99.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:19:5:19:17 | access to array | test.c:19:9:19:16 | ... - ... | 99.0 | 100 | file://:0:0:0:0 | char | 100 | 99.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:20:5:20:12 | access to array | test.c:20:9:20:11 | 100 | 100.0 | 100 | file://:0:0:0:0 | char | 100 | 100.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:21:5:21:13 | access to array | test.c:21:9:21:12 | size | 100.0 | 100 | file://:0:0:0:0 | char | 100 | 100.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | test.c:35:9:35:9 | 0 | 0.0 | 100 | file://:0:0:0:0 | char | 100 | 0.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | test.c:36:9:36:10 | 99 | 99.0 | 100 | file://:0:0:0:0 | char | 100 | 99.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:37:5:37:17 | access to array | test.c:37:9:37:16 | ... - ... | 299.0 | 100 | file://:0:0:0:0 | char | 100 | 299.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | test.c:38:9:38:11 | 100 | 100.0 | 100 | file://:0:0:0:0 | char | 100 | 100.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | 300.0 | 100 | file://:0:0:0:0 | char | 100 | 300.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | 198.0 | 100 | file://:0:0:0:0 | char | 100 | 198.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:44:9:44:21 | access to array | test.c:44:13:44:20 | ... + ... | 199.0 | 100 | file://:0:0:0:0 | char | 100 | 199.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:45:9:45:21 | access to array | test.c:45:13:45:20 | ... + ... | 200.0 | 100 | file://:0:0:0:0 | char | 100 | 200.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | test.c:35:9:35:9 | 0 | 0.0 | 200 | file://:0:0:0:0 | char | 200 | 0.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | test.c:36:9:36:10 | 99 | 99.0 | 200 | file://:0:0:0:0 | char | 200 | 99.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:37:5:37:17 | access to array | test.c:37:9:37:16 | ... - ... | 299.0 | 200 | file://:0:0:0:0 | char | 200 | 299.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | test.c:38:9:38:11 | 100 | 100.0 | 200 | file://:0:0:0:0 | char | 200 | 100.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | 300.0 | 200 | file://:0:0:0:0 | char | 200 | 300.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | 198.0 | 200 | file://:0:0:0:0 | char | 200 | 198.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:44:9:44:21 | access to array | test.c:44:13:44:20 | ... + ... | 199.0 | 200 | file://:0:0:0:0 | char | 200 | 199.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:45:9:45:21 | access to array | test.c:45:13:45:20 | ... + ... | 200.0 | 200 | file://:0:0:0:0 | char | 200 | 200.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:65:5:65:10 | access to array | test.c:65:9:65:9 | 0 | 0.0 | 200 | file://:0:0:0:0 | char | 200 | 0.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:66:5:66:12 | access to array | test.c:66:9:66:11 | 100 | 100.0 | 200 | file://:0:0:0:0 | char | 200 | 100.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:67:5:67:12 | access to array | test.c:67:9:67:11 | 200 | 200.0 | 200 | file://:0:0:0:0 | char | 200 | 200.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:68:5:68:23 | access to array | test.c:68:9:68:22 | ... - ... | 1.8446744073709552E19 | 200 | file://:0:0:0:0 | char | 200 | 1.8446744073709552E19 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:69:5:69:19 | access to array | test.c:69:9:69:18 | alloc_size | 1.8446744073709552E19 | 200 | file://:0:0:0:0 | char | 200 | 1.8446744073709552E19 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:73:9:73:23 | access to array | test.c:73:13:73:22 | alloc_size | 198.0 | 200 | file://:0:0:0:0 | char | 200 | 198.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:74:9:74:27 | access to array | test.c:74:13:74:26 | ... + ... | 199.0 | 200 | file://:0:0:0:0 | char | 200 | 199.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:75:9:75:27 | access to array | test.c:75:13:75:26 | ... + ... | 200.0 | 200 | file://:0:0:0:0 | char | 200 | 200.0 | diff --git a/session-tests/Example7/example7.qlref b/session-tests/Example7/example7.qlref new file mode 100644 index 0000000..5b0fec2 --- /dev/null +++ b/session-tests/Example7/example7.qlref @@ -0,0 +1 @@ +example7.ql diff --git a/session-tests/Example7/test.c b/session-tests/Example7/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example7/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example7a/example7a.expected b/session-tests/Example7a/example7a.expected new file mode 100644 index 0000000..1211e12 --- /dev/null +++ b/session-tests/Example7a/example7a.expected @@ -0,0 +1,32 @@ +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:8:5:8:10 | access to array | test.c:8:9:8:9 | 0 | 0.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 0.0 | +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:9:5:9:11 | access to array | test.c:9:9:9:10 | 99 | 99.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 99.0 | +| test.c:7:24:7:26 | 100 | test.c:7:17:7:22 | call to malloc | test.c:10:5:10:12 | access to array | test.c:10:9:10:11 | 100 | 100.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 100.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:17:5:17:10 | access to array | test.c:17:9:17:9 | 0 | 0.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 0.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:18:5:18:11 | access to array | test.c:18:9:18:10 | 99 | 99.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 99.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:19:5:19:17 | access to array | test.c:19:9:19:16 | ... - ... | 99.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 99.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:20:5:20:12 | access to array | test.c:20:9:20:11 | 100 | 100.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 100.0 | +| test.c:15:26:15:28 | 100 | test.c:16:17:16:22 | call to malloc | test.c:21:5:21:13 | access to array | test.c:21:9:21:12 | size | 100.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 100.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | test.c:35:9:35:9 | 0 | 0.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 0.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | test.c:36:9:36:10 | 99 | 99.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 99.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:37:5:37:17 | access to array | test.c:37:9:37:16 | ... - ... | 299.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 299.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | test.c:38:9:38:11 | 100 | 100.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 100.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | 300.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 300.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | 198.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 198.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:44:9:44:21 | access to array | test.c:44:13:44:20 | ... + ... | 199.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 199.0 | +| test.c:26:39:26:41 | 100 | test.c:28:17:28:22 | call to malloc | test.c:45:9:45:21 | access to array | test.c:45:13:45:20 | ... + ... | 200.0 | 100 | file://:0:0:0:0 | char | 1 | 1 | 100 | 200.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:35:5:35:10 | access to array | test.c:35:9:35:9 | 0 | 0.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 0.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:36:5:36:11 | access to array | test.c:36:9:36:10 | 99 | 99.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 99.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:37:5:37:17 | access to array | test.c:37:9:37:16 | ... - ... | 299.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 299.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:38:5:38:12 | access to array | test.c:38:9:38:11 | 100 | 100.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 100.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | 300.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 300.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | 198.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 198.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:44:9:44:21 | access to array | test.c:44:13:44:20 | ... + ... | 199.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 199.0 | +| test.c:26:45:26:47 | 200 | test.c:28:17:28:22 | call to malloc | test.c:45:9:45:21 | access to array | test.c:45:13:45:20 | ... + ... | 200.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 200.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:65:5:65:10 | access to array | test.c:65:9:65:9 | 0 | 0.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 0.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:66:5:66:12 | access to array | test.c:66:9:66:11 | 100 | 100.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 100.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:67:5:67:12 | access to array | test.c:67:9:67:11 | 200 | 200.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 200.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:68:5:68:23 | access to array | test.c:68:9:68:22 | ... - ... | 1.8446744073709552E19 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 1.8446744073709552E19 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:69:5:69:19 | access to array | test.c:69:9:69:18 | alloc_size | 1.8446744073709552E19 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 1.8446744073709552E19 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:73:9:73:23 | access to array | test.c:73:13:73:22 | alloc_size | 198.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 198.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:74:9:74:27 | access to array | test.c:74:13:74:26 | ... + ... | 199.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 199.0 | +| test.c:55:22:55:24 | 200 | test.c:63:17:63:22 | call to malloc | test.c:75:9:75:27 | access to array | test.c:75:13:75:26 | ... + ... | 200.0 | 200 | file://:0:0:0:0 | char | 1 | 1 | 200 | 200.0 | diff --git a/session-tests/Example7a/example7a.qlref b/session-tests/Example7a/example7a.qlref new file mode 100644 index 0000000..92b49c1 --- /dev/null +++ b/session-tests/Example7a/example7a.qlref @@ -0,0 +1 @@ +example7a.ql diff --git a/session-tests/Example7a/test.c b/session-tests/Example7a/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example7a/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example7b/example7b.expected b/session-tests/Example7b/example7b.expected new file mode 100644 index 0000000..fdf766e --- /dev/null +++ b/session-tests/Example7b/example7b.expected @@ -0,0 +1,15 @@ +WARNING: Unused predicate computeIndices (/Users/hohn/local/codeql-workshop-runtime-values-c/session/example7b.ql:66,11-25) +| test.c:10:5:10:12 | access to array | Array access at or beyond size; have 100 units, access at 100 | +| test.c:20:5:20:12 | access to array | Array access at or beyond size; have 100 units, access at 100 | +| test.c:21:5:21:13 | access to array | Array access at or beyond size; have 100 units, access at 100 | +| test.c:37:5:37:17 | access to array | Array access at or beyond size; have 100 units, access at 299 | +| test.c:37:5:37:17 | access to array | Array access at or beyond size; have 200 units, access at 299 | +| test.c:38:5:38:12 | access to array | Array access at or beyond size; have 100 units, access at 100 | +| test.c:39:5:39:13 | access to array | Array access at or beyond size; have 100 units, access at 300 | +| test.c:39:5:39:13 | access to array | Array access at or beyond size; have 200 units, access at 300 | +| test.c:43:9:43:17 | access to array | Array access at or beyond size; have 100 units, access at 198 | +| test.c:44:9:44:21 | access to array | Array access at or beyond size; have 100 units, access at 199 | +| test.c:45:9:45:21 | access to array | Array access at or beyond size; have 100 units, access at 200 | +| test.c:45:9:45:21 | access to array | Array access at or beyond size; have 200 units, access at 200 | +| test.c:67:5:67:12 | access to array | Array access at or beyond size; have 200 units, access at 200 | +| test.c:75:9:75:27 | access to array | Array access at or beyond size; have 200 units, access at 200 | diff --git a/session-tests/Example7b/example7b.qlref b/session-tests/Example7b/example7b.qlref new file mode 100644 index 0000000..6f8007b --- /dev/null +++ b/session-tests/Example7b/example7b.qlref @@ -0,0 +1 @@ +example7b.ql diff --git a/session-tests/Example7b/test.c b/session-tests/Example7b/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example7b/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example8/example8.expected b/session-tests/Example8/example8.expected new file mode 100644 index 0000000..407663f --- /dev/null +++ b/session-tests/Example8/example8.expected @@ -0,0 +1,12 @@ +| test.c:16:17:16:22 | call to malloc | test.c:16:24:16:27 | size | 0 | test.c:19:5:19:17 | access to array | test.c:19:9:19:12 | size | -1 | test.c:15:19:15:22 | size | test.c:15:19:15:22 | size | +| test.c:16:17:16:22 | call to malloc | test.c:16:24:16:27 | size | 0 | test.c:21:5:21:13 | access to array | test.c:21:9:21:12 | size | 0 | test.c:15:19:15:22 | size | test.c:15:19:15:22 | size | +| test.c:28:17:28:22 | call to malloc | test.c:28:24:28:27 | size | 0 | test.c:37:5:37:17 | access to array | test.c:37:9:37:12 | size | -1 | test.c:26:19:26:22 | size | test.c:26:19:26:22 | size | +| test.c:28:17:28:22 | call to malloc | test.c:28:24:28:27 | size | 0 | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | 0 | test.c:26:19:26:22 | size | test.c:26:19:26:22 | size | +| test.c:28:17:28:22 | call to malloc | test.c:28:24:28:27 | size | 0 | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | 0 | test.c:26:19:26:22 | size | test.c:26:19:26:22 | size | +| test.c:28:17:28:22 | call to malloc | test.c:28:24:28:27 | size | 0 | test.c:44:9:44:21 | access to array | test.c:44:13:44:16 | size | 1 | test.c:26:19:26:22 | size | test.c:26:19:26:22 | size | +| test.c:28:17:28:22 | call to malloc | test.c:28:24:28:27 | size | 0 | test.c:45:9:45:21 | access to array | test.c:45:13:45:16 | size | 2 | test.c:26:19:26:22 | size | test.c:26:19:26:22 | size | +| test.c:63:17:63:22 | call to malloc | test.c:63:24:63:33 | alloc_size | 0 | test.c:68:5:68:23 | access to array | test.c:68:9:68:18 | alloc_size | -1 | test.c:51:19:51:28 | alloc_size | test.c:51:19:51:28 | alloc_size | +| test.c:63:17:63:22 | call to malloc | test.c:63:24:63:33 | alloc_size | 0 | test.c:69:5:69:19 | access to array | test.c:69:9:69:18 | alloc_size | 0 | test.c:51:19:51:28 | alloc_size | test.c:51:19:51:28 | alloc_size | +| test.c:63:17:63:22 | call to malloc | test.c:63:24:63:33 | alloc_size | 0 | test.c:73:9:73:23 | access to array | test.c:73:13:73:22 | alloc_size | 0 | test.c:51:19:51:28 | alloc_size | test.c:51:19:51:28 | alloc_size | +| test.c:63:17:63:22 | call to malloc | test.c:63:24:63:33 | alloc_size | 0 | test.c:74:9:74:27 | access to array | test.c:74:13:74:22 | alloc_size | 1 | test.c:51:19:51:28 | alloc_size | test.c:51:19:51:28 | alloc_size | +| test.c:63:17:63:22 | call to malloc | test.c:63:24:63:33 | alloc_size | 0 | test.c:75:9:75:27 | access to array | test.c:75:13:75:22 | alloc_size | 2 | test.c:51:19:51:28 | alloc_size | test.c:51:19:51:28 | alloc_size | diff --git a/session-tests/Example8/example8.qlref b/session-tests/Example8/example8.qlref new file mode 100644 index 0000000..20f0e3f --- /dev/null +++ b/session-tests/Example8/example8.qlref @@ -0,0 +1 @@ +example8.ql diff --git a/session-tests/Example8/test.c b/session-tests/Example8/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example8/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example9/example9.expected b/session-tests/Example9/example9.expected new file mode 100644 index 0000000..c7fec59 --- /dev/null +++ b/session-tests/Example9/example9.expected @@ -0,0 +1,9 @@ +| test.c:21:5:21:13 | access to array | test.c:15:26:15:28 | GVN | test.c:15:26:15:28 | 100 | test.c:16:24:16:27 | size | test.c:15:26:15:28 | GVN | test.c:21:9:21:12 | size | 0 | +| test.c:21:5:21:13 | access to array | test.c:15:26:15:28 | GVN | test.c:16:24:16:27 | size | test.c:16:24:16:27 | size | test.c:15:26:15:28 | GVN | test.c:21:9:21:12 | size | 0 | +| test.c:38:5:38:12 | access to array | test.c:26:39:26:41 | GVN | test.c:26:39:26:41 | 100 | test.c:28:24:28:27 | size | test.c:26:39:26:41 | GVN | test.c:38:9:38:11 | 100 | 0 | +| test.c:69:5:69:19 | access to array | test.c:63:24:63:33 | GVN | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | GVN | test.c:69:9:69:18 | alloc_size | 0 | +| test.c:73:9:73:23 | access to array | test.c:63:24:63:33 | GVN | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | GVN | test.c:73:13:73:22 | alloc_size | 0 | +| test.c:74:9:74:27 | access to array | test.c:63:24:63:33 | GVN | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | alloc_size | test.c:74:13:74:26 | GVN | test.c:74:13:74:26 | ... + ... | 1 | +| test.c:75:9:75:27 | access to array | test.c:63:24:63:33 | GVN | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | alloc_size | test.c:75:13:75:26 | GVN | test.c:75:13:75:26 | ... + ... | 2 | +| test.c:83:5:83:19 | access to array | test.c:81:24:81:33 | GVN | test.c:81:24:81:33 | ... * ... | test.c:81:24:81:33 | ... * ... | test.c:81:24:81:33 | GVN | test.c:83:9:83:18 | ... * ... | 0 | +| test.c:84:5:84:23 | access to array | test.c:81:24:81:33 | GVN | test.c:81:24:81:33 | ... * ... | test.c:81:24:81:33 | ... * ... | test.c:84:9:84:22 | GVN | test.c:84:9:84:22 | ... + ... | 1 | diff --git a/session-tests/Example9/example9.qlref b/session-tests/Example9/example9.qlref new file mode 100644 index 0000000..2d30c37 --- /dev/null +++ b/session-tests/Example9/example9.qlref @@ -0,0 +1 @@ +example9.ql diff --git a/session-tests/Example9/test.c b/session-tests/Example9/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example9/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/Example9a/Example9a.expected b/session-tests/Example9a/Example9a.expected new file mode 100644 index 0000000..31e9a5f --- /dev/null +++ b/session-tests/Example9a/Example9a.expected @@ -0,0 +1,9 @@ +| test.c:21:5:21:13 | access to array | test.c:15:26:15:28 | GVN | test.c:15:26:15:28 | 100 | test.c:16:24:16:27 | size | test.c:15:26:15:28 | GVN | test.c:21:9:21:12 | size | test.c:21:9:21:12 | size | 0 | +| test.c:21:5:21:13 | access to array | test.c:15:26:15:28 | GVN | test.c:16:24:16:27 | size | test.c:16:24:16:27 | size | test.c:15:26:15:28 | GVN | test.c:21:9:21:12 | size | test.c:21:9:21:12 | size | 0 | +| test.c:38:5:38:12 | access to array | test.c:26:39:26:41 | GVN | test.c:26:39:26:41 | 100 | test.c:28:24:28:27 | size | test.c:26:39:26:41 | GVN | test.c:38:9:38:11 | 100 | test.c:38:9:38:11 | 100 | 0 | +| test.c:69:5:69:19 | access to array | test.c:63:24:63:33 | GVN | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | GVN | test.c:69:9:69:18 | alloc_size | test.c:69:9:69:18 | alloc_size | 0 | +| test.c:73:9:73:23 | access to array | test.c:63:24:63:33 | GVN | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | GVN | test.c:73:13:73:22 | alloc_size | test.c:73:13:73:22 | alloc_size | 0 | +| test.c:74:9:74:27 | access to array | test.c:63:24:63:33 | GVN | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | alloc_size | test.c:74:13:74:26 | GVN | test.c:74:13:74:26 | ... + ... | test.c:74:13:74:22 | alloc_size | 1 | +| test.c:75:9:75:27 | access to array | test.c:63:24:63:33 | GVN | test.c:63:24:63:33 | alloc_size | test.c:63:24:63:33 | alloc_size | test.c:75:13:75:26 | GVN | test.c:75:13:75:26 | ... + ... | test.c:75:13:75:22 | alloc_size | 2 | +| test.c:83:5:83:19 | access to array | test.c:81:24:81:33 | GVN | test.c:81:24:81:33 | ... * ... | test.c:81:24:81:33 | ... * ... | test.c:81:24:81:33 | GVN | test.c:83:9:83:18 | ... * ... | test.c:83:9:83:18 | ... * ... | 0 | +| test.c:84:5:84:23 | access to array | test.c:81:24:81:33 | GVN | test.c:81:24:81:33 | ... * ... | test.c:81:24:81:33 | ... * ... | test.c:84:9:84:22 | GVN | test.c:84:9:84:22 | ... + ... | test.c:84:9:84:18 | ... * ... | 1 | diff --git a/session-tests/Example9a/Example9a.qlref b/session-tests/Example9a/Example9a.qlref new file mode 100644 index 0000000..b2b29f8 --- /dev/null +++ b/session-tests/Example9a/Example9a.qlref @@ -0,0 +1 @@ +Example9a.ql diff --git a/session-tests/Example9a/test.c b/session-tests/Example9a/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/Example9a/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/example8a/example8a.expected b/session-tests/example8a/example8a.expected new file mode 100644 index 0000000..501d32d --- /dev/null +++ b/session-tests/example8a/example8a.expected @@ -0,0 +1,9 @@ +| test.c:16:17:16:22 | call to malloc | test.c:16:24:16:27 | size | test.c:21:5:21:13 | access to array | test.c:21:9:21:12 | size | test.c:15:19:15:22 | size | 0 | test.c:15:19:15:22 | size | 0 | +| test.c:28:17:28:22 | call to malloc | test.c:28:24:28:27 | size | test.c:39:5:39:13 | access to array | test.c:39:9:39:12 | size | test.c:26:19:26:22 | size | 0 | test.c:26:19:26:22 | size | 0 | +| test.c:28:17:28:22 | call to malloc | test.c:28:24:28:27 | size | test.c:43:9:43:17 | access to array | test.c:43:13:43:16 | size | test.c:26:19:26:22 | size | 0 | test.c:26:19:26:22 | size | 0 | +| test.c:28:17:28:22 | call to malloc | test.c:28:24:28:27 | size | test.c:44:9:44:21 | access to array | test.c:44:13:44:16 | size | test.c:26:19:26:22 | size | 0 | test.c:26:19:26:22 | size | 1 | +| test.c:28:17:28:22 | call to malloc | test.c:28:24:28:27 | size | test.c:45:9:45:21 | access to array | test.c:45:13:45:16 | size | test.c:26:19:26:22 | size | 0 | test.c:26:19:26:22 | size | 2 | +| test.c:63:17:63:22 | call to malloc | test.c:63:24:63:33 | alloc_size | test.c:69:5:69:19 | access to array | test.c:69:9:69:18 | alloc_size | test.c:51:19:51:28 | alloc_size | 0 | test.c:51:19:51:28 | alloc_size | 0 | +| test.c:63:17:63:22 | call to malloc | test.c:63:24:63:33 | alloc_size | test.c:73:9:73:23 | access to array | test.c:73:13:73:22 | alloc_size | test.c:51:19:51:28 | alloc_size | 0 | test.c:51:19:51:28 | alloc_size | 0 | +| test.c:63:17:63:22 | call to malloc | test.c:63:24:63:33 | alloc_size | test.c:74:9:74:27 | access to array | test.c:74:13:74:22 | alloc_size | test.c:51:19:51:28 | alloc_size | 0 | test.c:51:19:51:28 | alloc_size | 1 | +| test.c:63:17:63:22 | call to malloc | test.c:63:24:63:33 | alloc_size | test.c:75:9:75:27 | access to array | test.c:75:13:75:22 | alloc_size | test.c:51:19:51:28 | alloc_size | 0 | test.c:51:19:51:28 | alloc_size | 2 | diff --git a/session-tests/example8a/example8a.qlref b/session-tests/example8a/example8a.qlref new file mode 100644 index 0000000..770eadf --- /dev/null +++ b/session-tests/example8a/example8a.qlref @@ -0,0 +1 @@ +example8a.ql diff --git a/session-tests/example8a/test.c b/session-tests/example8a/test.c new file mode 100644 index 0000000..6eaa405 --- /dev/null +++ b/session-tests/example8a/test.c @@ -0,0 +1,85 @@ +void *malloc(unsigned long);/* clang compatible */ + +unsigned long extern_get_size(void); + +void test_const(void) +{ + char *buf = malloc(100); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[100]; // NON_COMPLIANT +} + +void test_const_var(void) +{ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // COMPLIANT + buf[100]; // NON_COMPLIANT + buf[size]; // NON_COMPLIANT +} + +void test_const_branch(int mode, int random_condition) +{ + unsigned long size = (mode == 1 ? 100 : 200); + + char *buf = malloc(size); + + if (random_condition) + { + size = 300; + } + + buf[0]; // COMPLIANT + buf[99]; // COMPLIANT + buf[size - 1]; // NON_COMPLIANT + buf[100]; // NON_COMPLIANT[DONT REPORT] + buf[size]; // NON_COMPLIANT + + if (size < 199) + { + buf[size]; // COMPLIANT + buf[size + 1]; // COMPLIANT + buf[size + 2]; // NON_COMPLIANT + } +} + +void test_const_branch2(int mode) +{ + unsigned long alloc_size = 0; + + if (mode == 1) + { + alloc_size = 200; + } + else + { + // unknown const size - don't report accesses + alloc_size = extern_get_size(); + } + + char *buf = malloc(alloc_size); + + buf[0]; // COMPLIANT + buf[100]; // COMPLIANT + buf[200]; // NON_COMPLIANT + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + + if (alloc_size < 199) + { + buf[alloc_size]; // COMPLIANT + buf[alloc_size + 1]; // COMPLIANT + buf[alloc_size + 2]; // NON_COMPLIANT + } +} + +void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) +{ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT +} diff --git a/session-tests/qlpack.yml b/session-tests/qlpack.yml new file mode 100644 index 0000000..b191d63 --- /dev/null +++ b/session-tests/qlpack.yml @@ -0,0 +1,8 @@ +--- +library: false +name: session-tests +version: 0.0.1 +dependencies: + "session": "*" +extractor: cpp +tests: . \ No newline at end of file diff --git a/session/Example9a.ql b/session/Example9a.ql new file mode 100644 index 0000000..98e5f3d --- /dev/null +++ b/session/Example9a.ql @@ -0,0 +1,64 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.valuenumbering.GlobalValueNumbering +import semmle.code.cpp.valuenumbering.HashCons + +from + AllocationExpr buffer, ArrayExpr access, Expr allocSizeExpr, Expr accessIdx, GVN gvnAccessIdx, + GVN gvnAllocSizeExpr, int accessOffset, + // +++ + Expr allocArg, Expr accessBase +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // buf[...] + // ^^^ ArrayExpr access + // buf[...] + // ^^^ accessIdx + accessIdx = access.getArrayOffset() and + // Find allocation size expression flowing to the allocation. + DataFlow::localExprFlow(allocSizeExpr, buffer.getSizeExpr()) and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Use GVN + globalValueNumber(accessIdx) = gvnAccessIdx and + globalValueNumber(allocSizeExpr) = gvnAllocSizeExpr and + ( + // buf[size] or buf[100] + gvnAccessIdx = gvnAllocSizeExpr and + accessOffset = 0 and + // +++ + accessBase = accessIdx + or + // buf[sz * x * y + 1]; + exists(AddExpr add | + accessIdx = add and + accessOffset >= 0 and + accessOffset = add.getRightOperand().(Literal).getValue().toInt() and + globalValueNumber(add.getLeftOperand()) = gvnAllocSizeExpr and + // +++ + accessBase = add.getLeftOperand() + ) + ) and + buffer.getSizeExpr() = allocArg and + ( + accessOffset >= 0 and + // +++ + // Illustrating the subtle meanings of equality: + // 0 results: + // (accessBase = allocSizeExpr or accessBase = allocArg) + // Only 6 results: + // ( + // gvnAccessIdx = gvnAllocSizeExpr or + // gvnAccessIdx = globalValueNumber(allocArg) + // ) + // 9 results: + ( + hashCons(accessBase) = hashCons(allocSizeExpr) or + hashCons(accessBase) = hashCons(allocArg) + ) + ) +// gvnAccessIdx = globalValueNumber(allocArg)) +// +++ overview select: +select access, gvnAllocSizeExpr, allocSizeExpr, allocArg, gvnAccessIdx, accessIdx, accessBase, + accessOffset diff --git a/session/example1.ql b/session/example1.ql new file mode 100644 index 0000000..4d26746 --- /dev/null +++ b/session/example1.ql @@ -0,0 +1,22 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +from AllocationExpr buffer, ArrayExpr access, int bufferSize, int accessIdx, Expr allocSizeExpr +where + // malloc (100) + // ^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + allocSizeExpr = buffer.(Call).getArgument(0) and + bufferSize = allocSizeExpr.getValue().toInt() +select buffer, access, accessIdx, access.getArrayOffset(), bufferSize, allocSizeExpr diff --git a/session/example2.ql b/session/example2.ql new file mode 100644 index 0000000..d5f4306 --- /dev/null +++ b/session/example2.ql @@ -0,0 +1,34 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +// Step 2 +// void test_const(void) +// void test_const_var(void) +from AllocationExpr buffer, ArrayExpr access, int bufferSize, int accessIdx, Expr allocSizeExpr +where + // malloc (100) + // ^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + allocSizeExpr = buffer.(Call).getArgument(0) and + bufferSize = allocSizeExpr.getValue().toInt() and + // + // Ensure buffer access is to the correct allocation. + // char *buf = ... buf[0]; + // ^^^ ---> ^^^ + // or + // malloc(100); buf[0] + // ^^^ --------> ^^^ + // + DataFlow::localExprFlow(buffer, access.getArrayBase()) +select buffer, access, accessIdx, access.getArrayOffset(), bufferSize, allocSizeExpr diff --git a/session/example3.ql b/session/example3.ql new file mode 100644 index 0000000..267d79b --- /dev/null +++ b/session/example3.ql @@ -0,0 +1,33 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +// Step 3 +// void test_const_var(void) +from AllocationExpr buffer, ArrayExpr access, int accessIdx, Expr allocSizeExpr +where + // malloc (100) + // ^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + allocSizeExpr = buffer.(Call).getArgument(0) and + // bufferSize = allocSizeExpr.getValue().toInt() and + // + // Ensure buffer access is to the correct allocation. + // char *buf = ... buf[0]; + // ^^^ ---> ^^^ + // or + // malloc(100); buf[0] + // ^^^ --------> ^^^ + // + DataFlow::localExprFlow(buffer, access.getArrayBase()) +select buffer, access, accessIdx, access.getArrayOffset() diff --git a/session/example4.ql b/session/example4.ql new file mode 100644 index 0000000..b0bdf63 --- /dev/null +++ b/session/example4.ql @@ -0,0 +1,40 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +// Step 4 +from AllocationExpr buffer, ArrayExpr access, int accessIdx, Expr allocSizeExpr, int bufferSize, Expr bse +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + allocSizeExpr = buffer.(Call).getArgument(0) and + // bufferSize = allocSizeExpr.getValue().toInt() and + // + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + exists(Expr bufferSizeExpr | + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + and bse = bufferSizeExpr + ) and + // Ensure buffer access is to the correct allocation. + // char *buf = ... buf[0]; + // ^^^ ---> ^^^ + // or + // malloc(100); buf[0] + // ^^^ --------> ^^^ + // + DataFlow::localExprFlow(buffer, access.getArrayBase()) +select buffer, access, accessIdx, access.getArrayOffset(), bufferSize, bse diff --git a/session/example4a.ql b/session/example4a.ql new file mode 100644 index 0000000..3550038 --- /dev/null +++ b/session/example4a.ql @@ -0,0 +1,45 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +from AllocationExpr buffer, ArrayExpr access, int accessIdx, int bufferSize, Expr bufferSizeExpr +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access refers to the matching allocation + // ensureSameFunction(buffer, access.getArrayBase()) and + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure buffer access refers to the matching allocation + // ensureSameFunction(bufferSizeExpr, buffer.getSizeExpr()) and + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) + // +select buffer, access, accessIdx, access.getArrayOffset(), bufferSize, bufferSizeExpr + +/** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ +predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + // 2. + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) +} diff --git a/session/example5.ql b/session/example5.ql new file mode 100644 index 0000000..321d4ac --- /dev/null +++ b/session/example5.ql @@ -0,0 +1,51 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + + +from AllocationExpr buffer, ArrayExpr access, Expr accessIdx, int bufferSize, Expr bufferSizeExpr +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) + // +select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax + +/** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ +predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + // 2. + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) +} + diff --git a/session/example6.ql b/session/example6.ql new file mode 100644 index 0000000..1ef2ca7 --- /dev/null +++ b/session/example6.ql @@ -0,0 +1,52 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from AllocationExpr buffer, ArrayExpr access, Expr accessIdx, int bufferSize, Expr bufferSizeExpr +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) + // +select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax, bufferSize, + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() as arrayBaseType, + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() as arrayTypeSize, + 1 as allocBaseSize + +/** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ +predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + // 2. + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) +} diff --git a/session/example7.ql b/session/example7.ql new file mode 100644 index 0000000..d255bf2 --- /dev/null +++ b/session/example7.ql @@ -0,0 +1,49 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from + AllocationExpr buffer, ArrayExpr access, Expr accessIdx, int bufferSize, Expr bufferSizeExpr, + int arrayTypeSize, int allocBaseSize +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // buf[...] + // ^^^ ArrayExpr access + // buf[...] + // ^^^ int accessIdx + accessIdx = access.getArrayOffset() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // + arrayTypeSize = access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() and + 1 = allocBaseSize +// +select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax, bufferSize, + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() as arrayBaseType, + allocBaseSize * bufferSize as allocatedUnits, arrayTypeSize * accessMax as maxAccessedIndex + +/** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ +predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // 2. + // unsigned long size = 100; ... ; char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) +} diff --git a/session/example7a.ql b/session/example7a.ql new file mode 100644 index 0000000..12fabb2 --- /dev/null +++ b/session/example7a.ql @@ -0,0 +1,46 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from + AllocationExpr buffer, ArrayExpr access, Expr accessIdx, int bufferSize, Expr bufferSizeExpr, + int arrayTypeSize, int allocBaseSize +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // buf[...] + // ^^^^^^^^ ArrayExpr access + // ^^^ int accessIdx + accessIdx = access.getArrayOffset() and + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // + arrayTypeSize = access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() and + 1 = allocBaseSize +// +select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax, bufferSize, + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() as arrayBaseType, + buffer.getSizeMult() as bufferBaseTypeSize, + arrayBaseType.getSize() as arrayBaseTypeSize, + allocBaseSize * bufferSize as allocatedUnits, arrayTypeSize * accessMax as maxAccessedIndex + +/** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ +predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ bufferSize + // 2. + // unsigned long size = 100; ... ; char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) +} diff --git a/session/example7b.ql b/session/example7b.ql new file mode 100644 index 0000000..3daa12f --- /dev/null +++ b/session/example7b.ql @@ -0,0 +1,100 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from + AllocationExpr buffer, ArrayExpr access, int bufferSize, Expr bufferSizeExpr, + int maxAccessedIndex, int allocatedUnits +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // computeIndices(access, buffer, bufferSize, allocatedUnits, maxAccessedIndex) + computeAllocationSize(buffer, bufferSize, allocatedUnits) and + computeMaxAccess(access, maxAccessedIndex) + // only consider out-of-bounds + and + maxAccessedIndex >= allocatedUnits +select access, + "Array access at or beyond size; have " + allocatedUnits + " units, access at " + maxAccessedIndex + +// select bufferSizeExpr, buffer, access, allocatedUnits, maxAccessedIndex + +/** + * Compute the maximum accessed index. + */ +predicate computeMaxAccess(ArrayExpr access, int maxAccessedIndex) { + exists( + int arrayTypeSize, int accessMax, Type arrayBaseType, int arrayBaseTypeSize, Expr accessIdx + | + // buf[...] + // ^^^^^^^^ ArrayExpr access + // ^^^ + accessIdx = access.getArrayOffset() and + upperBound(accessIdx) = accessMax and + arrayBaseType.getSize() = arrayBaseTypeSize and + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() = arrayBaseType and + arrayTypeSize = access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() and + arrayTypeSize * accessMax = maxAccessedIndex + ) +} + +/** + * Compute the allocation size. + */ +bindingset[bufferSize] +predicate computeAllocationSize(AllocationExpr buffer, int bufferSize, int allocatedUnits) { + exists(int bufferBaseTypeSize, Type arrayBaseType, int arrayBaseTypeSize | + // buf[...] + // ^^^^^^^^ ArrayExpr access + // ^^^ + buffer.getSizeMult() = bufferBaseTypeSize and + arrayBaseType.getSize() = arrayBaseTypeSize and + bufferSize * bufferBaseTypeSize = allocatedUnits + ) +} + +/** + * Compute the allocation size and the maximum accessed index for the allocation and access. + */ +bindingset[bufferSize] +predicate computeIndices( + ArrayExpr access, AllocationExpr buffer, int bufferSize, int allocatedUnits, int maxAccessedIndex +) { + exists( + int arrayTypeSize, int accessMax, int bufferBaseTypeSize, Type arrayBaseType, + int arrayBaseTypeSize, Expr accessIdx + | + // buf[...] + // ^^^^^^^^ ArrayExpr access + // ^^^ + accessIdx = access.getArrayOffset() and + upperBound(accessIdx) = accessMax and + buffer.getSizeMult() = bufferBaseTypeSize and + arrayBaseType.getSize() = arrayBaseTypeSize and + bufferSize * bufferBaseTypeSize = allocatedUnits and + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() = arrayBaseType and + arrayTypeSize = access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() and + arrayTypeSize * accessMax = maxAccessedIndex + ) +} + +/** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ +predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // Capture BOTH with datflow: + // 1. malloc (100) + // ^^^ bufferSize + // 2. unsigned long size = 100; ... ; char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) +} diff --git a/session/example8.ql b/session/example8.ql new file mode 100644 index 0000000..90a690f --- /dev/null +++ b/session/example8.ql @@ -0,0 +1,59 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from + AllocationExpr buffer, ArrayExpr access, Expr bufferSizeExpr, + // --- + // int maxAccessedIndex, int allocatedUnits, + // int bufferSize + int accessOffset, Expr accessBase, Expr bufferBase, int bufferOffset, Variable bufInit, + Variable accessInit +where + // malloc (...) + // ^^^^^^^^^^^^ AllocationExpr buffer + // --- + // getAllocConstExpr(...) + // +++ + bufferSizeExpr = buffer.getSizeExpr() and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // + // +++ + // base+offset + extractBaseAndOffset(bufferSizeExpr, bufferBase, bufferOffset) and + extractBaseAndOffset(access.getArrayOffset(), accessBase, accessOffset) and + // +++ + // Same initializer variable + bufferBase.(VariableAccess).getTarget() = bufInit and + accessBase.(VariableAccess).getTarget() = accessInit and + bufInit = accessInit +// +++ +// Identify questionable differences +select buffer, bufferBase, bufferOffset, access, accessBase, accessOffset, bufInit, accessInit + +/** + * Extract base and offset from y = base+offset and y = base-offset. For others, get y and 0. + * + * For cases like + * buf[alloc_size + 1]; + * + * The more general + * buf[sz * x * y - 1]; + * requires other tools. + */ +bindingset[expr] +predicate extractBaseAndOffset(Expr expr, Expr base, int offset) { + offset = expr.(AddExpr).getRightOperand().getValue().toInt() and + base = expr.(AddExpr).getLeftOperand() + or + offset = -expr.(SubExpr).getRightOperand().getValue().toInt() and + base = expr.(SubExpr).getLeftOperand() + or + not expr instanceof AddExpr and + not expr instanceof SubExpr and + base = expr and + offset = 0 +} diff --git a/session/example8a.ql b/session/example8a.ql new file mode 100644 index 0000000..cdd46aa --- /dev/null +++ b/session/example8a.ql @@ -0,0 +1,63 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from + AllocationExpr buffer, ArrayExpr access, Expr bufferSizeExpr, + // --- + // int maxAccessedIndex, int allocatedUnits, + // int bufferSize + int accessOffset, Expr accessBase, Expr bufferBase, int bufferOffset, Variable bufInit, + Variable accessInit +where + // malloc (...) + // ^^^^^^^^^^^^ AllocationExpr buffer + // --- + // getAllocConstExpr(...) + // +++ + bufferSizeExpr = buffer.getSizeExpr() and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Find allocation size expression flowing to buffer. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // + // +++ + // base+offset + extractBaseAndOffset(bufferSizeExpr, bufferBase, bufferOffset) and + extractBaseAndOffset(access.getArrayOffset(), accessBase, accessOffset) and + // +++ + // Same initializer variable + bufferBase.(VariableAccess).getTarget() = bufInit and + accessBase.(VariableAccess).getTarget() = accessInit and + bufInit = accessInit and + // +++ + // Identify questionable differences + accessOffset >= bufferOffset +select buffer, bufferBase, access, accessBase, bufInit, bufferOffset, accessInit, accessOffset + +/** + * Extract base and offset from y = base+offset and y = base-offset. For others, get y and 0. + * + * For cases like + * buf[alloc_size + 1]; + * ^^^^^^^^^^^^^^ expr + * ^^^^^^^^^^ base + * ^^^ offset + * + * The more general + * buf[sz * x * y - 1]; + * requires other tools. + */ +bindingset[expr] +predicate extractBaseAndOffset(Expr expr, Expr base, int offset) { + offset = expr.(AddExpr).getRightOperand().getValue().toInt() and + base = expr.(AddExpr).getLeftOperand() + or + offset = -expr.(SubExpr).getRightOperand().getValue().toInt() and + base = expr.(SubExpr).getLeftOperand() + or + not expr instanceof AddExpr and + not expr instanceof SubExpr and + base = expr and + offset = 0 +} diff --git a/session/example9.ql b/session/example9.ql new file mode 100644 index 0000000..0d7ceba --- /dev/null +++ b/session/example9.ql @@ -0,0 +1,41 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +from + AllocationExpr buffer, ArrayExpr access, + // --- + // Expr bufferSizeExpr + // int accessOffset, Expr accessBase, Expr bufferBase, int bufferOffset, Variable bufInit, + // +++ + Expr allocSizeExpr, Expr accessIdx, GVN gvnAccessIdx, GVN gvnAllocSizeExpr, int accessOffset +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // buf[...] + // ^^^ ArrayExpr access + // buf[...] + // ^^^ accessIdx + accessIdx = access.getArrayOffset() and + // Find allocation size expression flowing to the allocation. + DataFlow::localExprFlow(allocSizeExpr, buffer.getSizeExpr()) and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Use GVN + globalValueNumber(accessIdx) = gvnAccessIdx and + globalValueNumber(allocSizeExpr) = gvnAllocSizeExpr and + ( + // buf[size] or buf[100] + gvnAccessIdx = gvnAllocSizeExpr and + accessOffset = 0 + or + // buf[sz * x * y + 1]; + exists(AddExpr add | + accessIdx = add and + accessOffset >= 0 and + accessOffset = add.getRightOperand().(Literal).getValue().toInt() and + globalValueNumber(add.getLeftOperand()) = gvnAllocSizeExpr + ) + ) +select access, gvnAllocSizeExpr, allocSizeExpr, buffer.getSizeExpr() as allocArg, gvnAccessIdx, + accessIdx, accessOffset diff --git a/session/qlpack.yml b/session/qlpack.yml new file mode 100644 index 0000000..6c8e1f8 --- /dev/null +++ b/session/qlpack.yml @@ -0,0 +1,6 @@ +--- +library: false +name: session +version: 0.0.1 +dependencies: + codeql/cpp-all: 0.6.1 diff --git a/session/session.md b/session/session.md new file mode 100644 index 0000000..9db6831 --- /dev/null +++ b/session/session.md @@ -0,0 +1,2928 @@ + +# Table of Contents + +1. [CodeQL Workshop — Using Data-Flow and Range Analysis to Find Out-Of-Bounds Accesses](#codeql-workshop--using-data-flow-and-range-analysis-to-find-out-of-bounds-accesses) +2. [Acknowledgments](#acknowledgments) +3. [Setup Instructions](#setup-instructions) +4. [Introduction](#introduction) +5. [A Note on the Scope of This Workshop](#a-note-on-the-scope-of-this-workshop) +6. [A short note on the structure of directories and their use](#org28d73fb) +7. [Session/Workshop notes](#sessionworkshop-notes) + 1. [Step 1](#exercise-1) + 1. [Hints](#hints) + 2. [Solution](#org4777775) + 3. [First 5 results](#org1aa6a22) + 2. [Step 2](#org1e52aa7) + 1. [Hints](#hints) + 2. [Solution](#org4ba1960) + 3. [First 5 results](#org61872ef) + 3. [Step 3](#exercise-2) + 1. [Solution](#orgffef32c) + 2. [First 5 results](#orga647c8f) + 4. [Step 4](#orgd616664) + 1. [Hint](#orga9ca0e1) + 2. [Solution](#org072f835) + 3. [First 5 results](#orgab7f021) + 5. [Step 4a – some clean-up using predicates](#org74d9df9) + 1. [Solution](#orgd5a3519) + 2. [First 5 results](#orga608103) + 6. [Step 5 – SimpleRangeAnalysis](#org426ad70) + 1. [Solution](#org7c6288c) + 2. [First 5 results](#org338b606) + 7. [Step 6](#orgca8ff14) + 1. [Solution](#orgb24ef12) + 2. [First 5 results](#orgc3c6c20) + 8. [Step 7](#orgeb7c62d) + 1. [Solution](#org8b2cfc4) + 2. [First 5 results](#orgdf5441f) + 9. [Step 7a](#org980fc9e) + 1. [Solution](#org7a58133) + 2. [First 5 results](#org4d2ccdb) + 10. [Step 7b](#orgf204614) + 1. [Solution](#orgb536ad8) + 2. [First 5 results](#org91089f0) + 11. [Step 8](#orgf9da811) + 1. [Solution](#org4d950d1) + 2. [First 5 results](#org012e64b) + 12. [Interim notes](#orgd8277fd) + 13. [Step 8a](#orgdf6dd57) + 1. [Solution](#org2cbb86e) + 2. [First 5 results](#org0c626de) + 14. [Step 9 – Global Value Numbering](#org8474dff) + 1. [Solution](#orga7fc0bc) + 2. [First 5 results](#orgb436331) + 15. [Step 9a – hashconsing](#orgc768b64) + 1. [Solution](#org370d1e6) + 2. [First 5 results](#orgced1d9e) + + + + +# CodeQL Workshop — Using Data-Flow and Range Analysis to Find Out-Of-Bounds Accesses + + + + +# Acknowledgments + +This session-based workshop is based on the exercise/unit-test-based material at +, which in turn is +based on a significantly simplified and modified version of the +[OutOfBounds.qll library](https://github.com/github/codeql-coding-standards/blob/main/c/common/src/codingstandards/c/OutOfBounds.qll) from the +[CodeQL Coding Standards +repository](https://github.com/github/codeql-coding-standards). + + + + +# Setup Instructions + +- Install [Visual Studio Code](https://code.visualstudio.com/). + +- Install the + [CodeQL extension for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code/). + +- Install the latest version of the + [CodeQL CLI](https://github.com/github/codeql-cli-binaries/releases). + +- Clone this repository: + + git clone https://github.com/hohn/codeql-workshop-runtime-values-c + +- Install the CodeQL pack dependencies using the command + `CodeQL: Install Pack Dependencies` and select `exercises`, + `solutions`, `exercises-tests`, `session`, `session-db` and + `solutions-tests` from the list of packs. + +- If you have CodeQL on your PATH, build the database using + `build-database.sh` and load the database with the VS Code CodeQL + extension. It is at `session-db/cpp-runtime-values-db`. + - Alternatively, you can download + [this + pre-built database](https://drive.google.com/file/d/1N8TYJ6f4E33e6wuyorWHZHVCHBZy8Bhb/view?usp=sharing). + +- If you do **not** have CodeQL on your PATH, build the database using the + unit test sytem. Choose the `TESTING` tab in VS Code, run the + `session-db/DB/db.qlref` test. The test will fail, but it leaves a + usable CodeQL database in `session-db/DB/DB.testproj`. + +- ❗Important❗: Run `initialize-qltests.sh` to initialize the tests. + Otherwise, you will not be able to run the QLTests in + `exercises-tests`. + + + + +# Introduction + +This workshop focuses on analyzing and relating two values — array +access indices and memory allocation sizes — in order to identify +simple cases of out-of-bounds array accesses. + +The following snippets demonstrate how an out-of-bounds array access can +occur: + + char* buffer = malloc(10); + buffer[9] = 'a'; // ok + buffer[10] = 'b'; // out-of-bounds + +A more complex example: + + char* buffer; + if(rand() == 1) { + buffer = malloc(10); + } + else { + buffer = malloc(11); + } + size_t index = 0; + if(rand() == 1) { + index = 10; + } + buffer[index]; // potentially out-of-bounds depending on control-flow + +Another common case *not* covered in this introductory workshop involves +loops, as follows: + + int elements[5]; + for (int i = 0; i <= 5; ++i) { + elements[i] = 0; + } + +To find these issues, we can implement an analysis that tracks the upper +or lower bounds on an expression and, combined with data-flow analysis +to reduce false-positives, identifies cases where the index of the array +results in an access beyond the allocated size of the buffer. + + + + +# A Note on the Scope of This Workshop + +This workshop is not intended to be a complete analysis that is useful +for real-world cases of out-of-bounds analyses for reasons including but +not limited to: + +- Missing support for loops and recursion +- No interprocedural analysis +- Missing size calculation of arrays where the element size is not 1 +- No support for pointer arithmetic or in general, operations other than + addition and subtraction +- Overly specific modelling of a buffer access as an array expression + +The goal of this workshop is rather to demonstrate the building blocks +of analyzing run-time values and how to apply those building blocks to +modelling a common class of vulnerability. A more comprehensive and +production-appropriate example is the +[OutOfBounds.qll +library](https://github.com/github/codeql-coding-standards/blob/main/c/common/src/codingstandards/c/OutOfBounds.qll) from the +[CodeQL Coding +Standards repository](https://github.com/github/codeql-coding-standards). + + + + +# A short note on the structure of directories and their use + +`exercises-tests` are identical to `solution-tests`, the `exercises` directories +are a convenience for developing the queries on your own so you can use the unit +tests as reference. This is for full consistency with the workshop material – +the session – but you may veer off and experiment on your own. + +In that case, a simpler option is to follow the session writeup using a single +`.ql` file; the writeup has full queries and (at most) the first 5 results for +reference. + + + + +# Session/Workshop notes + +Unlike the the [exercises](../README.md#org3b74422) which use the *collection* of test problems in +`exercises-test`, this workshop is a sequential session following the actual +process of writing CodeQL: use a *single* database built from a single, larger +segment of code and inspect the query results as you write the query. + +For this workshop, the larger segment of code is still simplified skeleton code, +not a full source code repository. + +The queries are embedded in \`session.md\` but can also be found in the +\`example\*.ql\` files. They can all be run as test cases in VS Code. + +To reiterate: + +This workshop focuses on analyzing and relating two *static* values — array +access indices and memory allocation sizes — in order to identify +simple cases of out-of-bounds array accesses. We do not handle *dynamic* values +but take advantage of special cases. + +To find these issues, + +1. We can implement an analysis that tracks the upper or lower bounds on an + expression. +2. We then combine this with data-flow analysis to reduce false positives and + identify cases where the index of the array results in an access beyond the + allocated size of the buffer. +3. We further extend these queries with rudimentary arithmetic support involving + expressions common to the allocation and the array access. +4. For cases where constant expressions are not available or are uncertain, we + first try [range analysis](#org426ad70) to expand the query's applicability. +5. For cases where this is insufficient, we introduce global value numbering + [GVN](https://codeql.github.com/docs/codeql-language-guides/hash-consing-and-value-numbering) in [Step 9 – Global Value Numbering](#org8474dff), to detect values known to be equal + at runtime. +6. When *those* cases are insufficient, we handle the case of identical + structure using [BROKEN LINK: \*hashconsing]. + + + + +## Step 1 + +In the first step we are going to + +1. identify a dynamic allocation with `malloc` and +2. an access to that allocated buffer. The access is via an array expression; + we are **not** going to cover pointer dereferencing. + +The goal of this exercise is to then output the array access, array size, +buffer, and buffer offset. + +The focus here is on + + void test_const(void) + +and + + void test_const_var(void) + +in [db.c](file:///Users/hohn/local/codeql-workshop-runtime-values-c/session-db/DB/db.c). + + + + +### Hints + +1. `Expr::getValue()::toInt()` can be used to get the integer value of a + constant expression. + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + + from AllocationExpr buffer, ArrayExpr access, int bufferSize, int accessIdx, Expr allocSizeExpr + where + // malloc (100) + // ^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + allocSizeExpr = buffer.(Call).getArgument(0) and + bufferSize = allocSizeExpr.getValue().toInt() + select buffer, access, accessIdx, access.getArrayOffset(), bufferSize, allocSizeExpr + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:7:17:7:22call to malloctest.c:8:5:8:10access to array0test.c:8:9:8:90100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:9:5:9:11access to array99test.c:9:9:9:1099100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:10:5:10:12access to array100test.c:10:9:10:11100100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:17:5:17:10access to array0test.c:17:9:17:90100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:18:5:18:11access to array99test.c:18:9:18:1099100test.c:7:24:7:26100
+ + + + +## Step 2 + +The previous query fails to connect the `malloc` calls with the array accesses, +and in the results, `mallocs` from one function are paired with accesses in +another. + +To address these, take the query from the previous exercise and + +1. connect the allocation(s) with the +2. array accesses + + + + +### Hints + +1. Use `DataFlow::localExprFlow()` to relate the allocated buffer to the + array base. +2. The the array base is the `buf` part of `buf[0]`. Use the + `Expr.getArrayBase()` predicate. + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + + // Step 2 + // void test_const(void) + // void test_const_var(void) + from AllocationExpr buffer, ArrayExpr access, int bufferSize, int accessIdx, Expr allocSizeExpr + where + // malloc (100) + // ^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + allocSizeExpr = buffer.(Call).getArgument(0) and + bufferSize = allocSizeExpr.getValue().toInt() and + // + // Ensure buffer access is to the correct allocation. + // char *buf = ... buf[0]; + // ^^^ ---> ^^^ + // or + // malloc(100); buf[0] + // ^^^ --------> ^^^ + // + DataFlow::localExprFlow(buffer, access.getArrayBase()) + select buffer, access, accessIdx, access.getArrayOffset(), bufferSize, allocSizeExpr + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:7:17:7:22call to malloctest.c:8:5:8:10access to array0test.c:8:9:8:90100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:9:5:9:11access to array99test.c:9:9:9:1099100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:10:5:10:12access to array100test.c:10:9:10:11100100test.c:7:24:7:26100
+ + + + +## Step 3 + +The previous results need to be extended to the case + + void test_const_var(void) + { + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + ... + } + +Here, the `malloc` argument is a variable with known value. + +We include this result by removing the size-retrieval from the prior query. + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + + // Step 3 + // void test_const_var(void) + from AllocationExpr buffer, ArrayExpr access, int accessIdx, Expr allocSizeExpr + where + // malloc (100) + // ^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + allocSizeExpr = buffer.(Call).getArgument(0) and + // bufferSize = allocSizeExpr.getValue().toInt() and + // + // Ensure buffer access is to the correct allocation. + // char *buf = ... buf[0]; + // ^^^ ---> ^^^ + // or + // malloc(100); buf[0] + // ^^^ --------> ^^^ + // + DataFlow::localExprFlow(buffer, access.getArrayBase()) + select buffer, access, accessIdx, access.getArrayOffset() + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:7:17:7:22call to malloctest.c:8:5:8:10access to array0test.c:8:9:8:90
test.c:7:17:7:22call to malloctest.c:9:5:9:11access to array99test.c:9:9:9:1099
test.c:7:17:7:22call to malloctest.c:10:5:10:12access to array100test.c:10:9:10:11100
test.c:16:17:16:22call to malloctest.c:17:5:17:10access to array0test.c:17:9:17:90
test.c:16:17:16:22call to malloctest.c:18:5:18:11access to array99test.c:18:9:18:1099
+ + + + +## Step 4 + +We are looking for out-of-bounds accesses, so we to need to include the +bounds. But in a more general way than looking only at constant values. + +Note the results for the cases in `test_const_var` which involve a variable +access rather than a constant. The next goal is + +1. to handle the case where the allocation size or array index are variables + (with constant values) rather than integer constants. + +We have an expression `size` that flows into the `malloc()` call. + + + + +### Hint + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + + // Step 4 + from AllocationExpr buffer, ArrayExpr access, int accessIdx, Expr allocSizeExpr, int bufferSize, Expr bse + where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + allocSizeExpr = buffer.(Call).getArgument(0) and + // bufferSize = allocSizeExpr.getValue().toInt() and + // + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + exists(Expr bufferSizeExpr | + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + and bse = bufferSizeExpr + ) and + // Ensure buffer access is to the correct allocation. + // char *buf = ... buf[0]; + // ^^^ ---> ^^^ + // or + // malloc(100); buf[0] + // ^^^ --------> ^^^ + // + DataFlow::localExprFlow(buffer, access.getArrayBase()) + select buffer, access, accessIdx, access.getArrayOffset(), bufferSize, bse + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:7:17:7:22call to malloctest.c:8:5:8:10access to array0test.c:8:9:8:90100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:9:5:9:11access to array99test.c:9:9:9:1099100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:10:5:10:12access to array100test.c:10:9:10:11100100test.c:7:24:7:26100
test.c:16:17:16:22call to malloctest.c:17:5:17:10access to array0test.c:17:9:17:90100test.c:15:26:15:28100
test.c:16:17:16:22call to malloctest.c:18:5:18:11access to array99test.c:18:9:18:1099100test.c:15:26:15:28100
+ + + + +## Step 4a – some clean-up using predicates + +Note that the dataflow automatically captures/includes the + + allocSizeExpr = buffer.(Call).getArgument(0) + +so that's now redundant with `bufferSizeExpr` and can be removed. + + + allocSizeExpr = buffer.(Call).getArgument(0) and + // bufferSize = allocSizeExpr.getValue().toInt() and + // + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + +Also, simplify the `from...where...select`: + +1. Remove unnecessary `exists` clauses. +2. Use `DataFlow::localExprFlow` for the buffer and allocation sizes, with + `getValue().toInt()` as one possibility (one predicate). + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + + from AllocationExpr buffer, ArrayExpr access, int accessIdx, int bufferSize, Expr bufferSizeExpr + where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset().getValue().toInt() and + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access refers to the matching allocation + // ensureSameFunction(buffer, access.getArrayBase()) and + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure buffer access refers to the matching allocation + // ensureSameFunction(bufferSizeExpr, buffer.getSizeExpr()) and + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) + // + select buffer, access, accessIdx, access.getArrayOffset(), bufferSize, bufferSizeExpr + + /** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ + predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + // 2. + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) + } + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:7:17:7:22call to malloctest.c:8:5:8:10access to array0test.c:8:9:8:90100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:9:5:9:11access to array99test.c:9:9:9:1099100test.c:7:24:7:26100
test.c:7:17:7:22call to malloctest.c:10:5:10:12access to array100test.c:10:9:10:11100100test.c:7:24:7:26100
test.c:16:17:16:22call to malloctest.c:17:5:17:10access to array0test.c:17:9:17:90100test.c:15:26:15:28100
test.c:16:17:16:22call to malloctest.c:18:5:18:11access to array99test.c:18:9:18:1099100test.c:15:26:15:28100
+ + + + +## Step 5 – SimpleRangeAnalysis + +Running the query from Step 2 against the database yields a +significant number of missing or incorrect results. The reason is that +although great at identifying compile-time constants and their use, +data-flow analysis is not always the right tool for identifying the +*range* of values an `Expr` might have, particularly when multiple +potential constants might flow to an `Expr`. + +The range analysis already handles conditional branches; we don't +have to use guards on data flow – don't implement your own interpreter +if you can use the library. + +The CodeQL standard library has several mechanisms for addressing this +problem; in the remainder of this workshop we will explore two of them: +`SimpleRangeAnalysis` and, later, `GlobalValueNumbering`. + +Although not in the scope of this workshop, a standard use-case for +range analysis is reliably identifying integer overflow and validating +integer overflow checks. + +Now, add the use of the `SimpleRangeAnalysis` library. Specifically, the +relevant library predicates are `upperBound` and `lowerBound`, to be used with +the buffer access argument. + +Notes: + +- This requires the import + + import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +- We are not limiting the array access to integers any longer. Thus, we just + use + + accessIdx = access.getArrayOffset() +- To see the results in the order used in the C code, use + + select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + + + from AllocationExpr buffer, ArrayExpr access, Expr accessIdx, int bufferSize, Expr bufferSizeExpr + where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) + // + select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax + + /** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ + predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + // 2. + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) + } + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:8:5:8:10access to arraytest.c:8:9:8:900.0
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:9:5:9:11access to arraytest.c:9:9:9:109999.0
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:10:5:10:12access to arraytest.c:10:9:10:11100100.0
test.c:15:26:15:28100test.c:16:17:16:22call to malloctest.c:17:5:17:10access to arraytest.c:17:9:17:900.0
test.c:15:26:15:28100test.c:16:17:16:22call to malloctest.c:18:5:18:11access to arraytest.c:18:9:18:109999.0
+ + + + +## Step 6 + +To finally determine (some) out-of-bounds accesses, we have to convert +allocation units (usually in bytes) to size units. Then we are finally in a +position to compare buffer allocation size to the access index to find +out-of-bounds accesses – at least for expressions with known values. + +Add these to the query: + +1. Convert allocation units to size units. +2. Convert access units to the same size units. + +Hints: + +1. We need the size of the array element. Use + `access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType()` + to see the type and + `access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize()` + to get its size. + +2. Note from the docs: + *The malloc() function allocates size bytes of memory and returns a pointer + to the allocated memory.* + So `size = 1` + +3. These test cases all use type `char`. What would happen for `int` or + `double`? + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + + from AllocationExpr buffer, ArrayExpr access, Expr accessIdx, int bufferSize, Expr bufferSizeExpr + where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // + // buf[...] + // ^^^ int accessIdx + // + accessIdx = access.getArrayOffset() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) + // + select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax, bufferSize, + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() as arrayBaseType, + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() as arrayTypeSize, + 1 as allocBaseSize + + /** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ + predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + // 2. + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) + } + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:8:5:8:10access to arraytest.c:8:9:8:900.0100file://:0:0:0:0char11
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:9:5:9:11access to arraytest.c:9:9:9:109999.0100file://:0:0:0:0char11
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:10:5:10:12access to arraytest.c:10:9:10:11100100.0100file://:0:0:0:0char11
test.c:15:26:15:28100test.c:16:17:16:22call to malloctest.c:17:5:17:10access to arraytest.c:17:9:17:900.0100file://:0:0:0:0char11
test.c:15:26:15:28100test.c:16:17:16:22call to malloctest.c:18:5:18:11access to arraytest.c:18:9:18:109999.0100file://:0:0:0:0char11
+ + + + +## Step 7 + +1. Clean up the query. +2. Compare buffer allocation size to the access index. +3. Add expressions for `allocatedUnits` (from the malloc) and a + `maxAccessedIndex` (from array accesses) + 1. Calculate the `accessOffset` / `maxAccessedIndex` (from array accesses) + 2. Calculate the `allocSize` / `allocatedUnits` (from the malloc) + 3. Compare them + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + + from + AllocationExpr buffer, ArrayExpr access, Expr accessIdx, int bufferSize, Expr bufferSizeExpr, + int arrayTypeSize, int allocBaseSize + where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // buf[...] + // ^^^ ArrayExpr access + // buf[...] + // ^^^ int accessIdx + accessIdx = access.getArrayOffset() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // + arrayTypeSize = access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() and + 1 = allocBaseSize + // + select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax, bufferSize, + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() as arrayBaseType, + allocBaseSize * bufferSize as allocatedUnits, arrayTypeSize * accessMax as maxAccessedIndex + + /** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ + predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // 2. + // unsigned long size = 100; ... ; char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) + } + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:8:5:8:10access to arraytest.c:8:9:8:900.0100file://:0:0:0:0char1000.0
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:9:5:9:11access to arraytest.c:9:9:9:109999.0100file://:0:0:0:0char10099.0
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:10:5:10:12access to arraytest.c:10:9:10:11100100.0100file://:0:0:0:0char100100.0
test.c:15:26:15:28100test.c:16:17:16:22call to malloctest.c:17:5:17:10access to arraytest.c:17:9:17:900.0100file://:0:0:0:0char1000.0
test.c:15:26:15:28100test.c:16:17:16:22call to malloctest.c:18:5:18:11access to arraytest.c:18:9:18:109999.0100file://:0:0:0:0char10099.0
+ + + + +## Step 7a + +1. Account for base sizes – `char` in this case. +2. Put all expressions into the select for review. + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + + from + AllocationExpr buffer, ArrayExpr access, Expr accessIdx, int bufferSize, Expr bufferSizeExpr, + int arrayTypeSize, int allocBaseSize + where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // buf[...] + // ^^^^^^^^ ArrayExpr access + // ^^^ int accessIdx + accessIdx = access.getArrayOffset() and + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // + arrayTypeSize = access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() and + 1 = allocBaseSize + // + select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax, bufferSize, + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() as arrayBaseType, + buffer.getSizeMult() as bufferBaseTypeSize, + arrayBaseType.getSize() as arrayBaseTypeSize, + allocBaseSize * bufferSize as allocatedUnits, arrayTypeSize * accessMax as maxAccessedIndex + + /** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ + predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // Capture BOTH with datflow: + // 1. + // malloc (100) + // ^^^ bufferSize + // 2. + // unsigned long size = 100; ... ; char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) + } + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:8:5:8:10access to arraytest.c:8:9:8:900.0100file://:0:0:0:0char111000.0
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:9:5:9:11access to arraytest.c:9:9:9:109999.0100file://:0:0:0:0char1110099.0
test.c:7:24:7:26100test.c:7:17:7:22call to malloctest.c:10:5:10:12access to arraytest.c:10:9:10:11100100.0100file://:0:0:0:0char11100100.0
test.c:15:26:15:28100test.c:16:17:16:22call to malloctest.c:17:5:17:10access to arraytest.c:17:9:17:900.0100file://:0:0:0:0char111000.0
test.c:15:26:15:28100test.c:16:17:16:22call to malloctest.c:18:5:18:11access to arraytest.c:18:9:18:109999.0100file://:0:0:0:0char1110099.0
+ + + + +## Step 7b + +1. Introduce more general predicates. +2. Compare buffer allocation size to the access index. +3. Report only the questionable entries. + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + + from + AllocationExpr buffer, ArrayExpr access, int bufferSize, Expr bufferSizeExpr, + int maxAccessedIndex, int allocatedUnits + where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + getAllocConstantExpr(bufferSizeExpr, bufferSize) and + // Ensure buffer access is to the correct allocation. + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure use refers to the correct size defintion, even for non-constant + // expressions. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // computeIndices(access, buffer, bufferSize, allocatedUnits, maxAccessedIndex) + computeAllocationSize(buffer, bufferSize, allocatedUnits) and + computeMaxAccess(access, maxAccessedIndex) + // only consider out-of-bounds + and + maxAccessedIndex >= allocatedUnits + select access, + "Array access at or beyond size; have " + allocatedUnits + " units, access at " + maxAccessedIndex + + // select bufferSizeExpr, buffer, access, allocatedUnits, maxAccessedIndex + + /** + * Compute the maximum accessed index. + */ + predicate computeMaxAccess(ArrayExpr access, int maxAccessedIndex) { + exists( + int arrayTypeSize, int accessMax, Type arrayBaseType, int arrayBaseTypeSize, Expr accessIdx + | + // buf[...] + // ^^^^^^^^ ArrayExpr access + // ^^^ + accessIdx = access.getArrayOffset() and + upperBound(accessIdx) = accessMax and + arrayBaseType.getSize() = arrayBaseTypeSize and + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() = arrayBaseType and + arrayTypeSize = access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() and + arrayTypeSize * accessMax = maxAccessedIndex + ) + } + + /** + * Compute the allocation size. + */ + bindingset[bufferSize] + predicate computeAllocationSize(AllocationExpr buffer, int bufferSize, int allocatedUnits) { + exists(int bufferBaseTypeSize, Type arrayBaseType, int arrayBaseTypeSize | + // buf[...] + // ^^^^^^^^ ArrayExpr access + // ^^^ + buffer.getSizeMult() = bufferBaseTypeSize and + arrayBaseType.getSize() = arrayBaseTypeSize and + bufferSize * bufferBaseTypeSize = allocatedUnits + ) + } + + /** + * Compute the allocation size and the maximum accessed index for the allocation and access. + */ + bindingset[bufferSize] + predicate computeIndices( + ArrayExpr access, AllocationExpr buffer, int bufferSize, int allocatedUnits, int maxAccessedIndex + ) { + exists( + int arrayTypeSize, int accessMax, int bufferBaseTypeSize, Type arrayBaseType, + int arrayBaseTypeSize, Expr accessIdx + | + // buf[...] + // ^^^^^^^^ ArrayExpr access + // ^^^ + accessIdx = access.getArrayOffset() and + upperBound(accessIdx) = accessMax and + buffer.getSizeMult() = bufferBaseTypeSize and + arrayBaseType.getSize() = arrayBaseTypeSize and + bufferSize * bufferBaseTypeSize = allocatedUnits and + access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType() = arrayBaseType and + arrayTypeSize = access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize() and + arrayTypeSize * accessMax = maxAccessedIndex + ) + } + + /** + * Gets an expression that flows to the allocation (which includes those already in the allocation) + * and has a constant value. + */ + predicate getAllocConstantExpr(Expr bufferSizeExpr, int bufferSize) { + exists(AllocationExpr buffer | + // Capture BOTH with datflow: + // 1. malloc (100) + // ^^^ bufferSize + // 2. unsigned long size = 100; ... ; char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + bufferSizeExpr.getValue().toInt() = bufferSize + ) + } + + + + +### First 5 results + +WARNING: Unused predicate computeIndices (/Users/hohn/local/codeql-workshop-runtime-values-c/session/example7b.ql:66,11-25) + + + + +++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:10:5:10:12access to arrayArray access at or beyond size; have 100 units, access at 100
test.c:20:5:20:12access to arrayArray access at or beyond size; have 100 units, access at 100
test.c:21:5:21:13access to arrayArray access at or beyond size; have 100 units, access at 100
test.c:37:5:37:17access to arrayArray access at or beyond size; have 100 units, access at 299
+ + + + +## Step 8 + +Up to now, we have dealt with constant values + + char *buf = malloc(100); + buf[0]; // COMPLIANT + +or + + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + +and statically determinable or boundable values + + char *buf = malloc(size); + if (size < 199) + { + buf[size]; // COMPLIANT + // ... + } + +There is another statically determinable case. Examples are + +1. A simple expression + + char *buf = malloc(alloc_size); + // ... + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT +2. A complex expression + + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + +These both have the form `malloc(e)`, `buf[e+c]`, where `e` is an `Expr` and +`c` is a constant, possibly 0. Our existing queries only report known or +boundable results, but here `e` is neither. + +Write a new query, re-using or modifying the existing one to handle the simple +expression (case 1). + +Note: + +- We are looking at the allocation expression again, not its possible value. +- This only handles very specific cases. Constructing counterexamples is easy. +- We will address this in the next section. + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + + from + AllocationExpr buffer, ArrayExpr access, Expr bufferSizeExpr, + // --- + // int maxAccessedIndex, int allocatedUnits, + // int bufferSize + int accessOffset, Expr accessBase, Expr bufferBase, int bufferOffset, Variable bufInit, + Variable accessInit + where + // malloc (...) + // ^^^^^^^^^^^^ AllocationExpr buffer + // --- + // getAllocConstExpr(...) + // +++ + bufferSizeExpr = buffer.getSizeExpr() and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // + // +++ + // base+offset + extractBaseAndOffset(bufferSizeExpr, bufferBase, bufferOffset) and + extractBaseAndOffset(access.getArrayOffset(), accessBase, accessOffset) and + // +++ + // Same initializer variable + bufferBase.(VariableAccess).getTarget() = bufInit and + accessBase.(VariableAccess).getTarget() = accessInit and + bufInit = accessInit + // +++ + // Identify questionable differences + select buffer, bufferBase, bufferOffset, access, accessBase, accessOffset, bufInit, accessInit + + /** + * Extract base and offset from y = base+offset and y = base-offset. For others, get y and 0. + * + * For cases like + * buf[alloc_size + 1]; + * + * The more general + * buf[sz * x * y - 1]; + * requires other tools. + */ + bindingset[expr] + predicate extractBaseAndOffset(Expr expr, Expr base, int offset) { + offset = expr.(AddExpr).getRightOperand().getValue().toInt() and + base = expr.(AddExpr).getLeftOperand() + or + offset = -expr.(SubExpr).getRightOperand().getValue().toInt() and + base = expr.(SubExpr).getLeftOperand() + or + not expr instanceof AddExpr and + not expr instanceof SubExpr and + base = expr and + offset = 0 + } + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:16:17:16:22call to malloctest.c:16:24:16:27size0test.c:19:5:19:17access to arraytest.c:19:9:19:12size-1test.c:15:19:15:22sizetest.c:15:19:15:22size
test.c:16:17:16:22call to malloctest.c:16:24:16:27size0test.c:21:5:21:13access to arraytest.c:21:9:21:12size0test.c:15:19:15:22sizetest.c:15:19:15:22size
test.c:28:17:28:22call to malloctest.c:28:24:28:27size0test.c:37:5:37:17access to arraytest.c:37:9:37:12size-1test.c:26:19:26:22sizetest.c:26:19:26:22size
test.c:28:17:28:22call to malloctest.c:28:24:28:27size0test.c:39:5:39:13access to arraytest.c:39:9:39:12size0test.c:26:19:26:22sizetest.c:26:19:26:22size
test.c:28:17:28:22call to malloctest.c:28:24:28:27size0test.c:43:9:43:17access to arraytest.c:43:13:43:16size0test.c:26:19:26:22sizetest.c:26:19:26:22size
+ + + + +## Interim notes + +A common issue with the `SimpleRangeAnalysis` library is handling of +cases where the bounds are undeterminable at compile-time on one or more +paths. For example, even though certain paths have clearly defined +bounds, the range analysis library will define the `upperBound` and +`lowerBound` of `val` as `INT_MIN` and `INT_MAX` respectively: + + int val = rand() ? rand() : 30; + +A similar case is present in the `test_const_branch` and `test_const_branch2` +test-cases. In these cases, it is necessary to augment range analysis with +data-flow and restrict the bounds to the upper or lower bound of computable +constants that flow to a given expression. Another approach is global value +numbering, used next. + + + + +## Step 8a + +Find problematic accesses by reverting to some *simple* `var+const` checks using +`accessOffset` and `bufferOffset`. + +Note: + +- These will flag some false positives. +- The product expression `sz * x * y` is not easily checked for equality. + +These are addressed in the next step. + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + + from + AllocationExpr buffer, ArrayExpr access, Expr bufferSizeExpr, + // --- + // int maxAccessedIndex, int allocatedUnits, + // int bufferSize + int accessOffset, Expr accessBase, Expr bufferBase, int bufferOffset, Variable bufInit, + Variable accessInit + where + // malloc (...) + // ^^^^^^^^^^^^ AllocationExpr buffer + // --- + // getAllocConstExpr(...) + // +++ + bufferSizeExpr = buffer.getSizeExpr() and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Find allocation size expression flowing to buffer. + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + // + // +++ + // base+offset + extractBaseAndOffset(bufferSizeExpr, bufferBase, bufferOffset) and + extractBaseAndOffset(access.getArrayOffset(), accessBase, accessOffset) and + // +++ + // Same initializer variable + bufferBase.(VariableAccess).getTarget() = bufInit and + accessBase.(VariableAccess).getTarget() = accessInit and + bufInit = accessInit and + // +++ + // Identify questionable differences + accessOffset >= bufferOffset + select buffer, bufferBase, access, accessBase, bufInit, bufferOffset, accessInit, accessOffset + + /** + * Extract base and offset from y = base+offset and y = base-offset. For others, get y and 0. + * + * For cases like + * buf[alloc_size + 1]; + * ^^^^^^^^^^^^^^ expr + * ^^^^^^^^^^ base + * ^^^ offset + * + * The more general + * buf[sz * x * y - 1]; + * requires other tools. + */ + bindingset[expr] + predicate extractBaseAndOffset(Expr expr, Expr base, int offset) { + offset = expr.(AddExpr).getRightOperand().getValue().toInt() and + base = expr.(AddExpr).getLeftOperand() + or + offset = -expr.(SubExpr).getRightOperand().getValue().toInt() and + base = expr.(SubExpr).getLeftOperand() + or + not expr instanceof AddExpr and + not expr instanceof SubExpr and + base = expr and + offset = 0 + } + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:16:17:16:22call to malloctest.c:16:24:16:27sizetest.c:21:5:21:13access to arraytest.c:21:9:21:12sizetest.c:15:19:15:22size0test.c:15:19:15:22size0
test.c:28:17:28:22call to malloctest.c:28:24:28:27sizetest.c:39:5:39:13access to arraytest.c:39:9:39:12sizetest.c:26:19:26:22size0test.c:26:19:26:22size0
test.c:28:17:28:22call to malloctest.c:28:24:28:27sizetest.c:43:9:43:17access to arraytest.c:43:13:43:16sizetest.c:26:19:26:22size0test.c:26:19:26:22size0
test.c:28:17:28:22call to malloctest.c:28:24:28:27sizetest.c:44:9:44:21access to arraytest.c:44:13:44:16sizetest.c:26:19:26:22size0test.c:26:19:26:22size1
test.c:28:17:28:22call to malloctest.c:28:24:28:27sizetest.c:45:9:45:21access to arraytest.c:45:13:45:16sizetest.c:26:19:26:22size0test.c:26:19:26:22size2
+ + + + +## Step 9 – Global Value Numbering + +Range analyis won't bound `sz * x * y`, and simple equality checks don't work +at the structure level, so switch to global value numbering. + +This is the case in the last test case, + + void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) + { + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT + } + +Global value numbering only knows that runtime values are equal; they +are not comparable (`<, >, <=` etc.), and the *actual* value is not +known. + +Global value numbering finds expressions with the same known value, +independent of structure. + +So, we look for and use *relative* values between allocation and use. + +The relevant CodeQL constructs are + + import semmle.code.cpp.valuenumbering.GlobalValueNumbering + ... + globalValueNumber(e) = globalValueNumber(sizeExpr) and + e != sizeExpr + ... + +We can use global value numbering to identify common values as first step, but +for expressions like + + buf[sz * x * y - 1]; // COMPLIANT + +we have to "evaluate" the expressions – or at least bound them. + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + import semmle.code.cpp.valuenumbering.GlobalValueNumbering + + from + AllocationExpr buffer, ArrayExpr access, + // --- + // Expr bufferSizeExpr + // int accessOffset, Expr accessBase, Expr bufferBase, int bufferOffset, Variable bufInit, + // +++ + Expr allocSizeExpr, Expr accessIdx, GVN gvnAccessIdx, GVN gvnAllocSizeExpr, int accessOffset + where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // buf[...] + // ^^^ ArrayExpr access + // buf[...] + // ^^^ accessIdx + accessIdx = access.getArrayOffset() and + // Find allocation size expression flowing to the allocation. + DataFlow::localExprFlow(allocSizeExpr, buffer.getSizeExpr()) and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Use GVN + globalValueNumber(accessIdx) = gvnAccessIdx and + globalValueNumber(allocSizeExpr) = gvnAllocSizeExpr and + ( + // buf[size] or buf[100] + gvnAccessIdx = gvnAllocSizeExpr and + accessOffset = 0 + or + // buf[sz * x * y + 1]; + exists(AddExpr add | + accessIdx = add and + accessOffset >= 0 and + accessOffset = add.getRightOperand().(Literal).getValue().toInt() and + globalValueNumber(add.getLeftOperand()) = gvnAllocSizeExpr + ) + ) + select access, gvnAllocSizeExpr, allocSizeExpr, buffer.getSizeExpr() as allocArg, gvnAccessIdx, + accessIdx, accessOffset + + + + +### First 5 results + +Results note: + +- The allocation size of 200 is never used in an access, so the GVN match + eliminates it from the result list. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:21:5:21:13access to arraytest.c:15:26:15:28GVNtest.c:15:26:15:28100test.c:16:24:16:27sizetest.c:15:26:15:28GVNtest.c:21:9:21:12size0
test.c:21:5:21:13access to arraytest.c:15:26:15:28GVNtest.c:16:24:16:27sizetest.c:16:24:16:27sizetest.c:15:26:15:28GVNtest.c:21:9:21:12size0
test.c:38:5:38:12access to arraytest.c:26:39:26:41GVNtest.c:26:39:26:41100test.c:28:24:28:27sizetest.c:26:39:26:41GVNtest.c:38:9:38:111000
test.c:69:5:69:19access to arraytest.c:63:24:63:33GVNtest.c:63:24:63:33allocsizetest.c:63:24:63:33allocsizetest.c:63:24:63:33GVNtest.c:69:9:69:18allocsize0
test.c:73:9:73:23access to arraytest.c:63:24:63:33GVNtest.c:63:24:63:33allocsizetest.c:63:24:63:33allocsizetest.c:63:24:63:33GVNtest.c:73:13:73:22allocsize0
+ + + + +## Step 9a – hashconsing + +For the cases with variable `malloc` sizes, like `test_const_branch`, GVN +identifies same-value constant accesses, but we need a special case for +same-structure expression accesses. Enter `hashCons`. + +From the reference: + + +> The hash consing library (defined in semmle.code.cpp.valuenumbering.HashCons) +> provides a mechanism for identifying expressions that have the same syntactic +> structure. + +Additions to the imports, and use: + + import semmle.code.cpp.valuenumbering.HashCons + ... + hashCons(expr) + +This step illustrates some subtle meanings of equality. In particular, there +is plain `=`, GVN, and `hashCons`: + + // 0 results: + // (accessBase = allocSizeExpr or accessBase = allocArg) + + // Only 6 results: + + // ( + // gvnAccessIdx = gvnAllocSizeExpr or + // gvnAccessIdx = globalValueNumber(allocArg) + // ) + + // 9 results: + ( + hashCons(accessBase) = hashCons(allocSizeExpr) or + hashCons(accessBase) = hashCons(allocArg) + ) + + + + +### Solution + + import cpp + import semmle.code.cpp.dataflow.DataFlow + import semmle.code.cpp.valuenumbering.GlobalValueNumbering + import semmle.code.cpp.valuenumbering.HashCons + + from + AllocationExpr buffer, ArrayExpr access, Expr allocSizeExpr, Expr accessIdx, GVN gvnAccessIdx, + GVN gvnAllocSizeExpr, int accessOffset, + // +++ + Expr allocArg, Expr accessBase + where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // buf[...] + // ^^^ ArrayExpr access + // buf[...] + // ^^^ accessIdx + accessIdx = access.getArrayOffset() and + // Find allocation size expression flowing to the allocation. + DataFlow::localExprFlow(allocSizeExpr, buffer.getSizeExpr()) and + // Ensure buffer access refers to the matching allocation + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // Use GVN + globalValueNumber(accessIdx) = gvnAccessIdx and + globalValueNumber(allocSizeExpr) = gvnAllocSizeExpr and + ( + // buf[size] or buf[100] + gvnAccessIdx = gvnAllocSizeExpr and + accessOffset = 0 and + // +++ + accessBase = accessIdx + or + // buf[sz * x * y + 1]; + exists(AddExpr add | + accessIdx = add and + accessOffset >= 0 and + accessOffset = add.getRightOperand().(Literal).getValue().toInt() and + globalValueNumber(add.getLeftOperand()) = gvnAllocSizeExpr and + // +++ + accessBase = add.getLeftOperand() + ) + ) and + buffer.getSizeExpr() = allocArg and + ( + accessOffset >= 0 and + // +++ + // Illustrating the subtle meanings of equality: + // 0 results: + // (accessBase = allocSizeExpr or accessBase = allocArg) + // Only 6 results: + // ( + // gvnAccessIdx = gvnAllocSizeExpr or + // gvnAccessIdx = globalValueNumber(allocArg) + // ) + // 9 results: + ( + hashCons(accessBase) = hashCons(allocSizeExpr) or + hashCons(accessBase) = hashCons(allocArg) + ) + ) + // gvnAccessIdx = globalValueNumber(allocArg)) + // +++ overview select: + select access, gvnAllocSizeExpr, allocSizeExpr, allocArg, gvnAccessIdx, accessIdx, accessBase, + accessOffset + + + + +### First 5 results + + + + +++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
test.c:21:5:21:13access to arraytest.c:15:26:15:28GVNtest.c:15:26:15:28100test.c:16:24:16:27sizetest.c:15:26:15:28GVNtest.c:21:9:21:12sizetest.c:21:9:21:12size0
test.c:21:5:21:13access to arraytest.c:15:26:15:28GVNtest.c:16:24:16:27sizetest.c:16:24:16:27sizetest.c:15:26:15:28GVNtest.c:21:9:21:12sizetest.c:21:9:21:12size0
test.c:38:5:38:12access to arraytest.c:26:39:26:41GVNtest.c:26:39:26:41100test.c:28:24:28:27sizetest.c:26:39:26:41GVNtest.c:38:9:38:11100test.c:38:9:38:111000
test.c:69:5:69:19access to arraytest.c:63:24:63:33GVNtest.c:63:24:63:33allocsizetest.c:63:24:63:33allocsizetest.c:63:24:63:33GVNtest.c:69:9:69:18allocsizetest.c:69:9:69:18allocsize0
test.c:73:9:73:23access to arraytest.c:63:24:63:33GVNtest.c:63:24:63:33allocsizetest.c:63:24:63:33allocsizetest.c:63:24:63:33GVNtest.c:73:13:73:22allocsizetest.c:73:13:73:22allocsize0
+ diff --git a/session/session.org b/session/session.org new file mode 100644 index 0000000..4a94f73 --- /dev/null +++ b/session/session.org @@ -0,0 +1,594 @@ +* CodeQL Workshop --- Using Data-Flow and Range Analysis to Find Out-Of-Bounds Accesses +:PROPERTIES: +:CUSTOM_ID: codeql-workshop--using-data-flow-and-range-analysis-to-find-out-of-bounds-accesses +:END: +* Acknowledgments + :PROPERTIES: + :CUSTOM_ID: acknowledgments + :END: + +This session-based workshop is based on the exercise/unit-test-based material at +https://github.com/kraiouchkine/codeql-workshop-runtime-values-c, which in turn is +based on a significantly simplified and modified version of the +[[https://github.com/github/codeql-coding-standards/blob/main/c/common/src/codingstandards/c/OutOfBounds.qll][OutOfBounds.qll library]] from the +[[https://github.com/github/codeql-coding-standards][CodeQL Coding Standards +repository]]. + +* Setup Instructions + :PROPERTIES: + :CUSTOM_ID: setup-instructions + :END: +- Install [[https://code.visualstudio.com/][Visual Studio Code]]. + +- Install the + [[https://codeql.github.com/docs/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code/][CodeQL extension for Visual Studio Code]]. + +- Install the latest version of the + [[https://github.com/github/codeql-cli-binaries/releases][CodeQL CLI]]. + +- Clone this repository: + #+begin_src sh + git clone https://github.com/hohn/codeql-workshop-runtime-values-c + #+end_src + +- Install the CodeQL pack dependencies using the command + =CodeQL: Install Pack Dependencies= and select =exercises=, + =solutions=, =exercises-tests=, =session=, =session-db= and + =solutions-tests= from the list of packs. + +- If you have CodeQL on your PATH, build the database using + =build-database.sh= and load the database with the VS Code CodeQL + extension. It is at =session-db/cpp-runtime-values-db=. + + - Alternatively, you can download + [[https://drive.google.com/file/d/1N8TYJ6f4E33e6wuyorWHZHVCHBZy8Bhb/view?usp=sharing][this + pre-built database]]. + +- If you do *not* have CodeQL on your PATH, build the database using the + unit test sytem. Choose the =TESTING= tab in VS Code, run the + =session-db/DB/db.qlref= test. The test will fail, but it leaves a + usable CodeQL database in =session-db/DB/DB.testproj=. + +- ❗Important❗: Run =initialize-qltests.sh= to initialize the tests. + Otherwise, you will not be able to run the QLTests in + =exercises-tests=. + +* Introduction + :PROPERTIES: + :CUSTOM_ID: introduction + :END: +This workshop focuses on analyzing and relating two values --- array +access indices and memory allocation sizes --- in order to identify +simple cases of out-of-bounds array accesses. + +The following snippets demonstrate how an out-of-bounds array access can +occur: + +#+begin_src cpp +char* buffer = malloc(10); +buffer[9] = 'a'; // ok +buffer[10] = 'b'; // out-of-bounds +#+end_src + +A more complex example: + +#+begin_src cpp +char* buffer; +if(rand() == 1) { + buffer = malloc(10); +} +else { + buffer = malloc(11); +} +size_t index = 0; +if(rand() == 1) { + index = 10; +} +buffer[index]; // potentially out-of-bounds depending on control-flow +#+end_src + +Another common case /not/ covered in this introductory workshop involves +loops, as follows: + +#+begin_src cpp +int elements[5]; +for (int i = 0; i <= 5; ++i) { + elements[i] = 0; +} +#+end_src + +To find these issues, we can implement an analysis that tracks the upper +or lower bounds on an expression and, combined with data-flow analysis +to reduce false-positives, identifies cases where the index of the array +results in an access beyond the allocated size of the buffer. + +* A Note on the Scope of This Workshop + :PROPERTIES: + :CUSTOM_ID: a-note-on-the-scope-of-this-workshop + :END: + This workshop is not intended to be a complete analysis that is useful + for real-world cases of out-of-bounds analyses for reasons including but + not limited to: + + - Missing support for loops and recursion + - No interprocedural analysis + - Missing size calculation of arrays where the element size is not 1 + - No support for pointer arithmetic or in general, operations other than + addition and subtraction + - Overly specific modelling of a buffer access as an array expression + + The goal of this workshop is rather to demonstrate the building blocks + of analyzing run-time values and how to apply those building blocks to + modelling a common class of vulnerability. A more comprehensive and + production-appropriate example is the + [[https://github.com/github/codeql-coding-standards/blob/main/c/common/src/codingstandards/c/OutOfBounds.qll][OutOfBounds.qll + library]] from the + [[https://github.com/github/codeql-coding-standards][CodeQL Coding + Standards repository]]. + +* A short note on the structure of directories and their use + + =exercises-tests= are identical to =solution-tests=, the =exercises= directories + are a convenience for developing the queries on your own so you can use the unit + tests as reference. This is for full consistency with the workshop material -- + the session -- but you may veer off and experiment on your own. + + In that case, a simpler option is to follow the session writeup using a single + =.ql= file; the writeup has full queries and (at most) the first 5 results for + reference. + +* Session/Workshop notes + :PROPERTIES: + :CUSTOM_ID: sessionworkshop-notes + :END: + + Unlike the the [[../README.md#org3b74422][exercises]] which use the /collection/ of test problems in + =exercises-test=, this workshop is a sequential session following the actual + process of writing CodeQL: use a /single/ database built from a single, larger + segment of code and inspect the query results as you write the query. + + For this workshop, the larger segment of code is still simplified skeleton code, + not a full source code repository. + + The queries are embedded in `session.md` but can also be found in the + `example*.ql` files. They can all be run as test cases in VS Code. + + To reiterate: + + This workshop focuses on analyzing and relating two /static/ values --- array + access indices and memory allocation sizes --- in order to identify + simple cases of out-of-bounds array accesses. We do not handle /dynamic/ values + but take advantage of special cases. + + To find these issues, + 1. We can implement an analysis that tracks the upper or lower bounds on an + expression. + 2. We then combine this with data-flow analysis to reduce false positives and + identify cases where the index of the array results in an access beyond the + allocated size of the buffer. + 3. We further extend these queries with rudimentary arithmetic support involving + expressions common to the allocation and the array access. + 4. For cases where constant expressions are not available or are uncertain, we + first try [[*Step 5 -- SimpleRangeAnalysis][range analysis]] to expand the query's applicability. + 5. For cases where this is insufficient, we introduce global value numbering + [[https://codeql.github.com/docs/codeql-language-guides/hash-consing-and-value-numbering][GVN]] in [[*Step 9 -- Global Value Numbering][Step 9 -- Global Value Numbering]], to detect values known to be equal + at runtime. + 6. When /those/ cases are insufficient, we handle the case of identical + structure using [[*hashconsing][hashconsing]]. + +** Step 1 + :PROPERTIES: + :CUSTOM_ID: exercise-1 + :END: + In the first step we are going to + 1. identify a dynamic allocation with =malloc= and + 2. an access to that allocated buffer. The access is via an array expression; + we are *not* going to cover pointer dereferencing. + + The goal of this exercise is to then output the array access, array size, + buffer, and buffer offset. + + The focus here is on + : void test_const(void) + and + : void test_const_var(void) + in [[file:~/local/codeql-workshop-runtime-values-c/session-db/DB/db.c][db.c]]. + +*** Hints + :PROPERTIES: + :CUSTOM_ID: hints + :END: +1. =Expr::getValue()::toInt()= can be used to get the integer value of a + constant expression. + +*** Solution + #+INCLUDE: "example1.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example1/example1.expected" :lines "-6"’ + +** Step 2 +The previous query fails to connect the =malloc= calls with the array accesses, +and in the results, =mallocs= from one function are paired with accesses in +another. + +To address these, take the query from the previous exercise and +1. connect the allocation(s) with the +2. array accesses + +*** Hints + :PROPERTIES: + :CUSTOM_ID: hints + :END: +1. Use =DataFlow::localExprFlow()= to relate the allocated buffer to the + array base. +2. The the array base is the =buf= part of =buf[0]=. Use the + =Expr.getArrayBase()= predicate. + +*** Solution + #+INCLUDE: "example2.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example2/example2.expected" :lines "-6"’ + +** Step 3 + :PROPERTIES: + :CUSTOM_ID: exercise-2 + :END: + + The previous results need to be extended to the case + #+BEGIN_SRC c++ + void test_const_var(void) + { + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + ... + } + #+END_SRC + + Here, the =malloc= argument is a variable with known value. + + We include this result by removing the size-retrieval from the prior query. + +*** Solution + #+INCLUDE: "example3.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example3/example3.expected" :lines "-6"’ + +** Step 4 + We are looking for out-of-bounds accesses, so we to need to include the + bounds. But in a more general way than looking only at constant values. + + Note the results for the cases in =test_const_var= which involve a variable + access rather than a constant. The next goal is + 1. to handle the case where the allocation size or array index are variables + (with constant values) rather than integer constants. + + We have an expression =size= that flows into the =malloc()= call. + +*** Hint + +*** Solution + #+INCLUDE: "example4.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example4/example4.expected" :lines "-6"’ + +** Step 4a -- some clean-up using predicates + + Note that the dataflow automatically captures/includes the + : allocSizeExpr = buffer.(Call).getArgument(0) + so that's now redundant with =bufferSizeExpr= and can be removed. + #+BEGIN_SRC java + + allocSizeExpr = buffer.(Call).getArgument(0) and + // bufferSize = allocSizeExpr.getValue().toInt() and + // + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + DataFlow::localExprFlow(bufferSizeExpr, buffer.getSizeExpr()) and + + #+END_SRC + + Also, simplify the =from...where...select=: + 1. Remove unnecessary =exists= clauses. + 2. Use =DataFlow::localExprFlow= for the buffer and allocation sizes, with + =getValue().toInt()= as one possibility (one predicate). + +*** Solution + #+INCLUDE: "example4a.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example4a/example4a.expected" :lines "-6"’ + +** Step 5 -- SimpleRangeAnalysis + Running the query from Step 2 against the database yields a + significant number of missing or incorrect results. The reason is that + although great at identifying compile-time constants and their use, + data-flow analysis is not always the right tool for identifying the + /range/ of values an =Expr= might have, particularly when multiple + potential constants might flow to an =Expr=. + + The range analysis already handles conditional branches; we don't + have to use guards on data flow -- don't implement your own interpreter + if you can use the library. + + The CodeQL standard library has several mechanisms for addressing this + problem; in the remainder of this workshop we will explore two of them: + =SimpleRangeAnalysis= and, later, =GlobalValueNumbering=. + + Although not in the scope of this workshop, a standard use-case for + range analysis is reliably identifying integer overflow and validating + integer overflow checks. + + Now, add the use of the =SimpleRangeAnalysis= library. Specifically, the + relevant library predicates are =upperBound= and =lowerBound=, to be used with + the buffer access argument. + + Notes: + - This requires the import + : import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + - We are not limiting the array access to integers any longer. Thus, we just + use + : accessIdx = access.getArrayOffset() + - To see the results in the order used in the C code, use + : select bufferSizeExpr, buffer, access, accessIdx, upperBound(accessIdx) as accessMax + +*** Solution + #+INCLUDE: "example5.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example5/example5.expected" :lines "-6"’ + +** Step 6 + To finally determine (some) out-of-bounds accesses, we have to convert + allocation units (usually in bytes) to size units. Then we are finally in a + position to compare buffer allocation size to the access index to find + out-of-bounds accesses -- at least for expressions with known values. + + Add these to the query: + 1. Convert allocation units to size units. + 2. Convert access units to the same size units. + + Hints: + 1. We need the size of the array element. Use + =access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType()= + to see the type and + =access.getArrayBase().getUnspecifiedType().(PointerType).getBaseType().getSize()= + to get its size. + + 2. Note from the docs: + /The malloc() function allocates size bytes of memory and returns a pointer + to the allocated memory./ + So =size = 1= + + 3. These test cases all use type =char=. What would happen for =int= or + =double=? + +*** Solution + #+INCLUDE: "example6.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example6/example6.expected" :lines "-6"’ + +** Step 7 + 1. Clean up the query. + 2. Compare buffer allocation size to the access index. + 3. Add expressions for =allocatedUnits= (from the malloc) and a + =maxAccessedIndex= (from array accesses) + 1. Calculate the =accessOffset= / =maxAccessedIndex= (from array accesses) + 2. Calculate the =allocSize= / =allocatedUnits= (from the malloc) + 3. Compare them + +*** Solution + #+INCLUDE: "example7.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example7/example7.expected" :lines "-6"’ + +** Step 7a + 1. Account for base sizes -- =char= in this case. + 2. Put all expressions into the select for review. + +*** Solution + #+INCLUDE: "example7a.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example7a/example7a.expected" :lines "-6"’ + +** Step 7b + 1. Introduce more general predicates. + 2. Compare buffer allocation size to the access index. + 3. Report only the questionable entries. + +*** Solution + #+INCLUDE: "example7b.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example7b/example7b.expected" :lines "-6"’ + +** Step 8 + Up to now, we have dealt with constant values + #+BEGIN_SRC c++ + char *buf = malloc(100); + buf[0]; // COMPLIANT + #+END_SRC + or + #+BEGIN_SRC c++ + unsigned long size = 100; + char *buf = malloc(size); + buf[0]; // COMPLIANT + #+END_SRC + and statically determinable or boundable values + #+BEGIN_SRC c++ + char *buf = malloc(size); + if (size < 199) + { + buf[size]; // COMPLIANT + // ... + } + #+END_SRC + + There is another statically determinable case. Examples are + 1. A simple expression + #+BEGIN_SRC c++ + char *buf = malloc(alloc_size); + // ... + buf[alloc_size - 1]; // COMPLIANT + buf[alloc_size]; // NON_COMPLIANT + #+END_SRC + 2. A complex expression + #+BEGIN_SRC c++ + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + #+END_SRC + These both have the form =malloc(e)=, =buf[e+c]=, where =e= is an =Expr= and + =c= is a constant, possibly 0. Our existing queries only report known or + boundable results, but here =e= is neither. + + Write a new query, re-using or modifying the existing one to handle the simple + expression (case 1). + + Note: + - We are looking at the allocation expression again, not its possible value. + - This only handles very specific cases. Constructing counterexamples is easy. + - We will address this in the next section. + +*** Solution + #+INCLUDE: "example8.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example8/example8.expected" :lines "-6"’ + +** Interim notes + A common issue with the =SimpleRangeAnalysis= library is handling of + cases where the bounds are undeterminable at compile-time on one or more + paths. For example, even though certain paths have clearly defined + bounds, the range analysis library will define the =upperBound= and + =lowerBound= of =val= as =INT_MIN= and =INT_MAX= respectively: + + #+begin_src cpp + int val = rand() ? rand() : 30; + #+end_src + + A similar case is present in the =test_const_branch= and =test_const_branch2= + test-cases. In these cases, it is necessary to augment range analysis with + data-flow and restrict the bounds to the upper or lower bound of computable + constants that flow to a given expression. Another approach is global value + numbering, used next. + +** Step 8a + Find problematic accesses by reverting to some /simple/ =var+const= checks using + =accessOffset= and =bufferOffset=. + + Note: + - These will flag some false positives. + - The product expression =sz * x * y= is not easily checked for equality. + These are addressed in the next step. + +*** Solution + #+INCLUDE: "example8a.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/example8a/example8a.expected" :lines "-6"’ + +** Step 9 -- Global Value Numbering + Range analyis won't bound =sz * x * y=, and simple equality checks don't work + at the structure level, so switch to global value numbering. + + This is the case in the last test case, + #+begin_example + void test_gvn_var(unsigned long x, unsigned long y, unsigned long sz) + { + char *buf = malloc(sz * x * y); + buf[sz * x * y - 1]; // COMPLIANT + buf[sz * x * y]; // NON_COMPLIANT + buf[sz * x * y + 1]; // NON_COMPLIANT + } + #+end_example + + Global value numbering only knows that runtime values are equal; they + are not comparable (=<, >, <== etc.), and the /actual/ value is not + known. + + Global value numbering finds expressions with the same known value, + independent of structure. + + So, we look for and use /relative/ values between allocation and use. + + The relevant CodeQL constructs are + #+BEGIN_SRC java + import semmle.code.cpp.valuenumbering.GlobalValueNumbering + ... + globalValueNumber(e) = globalValueNumber(sizeExpr) and + e != sizeExpr + ... + #+END_SRC + + We can use global value numbering to identify common values as first step, but + for expressions like + #+begin_example + buf[sz * x * y - 1]; // COMPLIANT + #+end_example + we have to "evaluate" the expressions -- or at least bound them. + +*** Solution + #+INCLUDE: "example9.ql" src java + +*** First 5 results + Results note: + - The allocation size of 200 is never used in an access, so the GVN match + eliminates it from the result list. + + #+INCLUDE: "../session-tests/Example9/example9.expected" :lines "-6"’ + +** Step 9a -- hashconsing + For the cases with variable =malloc= sizes, like =test_const_branch=, GVN + identifies same-value constant accesses, but we need a special case for + same-structure expression accesses. Enter =hashCons=. + + From the reference: + [[https://codeql.github.com/docs/codeql-language-guides/hash-consing-and-value-numbering/]] + + #+BEGIN_QUOTE + The hash consing library (defined in semmle.code.cpp.valuenumbering.HashCons) + provides a mechanism for identifying expressions that have the same syntactic + structure. + #+END_QUOTE + + Additions to the imports, and use: + #+BEGIN_SRC java + import semmle.code.cpp.valuenumbering.HashCons + ... + hashCons(expr) + #+END_SRC + + This step illustrates some subtle meanings of equality. In particular, there + is plain ===, GVN, and =hashCons=: + #+BEGIN_SRC java + // 0 results: + // (accessBase = allocSizeExpr or accessBase = allocArg) + + // Only 6 results: + + // ( + // gvnAccessIdx = gvnAllocSizeExpr or + // gvnAccessIdx = globalValueNumber(allocArg) + // ) + + // 9 results: + ( + hashCons(accessBase) = hashCons(allocSizeExpr) or + hashCons(accessBase) = hashCons(allocArg) + ) + + #+END_SRC + +*** Solution + #+INCLUDE: "Example9a.ql" src java + +*** First 5 results + #+INCLUDE: "../session-tests/Example9a/example9a.expected" :lines "-6"’ diff --git a/session/session.ql b/session/session.ql new file mode 100644 index 0000000..bc98c07 --- /dev/null +++ b/session/session.ql @@ -0,0 +1,53 @@ +/** + * @ kind problem + */ + +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +// Step 9 +from + AllocationExpr buffer, ArrayExpr access, Expr accessIdx, Expr allocSizeExpr, GVN gvnAccess, + GVN gvnAlloc +where + // malloc (100) + // ^^^^^^^^^^^^ AllocationExpr buffer + // + // buf[...] + // ^^^ ArrayExpr access + // buf[...] + // ^^^ accessIdx + accessIdx = access.getArrayOffset() and + // + // malloc (100) + // ^^^ allocSizeExpr / bufferSize + // unsigned long size = 100; + // ... + // char *buf = malloc(size); + DataFlow::localExprFlow(allocSizeExpr, buffer.getSizeExpr()) and + // char *buf = ... buf[0]; + // ^^^ ---> ^^^ + // or + // malloc(100); buf[0] + // ^^^ --------> ^^^ + // + DataFlow::localExprFlow(buffer, access.getArrayBase()) and + // + // Use GVN + globalValueNumber(accessIdx) = gvnAccess and + globalValueNumber(allocSizeExpr) = gvnAlloc and + ( + gvnAccess = gvnAlloc + or + // buf[sz * x * y] above + // buf[sz * x * y + 1]; + exists(AddExpr add | + accessIdx = add and + // add.getAnOperand() = accessIdx and + add.getAnOperand().getValue().toInt() > 0 and + globalValueNumber(add.getAnOperand()) = gvnAlloc + ) + ) +select access, gvnAccess, gvnAlloc diff --git a/snapshot-from b/snapshot-from new file mode 100755 index 0000000..ddc1706 --- /dev/null +++ b/snapshot-from @@ -0,0 +1,40 @@ +#!/bin/bash -e +usage="$0 from.ql to + +Create +session-tests/queryfile/ +├── queryfile.expected +├── queryfile..qlref +└── test.c + +Example: +$0 session/example8.ql example8a +" + +if [ $# -ne 2 ]; then + echo "$usage" + exit 1 +fi + +query=$1 +from=$(basename $(echo $1 | sed s/\.ql//g;)) +to=session-tests/$2 +tof=$2 + +if [ ! -f $query ] ; then + echo "Missing source query file $query (1st argument)" + exit 1 +fi + +echo "Creating test directory $to" +mkdir -p $to +echo "no value" > $to/$tof.expected +echo $tof.ql > $to/$tof.qlref +cp session-db/DB/db.c $to/test.c + +echo "Creating source file $to" +cp $query session/$tof.ql + + + + diff --git a/solutions/Exercise6.ql b/solutions/Exercise6.ql index f316e36..34516d9 100644 --- a/solutions/Exercise6.ql +++ b/solutions/Exercise6.ql @@ -9,6 +9,7 @@ import cpp import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.valuenumbering.GlobalValueNumbering +import RuntimeValues /** * Gets an expression that flows to `dest` and has a constant value. @@ -36,73 +37,38 @@ int getMaxStatedValue(Expr e) { result = upperBound(e).minimum(max(getSourceConstantExpr(e).getValue().toInt())) } -bindingset[expr] -int getExprOffsetValue(Expr expr, Expr base) { - result = expr.(AddExpr).getRightOperand().getValue().toInt() and - base = expr.(AddExpr).getLeftOperand() - or - result = -expr.(SubExpr).getRightOperand().getValue().toInt() and - base = expr.(SubExpr).getLeftOperand() - or - // currently only AddExpr and SubExpr are supported: else, fall-back to 0 - not expr instanceof AddExpr and - not expr instanceof SubExpr and - base = expr and - result = 0 +predicate allocatedBufferArrayAccess(ArrayExpr access, FunctionCall alloc) { + alloc.getTarget().hasName("malloc") and + DataFlow::localExprFlow(alloc, access.getArrayBase()) } -class AllocationCall extends FunctionCall { - AllocationCall() { this.getTarget() instanceof AllocationFunction } - - Expr getBuffer() { result = this } - - Expr getSizeExpr() { - // AllocationExpr may sometimes return a subexpression of the size expression - // in order to separate the size from a sizeof expression in a MulExpr. - exists(AllocationFunction f | - f = this.(FunctionCall).getTarget() and - result = this.(FunctionCall).getArgument(f.getSizeArg()) - ) - } - - int getFixedSize() { result = getMaxStatedValue(this.getSizeExpr()) } -} - -class AccessExpr extends ArrayExpr { - AllocationCall source; - - AccessExpr() { DataFlow::localExprFlow(source.getBuffer(), this.getArrayBase()) } - - AllocationCall getSource() { result = source } - - int getFixedArrayOffset() { - exists(Expr base, int offset | - offset = getExprOffsetValue(this.getArrayOffset(), base) and - result = getMaxStatedValue(base) + offset - ) - } +int getFixedArrayOffset(ArrayExpr access) { + exists(Expr base, int offset | + offset = getExprOffsetValue(access.getArrayOffset(), base) and + result = getMaxStatedValue(base) + offset + ) } predicate isOffsetOutOfBoundsConstant( - AccessExpr access, AllocationCall source, int allocSize, int accessOffset + ArrayExpr access, FunctionCall source, int allocSize, int accessOffset ) { - source = access.getSource() and - allocSize = source.getFixedSize() and - accessOffset = access.getFixedArrayOffset() and + allocatedBufferArrayAccess(access, source) and + allocSize = getMaxStatedValue(source.getArgument(0)) and + accessOffset = getFixedArrayOffset(access) and accessOffset >= allocSize } -predicate isOffsetOutOfBoundsGVN(AccessExpr access, AllocationCall source) { - source = access.getSource() and +predicate isOffsetOutOfBoundsGVN(ArrayExpr access, FunctionCall source) { + allocatedBufferArrayAccess(access, source) and not isOffsetOutOfBoundsConstant(access, source, _, _) and exists(Expr accessOffsetBase, int accessOffsetBaseValue | accessOffsetBaseValue = getExprOffsetValue(access.getArrayOffset(), accessOffsetBase) and - globalValueNumber(source.getSizeExpr()) = globalValueNumber(accessOffsetBase) and + globalValueNumber(source.getArgument(0)) = globalValueNumber(accessOffsetBase) and not accessOffsetBaseValue < 0 ) } -from AllocationCall source, AccessExpr access, string message +from FunctionCall source, ArrayExpr access, string message where exists(int allocSize, int accessOffset | isOffsetOutOfBoundsConstant(access, source, allocSize, accessOffset) and diff --git a/solutions/qlpack.yml b/solutions/qlpack.yml index b2d0347..8c53758 100644 --- a/solutions/qlpack.yml +++ b/solutions/qlpack.yml @@ -3,4 +3,5 @@ library: false name: solutions version: 0.0.1 dependencies: - codeql/cpp-all: 0.6.1 \ No newline at end of file + codeql/cpp-all: 0.6.1 + library: "*" \ No newline at end of file