Compare commits

..

1 Commits

Author SHA1 Message Date
yoff
e42def5f44 Fix comparison
This looks like an oversight, index -1 should probably not be a key.
2023-06-22 11:22:05 +02:00
3014 changed files with 112603 additions and 146096 deletions

View File

@@ -1 +1 @@
6.3.1 6.1.2

View File

@@ -1,29 +0,0 @@
name: "Check implicit this warnings"
on:
workflow_dispatch:
pull_request:
paths:
- "**qlpack.yml"
branches:
- main
- "rc/*"
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check that implicit this warnings is enabled for all packs
shell: bash
run: |
EXIT_CODE=0
packs="$(find . -iname 'qlpack.yml')"
for pack_file in ${packs}; do
option="$(yq '.warnOnImplicitThis' ${pack_file})"
if [ "${option}" != "true" ]; then
echo "::error file=${pack_file}::warnOnImplicitThis property must be set to 'true' for pack ${pack_file}"
EXIT_CODE=1
fi
done
exit "${EXIT_CODE}"

View File

@@ -7,17 +7,15 @@ on:
- .github/workflows/go-tests-other-os.yml - .github/workflows/go-tests-other-os.yml
- .github/actions/** - .github/actions/**
- codeql-workspace.yml - codeql-workspace.yml
env:
GO_VERSION: '~1.21.0'
jobs: jobs:
test-mac: test-mac:
name: Test MacOS name: Test MacOS
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- name: Set up Go ${{ env.GO_VERSION }} - name: Set up Go 1.20
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: ${{ env.GO_VERSION }} go-version: '1.20'
id: go id: go
- name: Check out code - name: Check out code
@@ -49,10 +47,10 @@ jobs:
name: Test Windows name: Test Windows
runs-on: windows-latest-xl runs-on: windows-latest-xl
steps: steps:
- name: Set up Go ${{ env.GO_VERSION }} - name: Set up Go 1.20
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: ${{ env.GO_VERSION }} go-version: '1.20'
id: go id: go
- name: Check out code - name: Check out code

View File

@@ -15,17 +15,15 @@ on:
- .github/workflows/go-tests.yml - .github/workflows/go-tests.yml
- .github/actions/** - .github/actions/**
- codeql-workspace.yml - codeql-workspace.yml
env:
GO_VERSION: '~1.21.0'
jobs: jobs:
test-linux: test-linux:
name: Test Linux (Ubuntu) name: Test Linux (Ubuntu)
runs-on: ubuntu-latest-xl runs-on: ubuntu-latest-xl
steps: steps:
- name: Set up Go ${{ env.GO_VERSION }} - name: Set up Go 1.20
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: ${{ env.GO_VERSION }} go-version: '1.20'
id: go id: go
- name: Check out code - name: Check out code

View File

@@ -14,7 +14,6 @@ on:
pull_request: pull_request:
paths: paths:
- "ruby/**" - "ruby/**"
- "shared/**"
- .github/workflows/ruby-qltest.yml - .github/workflows/ruby-qltest.yml
- .github/actions/fetch-codeql/action.yml - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml - codeql-workspace.yml

View File

@@ -5,9 +5,9 @@ repos:
rev: v3.2.0 rev: v3.2.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)|.*\.patch exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
- id: end-of-file-fixer - id: end-of-file-fixer
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)|.*\.patch exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
- repo: https://github.com/pre-commit/mirrors-clang-format - repo: https://github.com/pre-commit/mirrors-clang-format
rev: v13.0.1 rev: v13.0.1
@@ -21,11 +21,6 @@ repos:
- id: autopep8 - id: autopep8
files: ^misc/codegen/.*\.py files: ^misc/codegen/.*\.py
- repo: https://github.com/warchant/pre-commit-buildifier
rev: 0.0.2
hooks:
- id: buildifier
- repo: local - repo: local
hooks: hooks:
- id: codeql-format - id: codeql-format

View File

@@ -14,16 +14,14 @@ If you have an idea for a query that you would like to share with other CodeQL u
1. **Directory structure** 1. **Directory structure**
There are eight language-specific query directories in this repository: There are six language-specific query directories in this repository:
* C/C++: `cpp/ql/src` * C/C++: `cpp/ql/src`
* C#: `csharp/ql/src` * C#: `csharp/ql/src`
* Go: `go/ql/src` * Java: `java/ql/src`
* Java/Kotlin: `java/ql/src`
* JavaScript: `javascript/ql/src` * JavaScript: `javascript/ql/src`
* Python: `python/ql/src` * Python: `python/ql/src`
* Ruby: `ruby/ql/src` * Ruby: `ruby/ql/src`
* Swift: `swift/ql/src`
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose. Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`. - Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.

View File

@@ -4,8 +4,6 @@ provide:
- "*/ql/test/qlpack.yml" - "*/ql/test/qlpack.yml"
- "*/ql/examples/qlpack.yml" - "*/ql/examples/qlpack.yml"
- "*/ql/consistency-queries/qlpack.yml" - "*/ql/consistency-queries/qlpack.yml"
- "*/ql/automodel/src/qlpack.yml"
- "*/ql/automodel/test/qlpack.yml"
- "shared/*/qlpack.yml" - "shared/*/qlpack.yml"
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml" - "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
- "go/ql/config/legacy-support/qlpack.yml" - "go/ql/config/legacy-support/qlpack.yml"

View File

@@ -1,4 +1,24 @@
{ {
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlow.qll"
],
"DataFlowImpl Java/C++/C#/Go/Python/Ruby/Swift": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
],
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift Legacy Configuration": [ "DataFlow Java/C++/C#/Go/Python/Ruby/Swift Legacy Configuration": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll", "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll", "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
@@ -22,6 +42,7 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll", "csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll", "go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll", "go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll", "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll", "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll", "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
@@ -32,6 +53,26 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll", "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll" "swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll"
], ],
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift Common": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll"
],
"TaintTracking Java/C++/C#/Go/Python/Ruby/Swift": [
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTracking.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTracking.qll",
"go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTracking.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTracking.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTracking.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTracking.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTracking.qll"
],
"TaintTracking Legacy Configuration Java/C++/C#/Go/Python/Ruby/Swift": [ "TaintTracking Legacy Configuration Java/C++/C#/Go/Python/Ruby/Swift": [
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll", "cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll", "cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
@@ -55,6 +96,15 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll", "ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll" "swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
], ],
"DataFlow Java/C++/C#/Python/Ruby/Swift Consistency checks": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll"
],
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [ "DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll", "java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll", "csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
@@ -464,6 +514,11 @@
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll", "ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll",
"swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll" "swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll"
], ],
"CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll",
"swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll"
],
"TypeTracker": [ "TypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll", "python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll" "ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
@@ -547,9 +602,5 @@
"EncryptionKeySizes Python/Java": [ "EncryptionKeySizes Python/Java": [
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll", "python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll" "java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
],
"Python model summaries test extension": [
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
] ]
} }

View File

@@ -327,7 +327,7 @@ namespace Semmle.Autobuild.Cpp.Tests
{ {
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1; Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0; Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program^ Files^ ^(x86^)\Microsoft^ Visual^ Studio^ 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0; Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = ""; Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1; Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0; Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;

View File

@@ -1,2 +0,0 @@
description: Remove _Float128 type
compatibility: full

View File

@@ -2,4 +2,3 @@ name: codeql/cpp-downgrades
groups: cpp groups: cpp
downgrades: . downgrades: .
library: true library: true
warnOnImplicitThis: true

View File

@@ -4,4 +4,3 @@ groups:
- examples - examples
dependencies: dependencies:
codeql/cpp-all: ${workspace} codeql/cpp-all: ${workspace}
warnOnImplicitThis: true

View File

@@ -1,71 +1,3 @@
## 0.9.2
### Deprecated APIs
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
### New Features
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
### Minor Analysis Improvements
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
## 0.9.1
No user-facing changes.
## 0.9.0
### Breaking Changes
* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
### Major Analysis Improvements
* The `PrintAST` library now also prints global and namespace variables and their initializers.
### Minor Analysis Improvements
* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types.
## 0.8.1
### Deprecated APIs
* The library `semmle.code.cpp.dataflow.DataFlow` has been deprecated. Please use `semmle.code.cpp.dataflow.new.DataFlow` instead.
### New Features
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Data flow configurations can now include a predicate `neverSkip(Node node)`
in order to ensure inclusion of certain nodes in the path explanations. The
predicate defaults to the end-points of the additional flow steps provided in
the configuration, which means that such steps now always are visible by
default in path explanations.
* The `IRGuards` library has improved handling of pointer addition and subtraction operations.
## 0.8.0
### New Features
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.
## 0.7.4
No user-facing changes.
## 0.7.3 ## 0.7.3
### Minor Analysis Improvements ### Minor Analysis Improvements

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.

View File

@@ -1,3 +0,0 @@
## 0.7.4
No user-facing changes.

View File

@@ -1,9 +0,0 @@
## 0.8.0
### New Features
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.

View File

@@ -1,19 +0,0 @@
## 0.8.1
### Deprecated APIs
* The library `semmle.code.cpp.dataflow.DataFlow` has been deprecated. Please use `semmle.code.cpp.dataflow.new.DataFlow` instead.
### New Features
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Data flow configurations can now include a predicate `neverSkip(Node node)`
in order to ensure inclusion of certain nodes in the path explanations. The
predicate defaults to the end-points of the additional flow steps provided in
the configuration, which means that such steps now always are visible by
default in path explanations.
* The `IRGuards` library has improved handling of pointer addition and subtraction operations.

View File

@@ -1,14 +0,0 @@
## 0.9.0
### Breaking Changes
* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
### Major Analysis Improvements
* The `PrintAST` library now also prints global and namespace variables and their initializers.
### Minor Analysis Improvements
* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types.

View File

@@ -1,3 +0,0 @@
## 0.9.1
No user-facing changes.

View File

@@ -1,14 +0,0 @@
## 0.9.2
### Deprecated APIs
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
### New Features
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
### Minor Analysis Improvements
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 0.9.2 lastReleaseVersion: 0.7.3

View File

@@ -18,10 +18,10 @@ external string selectedSourceFile();
class Cfg extends PrintAstConfiguration { class Cfg extends PrintAstConfiguration {
/** /**
* Holds if the AST for `decl` should be printed. * Holds if the AST for `func` should be printed.
* Print All declarations from the selected file. * Print All functions from the selected file.
*/ */
override predicate shouldPrintDeclaration(Declaration decl) { override predicate shouldPrintFunction(Function func) {
decl.getFile() = getFileBySourceArchiveName(selectedSourceFile()) func.getFile() = getFileBySourceArchiveName(selectedSourceFile())
} }
} }

View File

@@ -1,12 +1,11 @@
name: codeql/cpp-all name: codeql/cpp-all
version: 0.9.2 version: 0.7.4-dev
groups: cpp groups: cpp
dbscheme: semmlecode.cpp.dbscheme dbscheme: semmlecode.cpp.dbscheme
extractor: cpp extractor: cpp
library: true library: true
upgrades: upgrades upgrades: upgrades
dependencies: dependencies:
codeql/dataflow: ${workspace}
codeql/ssa: ${workspace} codeql/ssa: ${workspace}
codeql/tutorial: ${workspace} codeql/tutorial: ${workspace}
codeql/util: ${workspace} codeql/util: ${workspace}

View File

@@ -6,9 +6,11 @@ private import PrintAST
* that requests that function, or no `PrintASTConfiguration` exists. * that requests that function, or no `PrintASTConfiguration` exists.
*/ */
private predicate shouldPrintDeclaration(Declaration decl) { private predicate shouldPrintDeclaration(Declaration decl) {
not (decl instanceof Function or decl instanceof GlobalOrNamespaceVariable) not decl instanceof Function
or or
exists(PrintAstConfiguration config | config.shouldPrintDeclaration(decl)) not exists(PrintAstConfiguration c)
or
exists(PrintAstConfiguration config | config.shouldPrintFunction(decl))
} }
/** /**

View File

@@ -9,13 +9,13 @@ import cpp
import PrintAST import PrintAST
/** /**
* Temporarily tweak this class or make a copy to control which declarations are * Temporarily tweak this class or make a copy to control which functions are
* printed. * printed.
*/ */
class Cfg extends PrintAstConfiguration { class Cfg extends PrintAstConfiguration {
/** /**
* TWEAK THIS PREDICATE AS NEEDED. * TWEAK THIS PREDICATE AS NEEDED.
* Holds if the AST for `decl` should be printed. * Holds if the AST for `func` should be printed.
*/ */
override predicate shouldPrintDeclaration(Declaration decl) { any() } override predicate shouldPrintFunction(Function func) { any() }
} }

View File

@@ -1,9 +1,9 @@
/** /**
* Provides queries to pretty-print a C++ AST as a graph. * Provides queries to pretty-print a C++ AST as a graph.
* *
* By default, this will print the AST for all functions and global and namespace variables in * By default, this will print the AST for all functions in the database. To change this behavior,
* the database. To change this behavior, extend `PrintASTConfiguration` and override * extend `PrintASTConfiguration` and override `shouldPrintFunction` to hold for only the functions
* `shouldPrintDeclaration` to hold for only the declarations you wish to view the AST for. * you wish to view the AST for.
*/ */
import cpp import cpp
@@ -12,7 +12,7 @@ private import semmle.code.cpp.Print
private newtype TPrintAstConfiguration = MkPrintAstConfiguration() private newtype TPrintAstConfiguration = MkPrintAstConfiguration()
/** /**
* The query can extend this class to control which declarations are printed. * The query can extend this class to control which functions are printed.
*/ */
class PrintAstConfiguration extends TPrintAstConfiguration { class PrintAstConfiguration extends TPrintAstConfiguration {
/** /**
@@ -21,16 +21,14 @@ class PrintAstConfiguration extends TPrintAstConfiguration {
string toString() { result = "PrintASTConfiguration" } string toString() { result = "PrintASTConfiguration" }
/** /**
* Holds if the AST for `decl` should be printed. By default, holds for all * Holds if the AST for `func` should be printed. By default, holds for all
* functions and global and namespace variables. Currently, does not support any * functions.
* other declaration types.
*/ */
predicate shouldPrintDeclaration(Declaration decl) { any() } predicate shouldPrintFunction(Function func) { any() }
} }
private predicate shouldPrintDeclaration(Declaration decl) { private predicate shouldPrintFunction(Function func) {
exists(PrintAstConfiguration config | config.shouldPrintDeclaration(decl)) and exists(PrintAstConfiguration config | config.shouldPrintFunction(func))
(decl instanceof Function or decl instanceof GlobalOrNamespaceVariable)
} }
bindingset[s] bindingset[s]
@@ -71,7 +69,7 @@ private predicate locationSortKeys(Locatable ast, string file, int line, int col
) )
} }
private Declaration getAnEnclosingDeclaration(Locatable ast) { private Function getEnclosingFunction(Locatable ast) {
result = ast.(Expr).getEnclosingFunction() result = ast.(Expr).getEnclosingFunction()
or or
result = ast.(Stmt).getEnclosingFunction() result = ast.(Stmt).getEnclosingFunction()
@@ -80,10 +78,6 @@ private Declaration getAnEnclosingDeclaration(Locatable ast) {
or or
result = ast.(Parameter).getFunction() result = ast.(Parameter).getFunction()
or or
result = ast.(Expr).getEnclosingDeclaration()
or
result = ast.(Initializer).getDeclaration()
or
result = ast result = ast
} }
@@ -92,21 +86,21 @@ private Declaration getAnEnclosingDeclaration(Locatable ast) {
* nodes for things like parameter lists and constructor init lists. * nodes for things like parameter lists and constructor init lists.
*/ */
private newtype TPrintAstNode = private newtype TPrintAstNode =
TAstNode(Locatable ast) { shouldPrintDeclaration(getAnEnclosingDeclaration(ast)) } or TAstNode(Locatable ast) { shouldPrintFunction(getEnclosingFunction(ast)) } or
TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) { TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) {
// We create a unique node for each pair of (stmt, entry), to avoid having one node with // We create a unique node for each pair of (stmt, entry), to avoid having one node with
// multiple parents due to extractor bug CPP-413. // multiple parents due to extractor bug CPP-413.
stmt.getADeclarationEntry() = entry and stmt.getADeclarationEntry() = entry and
shouldPrintDeclaration(stmt.getEnclosingFunction()) shouldPrintFunction(stmt.getEnclosingFunction())
} or } or
TParametersNode(Function func) { shouldPrintDeclaration(func) } or TParametersNode(Function func) { shouldPrintFunction(func) } or
TConstructorInitializersNode(Constructor ctor) { TConstructorInitializersNode(Constructor ctor) {
ctor.hasEntryPoint() and ctor.hasEntryPoint() and
shouldPrintDeclaration(ctor) shouldPrintFunction(ctor)
} or } or
TDestructorDestructionsNode(Destructor dtor) { TDestructorDestructionsNode(Destructor dtor) {
dtor.hasEntryPoint() and dtor.hasEntryPoint() and
shouldPrintDeclaration(dtor) shouldPrintFunction(dtor)
} }
/** /**
@@ -164,10 +158,10 @@ class PrintAstNode extends TPrintAstNode {
/** /**
* Holds if this node should be printed in the output. By default, all nodes * Holds if this node should be printed in the output. By default, all nodes
* within functions and global and namespace variables are printed, but the query * within a function are printed, but the query can override
* can override `PrintASTConfiguration.shouldPrintDeclaration` to filter the output. * `PrintASTConfiguration.shouldPrintFunction` to filter the output.
*/ */
final predicate shouldPrint() { shouldPrintDeclaration(this.getEnclosingDeclaration()) } final predicate shouldPrint() { shouldPrintFunction(this.getEnclosingFunction()) }
/** /**
* Gets the children of this node. * Gets the children of this node.
@@ -235,15 +229,10 @@ class PrintAstNode extends TPrintAstNode {
abstract string getChildAccessorPredicateInternal(int childIndex); abstract string getChildAccessorPredicateInternal(int childIndex);
/** /**
* Gets the `Declaration` that contains this node. * Gets the `Function` that contains this node.
*/ */
private Declaration getEnclosingDeclaration() { result = this.getParent*().getDeclaration() } private Function getEnclosingFunction() {
result = this.getParent*().(FunctionNode).getFunction()
/**
* Gets the `Declaration` this node represents.
*/
private Declaration getDeclaration() {
result = this.(AstNode).getAst() and shouldPrintDeclaration(result)
} }
} }
@@ -582,53 +571,16 @@ class DestructorDestructionsNode extends PrintAstNode, TDestructorDestructionsNo
final Destructor getDestructor() { result = dtor } final Destructor getDestructor() { result = dtor }
} }
abstract private class FunctionOrGlobalOrNamespaceVariableNode extends AstNode {
override string toString() { result = qlClass(ast) + getIdentityString(ast) }
private int getOrder() {
this =
rank[result](FunctionOrGlobalOrNamespaceVariableNode node, Declaration decl, string file,
int line, int column |
node.getAst() = decl and
locationSortKeys(decl, file, line, column)
|
node order by file, line, column, getIdentityString(decl)
)
}
override string getProperty(string key) {
result = super.getProperty(key)
or
key = "semmle.order" and result = this.getOrder().toString()
}
}
/**
* A node representing a `GlobalOrNamespaceVariable`.
*/
class GlobalOrNamespaceVariableNode extends FunctionOrGlobalOrNamespaceVariableNode {
GlobalOrNamespaceVariable var;
GlobalOrNamespaceVariableNode() { var = ast }
override PrintAstNode getChildInternal(int childIndex) {
childIndex = 0 and
result.(AstNode).getAst() = var.getInitializer()
}
override string getChildAccessorPredicateInternal(int childIndex) {
childIndex = 0 and result = "getInitializer()"
}
}
/** /**
* A node representing a `Function`. * A node representing a `Function`.
*/ */
class FunctionNode extends FunctionOrGlobalOrNamespaceVariableNode { class FunctionNode extends AstNode {
Function func; Function func;
FunctionNode() { func = ast } FunctionNode() { func = ast }
override string toString() { result = qlClass(func) + getIdentityString(func) }
override PrintAstNode getChildInternal(int childIndex) { override PrintAstNode getChildInternal(int childIndex) {
childIndex = 0 and childIndex = 0 and
result.(ParametersNode).getFunction() = func result.(ParametersNode).getFunction() = func
@@ -652,10 +604,31 @@ class FunctionNode extends FunctionOrGlobalOrNamespaceVariableNode {
or or
childIndex = 3 and result = "<destructions>" childIndex = 3 and result = "<destructions>"
} }
private int getOrder() {
this =
rank[result](FunctionNode node, Function function, string file, int line, int column |
node.getAst() = function and
locationSortKeys(function, file, line, column)
|
node order by file, line, column, getIdentityString(function)
)
}
override string getProperty(string key) {
result = super.getProperty(key)
or
key = "semmle.order" and result = this.getOrder().toString()
}
/**
* Gets the `Function` this node represents.
*/
final Function getFunction() { result = func }
} }
private string getChildAccessorWithoutConversions(Locatable parent, Element child) { private string getChildAccessorWithoutConversions(Locatable parent, Element child) {
shouldPrintDeclaration(getAnEnclosingDeclaration(parent)) and shouldPrintFunction(getEnclosingFunction(parent)) and
( (
exists(Stmt s | s = parent | exists(Stmt s | s = parent |
namedStmtChildPredicates(s, child, result) namedStmtChildPredicates(s, child, result)
@@ -674,7 +647,7 @@ private string getChildAccessorWithoutConversions(Locatable parent, Element chil
} }
private predicate namedStmtChildPredicates(Locatable s, Element e, string pred) { private predicate namedStmtChildPredicates(Locatable s, Element e, string pred) {
shouldPrintDeclaration(getAnEnclosingDeclaration(s)) and shouldPrintFunction(getEnclosingFunction(s)) and
( (
exists(int n | s.(BlockStmt).getStmt(n) = e and pred = "getStmt(" + n + ")") exists(int n | s.(BlockStmt).getStmt(n) = e and pred = "getStmt(" + n + ")")
or or
@@ -762,14 +735,12 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
} }
private predicate namedExprChildPredicates(Expr expr, Element ele, string pred) { private predicate namedExprChildPredicates(Expr expr, Element ele, string pred) {
shouldPrintDeclaration(expr.getEnclosingDeclaration()) and shouldPrintFunction(expr.getEnclosingFunction()) and
( (
expr.(Access).getTarget() = ele and pred = "getTarget()" expr.(Access).getTarget() = ele and pred = "getTarget()"
or or
expr.(VariableAccess).getQualifier() = ele and pred = "getQualifier()" expr.(VariableAccess).getQualifier() = ele and pred = "getQualifier()"
or or
expr.(FunctionAccess).getQualifier() = ele and pred = "getQualifier()"
or
exists(Field f | exists(Field f |
expr.(ClassAggregateLiteral).getAFieldExpr(f) = ele and expr.(ClassAggregateLiteral).getAFieldExpr(f) = ele and
pred = "getAFieldExpr(" + f.toString() + ")" pred = "getAFieldExpr(" + f.toString() + ")"
@@ -826,11 +797,17 @@ private predicate namedExprChildPredicates(Expr expr, Element ele, string pred)
or or
expr.(Conversion).getExpr() = ele and pred = "getExpr()" expr.(Conversion).getExpr() = ele and pred = "getExpr()"
or or
expr.(DeleteOrDeleteArrayExpr).getDeallocatorCall() = ele and pred = "getDeallocatorCall()" expr.(DeleteArrayExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()"
or or
expr.(DeleteOrDeleteArrayExpr).getDestructorCall() = ele and pred = "getDestructorCall()" expr.(DeleteArrayExpr).getDestructorCall() = ele and pred = "getDestructorCall()"
or or
expr.(DeleteOrDeleteArrayExpr).getExpr() = ele and pred = "getExpr()" expr.(DeleteArrayExpr).getExpr() = ele and pred = "getExpr()"
or
expr.(DeleteExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()"
or
expr.(DeleteExpr).getDestructorCall() = ele and pred = "getDestructorCall()"
or
expr.(DeleteExpr).getExpr() = ele and pred = "getExpr()"
or or
expr.(DestructorFieldDestruction).getExpr() = ele and pred = "getExpr()" expr.(DestructorFieldDestruction).getExpr() = ele and pred = "getExpr()"
or or

View File

@@ -814,6 +814,9 @@ private predicate floatingPointTypeMapping(
// _Float128 // _Float128
kind = 49 and base = 2 and domain = TRealDomain() and realKind = 49 and extended = false kind = 49 and base = 2 and domain = TRealDomain() and realKind = 49 and extended = false
or or
// _Float128x
kind = 50 and base = 2 and domain = TRealDomain() and realKind = 50 and extended = true
or
// _Float16 // _Float16
kind = 52 and base = 2 and domain = TRealDomain() and realKind = 52 and extended = false kind = 52 and base = 2 and domain = TRealDomain() and realKind = 52 and extended = false
or or

View File

@@ -627,20 +627,6 @@ private predicate sub_lt(
x = int_value(rhs.getRight()) and x = int_value(rhs.getRight()) and
k = c - x k = c - x
) )
or
exists(PointerSubInstruction lhs, int c, int x |
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRight()) and
k = c + x
)
or
exists(PointerSubInstruction rhs, int c, int x |
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRight()) and
k = c - x
)
} }
// left + x < right + c => left < right + (c-x) // left + x < right + c => left < right + (c-x)
@@ -667,26 +653,6 @@ private predicate add_lt(
) and ) and
k = c + x k = c + x
) )
or
exists(PointerAddInstruction lhs, int c, int x |
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
) and
k = c - x
)
or
exists(PointerAddInstruction rhs, int c, int x |
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeft())
) and
k = c + x
)
} }
// left - x == right + c => left == right + (c+x) // left - x == right + c => left == right + (c+x)
@@ -707,20 +673,6 @@ private predicate sub_eq(
x = int_value(rhs.getRight()) and x = int_value(rhs.getRight()) and
k = c - x k = c - x
) )
or
exists(PointerSubInstruction lhs, int c, int x |
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRight()) and
k = c + x
)
or
exists(PointerSubInstruction rhs, int c, int x |
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRight()) and
k = c - x
)
} }
// left + x == right + c => left == right + (c-x) // left + x == right + c => left == right + (c-x)
@@ -747,26 +699,6 @@ private predicate add_eq(
) and ) and
k = c + x k = c + x
) )
or
exists(PointerAddInstruction lhs, int c, int x |
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
) and
k = c - x
)
or
exists(PointerAddInstruction rhs, int c, int x |
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeft())
) and
k = c + x
)
} }
/** The int value of integer constant expression. */ /** The int value of integer constant expression. */

View File

@@ -332,12 +332,21 @@ private Node getControlOrderChildSparse(Node n, int i) {
n = any(ConditionDeclExpr cd | i = 0 and result = cd.getInitializingExpr()) n = any(ConditionDeclExpr cd | i = 0 and result = cd.getInitializingExpr())
or or
n = n =
any(DeleteOrDeleteArrayExpr del | any(DeleteExpr del |
i = 0 and result = del.getExpr() i = 0 and result = del.getExpr()
or or
i = 1 and result = del.getDestructorCall() i = 1 and result = del.getDestructorCall()
or or
i = 2 and result = del.getDeallocatorCall() i = 2 and result = del.getAllocatorCall()
)
or
n =
any(DeleteArrayExpr del |
i = 0 and result = del.getExpr()
or
i = 1 and result = del.getDestructorCall()
or
i = 2 and result = del.getAllocatorCall()
) )
or or
n = n =

View File

@@ -20,14 +20,10 @@
import cpp import cpp
/** /**
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
*
* Provides classes for performing local (intra-procedural) and * Provides classes for performing local (intra-procedural) and
* global (inter-procedural) data flow analyses. * global (inter-procedural) data flow analyses.
*/ */
deprecated module DataFlow { module DataFlow {
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific import semmle.code.cpp.dataflow.internal.DataFlow
private import codeql.dataflow.DataFlow
import DataFlowMake<CppOldDataFlow>
import semmle.code.cpp.dataflow.internal.DataFlowImpl1 import semmle.code.cpp.dataflow.internal.DataFlowImpl1
} }

View File

@@ -12,11 +12,9 @@
import cpp import cpp
/** /**
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow2` instead.
*
* Provides classes for performing local (intra-procedural) and * Provides classes for performing local (intra-procedural) and
* global (inter-procedural) data flow analyses. * global (inter-procedural) data flow analyses.
*/ */
deprecated module DataFlow2 { module DataFlow2 {
import semmle.code.cpp.dataflow.internal.DataFlowImpl2 import semmle.code.cpp.dataflow.internal.DataFlowImpl2
} }

View File

@@ -12,11 +12,9 @@
import cpp import cpp
/** /**
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow3` instead.
*
* Provides classes for performing local (intra-procedural) and * Provides classes for performing local (intra-procedural) and
* global (inter-procedural) data flow analyses. * global (inter-procedural) data flow analyses.
*/ */
deprecated module DataFlow3 { module DataFlow3 {
import semmle.code.cpp.dataflow.internal.DataFlowImpl3 import semmle.code.cpp.dataflow.internal.DataFlowImpl3
} }

View File

@@ -12,11 +12,9 @@
import cpp import cpp
/** /**
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow4` instead.
*
* Provides classes for performing local (intra-procedural) and * Provides classes for performing local (intra-procedural) and
* global (inter-procedural) data flow analyses. * global (inter-procedural) data flow analyses.
*/ */
deprecated module DataFlow4 { module DataFlow4 {
import semmle.code.cpp.dataflow.internal.DataFlowImpl4 import semmle.code.cpp.dataflow.internal.DataFlowImpl4
} }

View File

@@ -19,16 +19,10 @@ import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.dataflow.DataFlow2 import semmle.code.cpp.dataflow.DataFlow2
/** /**
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.TaintTracking` instead.
*
* Provides classes for performing local (intra-procedural) and * Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses. * global (inter-procedural) taint-tracking analyses.
*/ */
deprecated module TaintTracking { module TaintTracking {
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingParameter::Public import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTracking
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
private import semmle.code.cpp.dataflow.internal.TaintTrackingImplSpecific
private import codeql.dataflow.TaintTracking
import TaintFlowMake<CppOldDataFlow, CppOldTaintTracking>
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
} }

View File

@@ -12,11 +12,9 @@
*/ */
/** /**
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.TaintTracking2` instead.
*
* Provides classes for performing local (intra-procedural) and * Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses. * global (inter-procedural) taint-tracking analyses.
*/ */
deprecated module TaintTracking2 { module TaintTracking2 {
import semmle.code.cpp.dataflow.internal.tainttracking2.TaintTrackingImpl import semmle.code.cpp.dataflow.internal.tainttracking2.TaintTrackingImpl
} }

View File

@@ -0,0 +1,412 @@
/**
* Provides an implementation of global (interprocedural) data flow. This file
* re-exports the local (intraprocedural) data flow analysis from
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
* through the `Global` and `GlobalWithState` modules.
*/
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
import DataFlowImplCommonPublic
private import DataFlowImpl
/** An input configuration for data flow. */
signature module ConfigSig {
/**
* Holds if `source` is a relevant data flow source.
*/
predicate isSource(Node source);
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(Node sink);
/**
* Holds if data flow through `node` is prohibited. This completely removes
* `node` from the data flow graph.
*/
default predicate isBarrier(Node node) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrierIn(Node node) { none() }
/** Holds if data flow out of `node` is prohibited. */
default predicate isBarrierOut(Node node) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
default predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be
* taken at `node`.
*/
default predicate allowImplicitRead(Node node, ContentSet c) { none() }
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
default int fieldFlowBranchLimit() { result = 2 }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*
* These features are generally not relevant for typical end-to-end data flow
* queries, but should only be used for constructing paths that need to
* somehow be pluggable in another path context.
*/
default FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `flowPath`. */
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `flowPath`. */
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (as it is in a `path-problem` query).
*/
default predicate includeHiddenNodes() { none() }
}
/** An input configuration for data flow using flow state. */
signature module StateConfigSig {
bindingset[this]
class FlowState;
/**
* Holds if `source` is a relevant data flow source with the given initial
* `state`.
*/
predicate isSource(Node source, FlowState state);
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node sink, FlowState state);
/**
* Holds if data flow through `node` is prohibited. This completely removes
* `node` from the data flow graph.
*/
default predicate isBarrier(Node node) { none() }
/**
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state);
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrierIn(Node node) { none() }
/** Holds if data flow out of `node` is prohibited. */
default predicate isBarrierOut(Node node) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
default predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2);
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be
* taken at `node`.
*/
default predicate allowImplicitRead(Node node, ContentSet c) { none() }
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
default int fieldFlowBranchLimit() { result = 2 }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*
* These features are generally not relevant for typical end-to-end data flow
* queries, but should only be used for constructing paths that need to
* somehow be pluggable in another path context.
*/
default FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `flowPath`. */
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `flowPath`. */
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (as it is in a `path-problem` query).
*/
default predicate includeHiddenNodes() { none() }
}
/**
* Gets the exploration limit for `partialFlow` and `partialFlowRev`
* measured in approximate number of interprocedural steps.
*/
signature int explorationLimitSig();
/**
* The output of a global data flow computation.
*/
signature module GlobalFlowSig {
/**
* A `Node` augmented with a call context (except for sinks) and an access path.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
class PathNode;
/**
* Holds if data can flow from `source` to `sink`.
*
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate flowPath(PathNode source, PathNode sink);
/**
* Holds if data can flow from `source` to `sink`.
*/
predicate flow(Node source, Node sink);
/**
* Holds if data can flow from some source to `sink`.
*/
predicate flowTo(Node sink);
/**
* Holds if data can flow from some source to `sink`.
*/
predicate flowToExpr(DataFlowExpr sink);
}
/**
* Constructs a global data flow computation.
*/
module Global<ConfigSig Config> implements GlobalFlowSig {
private module C implements FullStateConfigSig {
import DefaultState<Config>
import Config
}
import Impl<C>
}
/** DEPRECATED: Use `Global` instead. */
deprecated module Make<ConfigSig Config> implements GlobalFlowSig {
import Global<Config>
}
/**
* Constructs a global data flow computation using flow state.
*/
module GlobalWithState<StateConfigSig Config> implements GlobalFlowSig {
private module C implements FullStateConfigSig {
import Config
}
import Impl<C>
}
/** DEPRECATED: Use `GlobalWithState` instead. */
deprecated module MakeWithState<StateConfigSig Config> implements GlobalFlowSig {
import GlobalWithState<Config>
}
signature class PathNodeSig {
/** Gets a textual representation of this element. */
string toString();
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
);
/** Gets the underlying `Node`. */
Node getNode();
}
signature module PathGraphSig<PathNodeSig PathNode> {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
predicate edges(PathNode a, PathNode b);
/** Holds if `n` is a node in the graph of data flow path explanations. */
predicate nodes(PathNode n, string key, string val);
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out);
}
/**
* Constructs a `PathGraph` from two `PathGraph`s by disjoint union.
*/
module MergePathGraph<
PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig<PathNode1> Graph1,
PathGraphSig<PathNode2> Graph2>
{
private newtype TPathNode =
TPathNode1(PathNode1 p) or
TPathNode2(PathNode2 p)
/** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */
class PathNode extends TPathNode {
/** Gets this as a projection on the first given `PathGraph`. */
PathNode1 asPathNode1() { this = TPathNode1(result) }
/** Gets this as a projection on the second given `PathGraph`. */
PathNode2 asPathNode2() { this = TPathNode2(result) }
/** Gets a textual representation of this element. */
string toString() {
result = this.asPathNode1().toString() or
result = this.asPathNode2().toString()
}
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or
this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
Node getNode() {
result = this.asPathNode1().getNode() or
result = this.asPathNode2().getNode()
}
}
/**
* Provides the query predicates needed to include a graph in a path-problem query.
*/
module PathGraph implements PathGraphSig<PathNode> {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) {
Graph1::edges(a.asPathNode1(), b.asPathNode1()) or
Graph2::edges(a.asPathNode2(), b.asPathNode2())
}
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
Graph1::nodes(n.asPathNode1(), key, val) or
Graph2::nodes(n.asPathNode2(), key, val)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or
Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2())
}
}
}
/**
* Constructs a `PathGraph` from three `PathGraph`s by disjoint union.
*/
module MergePathGraph3<
PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3,
PathGraphSig<PathNode1> Graph1, PathGraphSig<PathNode2> Graph2, PathGraphSig<PathNode3> Graph3>
{
private module MergedInner = MergePathGraph<PathNode1, PathNode2, Graph1, Graph2>;
private module Merged =
MergePathGraph<MergedInner::PathNode, PathNode3, MergedInner::PathGraph, Graph3>;
/** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */
class PathNode instanceof Merged::PathNode {
/** Gets this as a projection on the first given `PathGraph`. */
PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() }
/** Gets this as a projection on the second given `PathGraph`. */
PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() }
/** Gets this as a projection on the third given `PathGraph`. */
PathNode3 asPathNode3() { result = super.asPathNode2() }
/** Gets a textual representation of this element. */
string toString() { result = super.toString() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
Node getNode() { result = super.getNode() }
}
/**
* Provides the query predicates needed to include a graph in a path-problem query.
*/
module PathGraph = Merged::PathGraph;
}

View File

@@ -5,8 +5,8 @@ private import DataFlowUtil
/** /**
* Gets a function that might be called by `call`. * Gets a function that might be called by `call`.
*/ */
Function viableCallable(DataFlowCall call) { Function viableCallable(Call call) {
result = call.(Call).getTarget() result = call.getTarget()
or or
// If the target of the call does not have a body in the snapshot, it might // If the target of the call does not have a body in the snapshot, it might
// be because the target is just a header declaration, and the real target // be because the target is just a header declaration, and the real target
@@ -58,13 +58,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
* Holds if the set of viable implementations that can be called by `call` * Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. * might be improved by knowing the call context.
*/ */
predicate mayBenefitFromCallContext(DataFlowCall call, Function f) { none() } predicate mayBenefitFromCallContext(Call call, Function f) { none() }
/** /**
* Gets a viable dispatch target of `call` in the context `ctx`. This is * Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference. * restricted to those `call`s for which a context might make a difference.
*/ */
Function viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() } Function viableImplInCallContext(Call call, Call ctx) { none() }
/** A parameter position represented by an integer. */ /** A parameter position represented by an integer. */
class ParameterPosition extends int { class ParameterPosition extends int {

File diff suppressed because it is too large Load Diff

View File

@@ -276,8 +276,6 @@ private module Config implements FullStateConfigSig {
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
} }
predicate isSink(Node sink) { none() }
predicate isSink(Node sink, FlowState state) { predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state)) getConfig(state).isSink(sink, getState(state))
or or
@@ -315,8 +313,6 @@ private module Config implements FullStateConfigSig {
any(Configuration config).allowImplicitRead(node, c) any(Configuration config).allowImplicitRead(node, c)
} }
predicate neverSkip(Node node) { none() }
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) } int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() } FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }

View File

@@ -276,8 +276,6 @@ private module Config implements FullStateConfigSig {
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
} }
predicate isSink(Node sink) { none() }
predicate isSink(Node sink, FlowState state) { predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state)) getConfig(state).isSink(sink, getState(state))
or or
@@ -315,8 +313,6 @@ private module Config implements FullStateConfigSig {
any(Configuration config).allowImplicitRead(node, c) any(Configuration config).allowImplicitRead(node, c)
} }
predicate neverSkip(Node node) { none() }
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) } int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() } FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }

View File

@@ -276,8 +276,6 @@ private module Config implements FullStateConfigSig {
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
} }
predicate isSink(Node sink) { none() }
predicate isSink(Node sink, FlowState state) { predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state)) getConfig(state).isSink(sink, getState(state))
or or
@@ -315,8 +313,6 @@ private module Config implements FullStateConfigSig {
any(Configuration config).allowImplicitRead(node, c) any(Configuration config).allowImplicitRead(node, c)
} }
predicate neverSkip(Node node) { none() }
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) } int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() } FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }

View File

@@ -276,8 +276,6 @@ private module Config implements FullStateConfigSig {
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
} }
predicate isSink(Node sink) { none() }
predicate isSink(Node sink, FlowState state) { predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state)) getConfig(state).isSink(sink, getState(state))
or or
@@ -315,8 +313,6 @@ private module Config implements FullStateConfigSig {
any(Configuration config).allowImplicitRead(node, c) any(Configuration config).allowImplicitRead(node, c)
} }
predicate neverSkip(Node node) { none() }
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) } int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() } FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }

View File

@@ -3,25 +3,297 @@
* data-flow classes and predicates. * data-flow classes and predicates.
*/ */
private import cpp private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific private import DataFlowImplSpecific::Public
private import TaintTrackingImplSpecific private import tainttracking1.TaintTrackingParameter::Private
private import codeql.dataflow.internal.DataFlowImplConsistency private import tainttracking1.TaintTrackingParameter::Public
private module Input implements InputSig<CppOldDataFlow> { module Consistency {
predicate argHasPostUpdateExclude(Private::ArgumentNode n) { private newtype TConsistencyConfiguration = MkConsistencyConfiguration()
// Is the null pointer (or something that's not really a pointer)
exists(n.asExpr().getValue()) /** A class for configuring the consistency queries. */
or class ConsistencyConfiguration extends TConsistencyConfiguration {
// Isn't a pointer or is a pointer to const string toString() { none() }
forall(DerivedType dt | dt = n.asExpr().getActualType() |
dt.getBaseType().isConst() /** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
or predicate uniqueEnclosingCallableExclude(Node n) { none() }
dt.getBaseType() instanceof RoutineType
/** Holds if `call` should be excluded from the consistency test `uniqueCallEnclosingCallable`. */
predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) { none() }
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
predicate uniqueNodeLocationExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
predicate missingLocationExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
predicate postWithInFlowExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
predicate reverseReadExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `postHasUniquePre`. */
predicate postHasUniquePreExclude(PostUpdateNode n) { none() }
/** Holds if `n` should be excluded from the consistency test `uniquePostUpdate`. */
predicate uniquePostUpdateExclude(Node n) { none() }
/** Holds if `(call, ctx)` should be excluded from the consistency test `viableImplInCallContextTooLargeExclude`. */
predicate viableImplInCallContextTooLargeExclude(
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
) {
none()
}
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
none()
}
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
none()
}
/** Holds if `n` should be excluded from the consistency test `identityLocalStep`. */
predicate identityLocalStepExclude(Node n) { none() }
}
private class RelevantNode extends Node {
RelevantNode() {
this instanceof ArgumentNode or
this instanceof ParameterNode or
this instanceof ReturnNode or
this = getAnOutNode(_, _) or
simpleLocalFlowStep(this, _) or
simpleLocalFlowStep(_, this) or
jumpStep(this, _) or
jumpStep(_, this) or
storeStep(this, _, _) or
storeStep(_, _, this) or
readStep(this, _, _) or
readStep(_, _, this) or
defaultAdditionalTaintStep(this, _) or
defaultAdditionalTaintStep(_, this)
}
}
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
msg = "Node should have one enclosing callable but has " + c + "."
) )
// The above list of cases isn't exhaustive, but it narrows down the }
// consistency alerts enough that most of them are interesting.
query predicate uniqueCallEnclosingCallable(DataFlowCall call, string msg) {
exists(int c |
c = count(call.getEnclosingCallable()) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueCallEnclosingCallableExclude(call) and
msg = "Call should have one enclosing callable but has " + c + "."
)
}
query predicate uniqueType(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(getNodeType(n)) and
c != 1 and
msg = "Node should have one type but has " + c + "."
)
}
query predicate uniqueNodeLocation(Node n, string msg) {
exists(int c |
c =
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
msg = "Node should have one location but has " + c + "."
)
}
query predicate missingLocation(string msg) {
exists(int c |
c =
strictcount(Node n |
not n.hasLocationInfo(_, _, _, _, _) and
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
) and
msg = "Nodes without location: " + c
)
}
query predicate uniqueNodeToString(Node n, string msg) {
exists(int c |
c = count(n.toString()) and
c != 1 and
msg = "Node should have one toString but has " + c + "."
)
}
query predicate missingToString(string msg) {
exists(int c |
c = strictcount(Node n | not exists(n.toString())) and
msg = "Nodes without toString: " + c
)
}
query predicate parameterCallable(ParameterNode p, string msg) {
exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Local flow step does not preserve enclosing callable."
}
query predicate readStepIsLocal(Node n1, Node n2, string msg) {
readStep(n1, _, n2) and
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Read step does not preserve enclosing callable."
}
query predicate storeStepIsLocal(Node n1, Node n2, string msg) {
storeStep(n1, _, n2) and
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Store step does not preserve enclosing callable."
}
private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
t = typeRepr() and
not compatibleTypes(t, t) and
msg = "Type compatibility predicate is not reflexive."
}
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
c = nodeGetEnclosingCallable(n) and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
}
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
(
n = getAnOutNode(call, _) and
msg = "OutNode and call does not share enclosing callable."
or
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
// it is impossible for a result of `getPreUpdateNode` to be an
// instance of `PostUpdateNode`.
private Node getPre(PostUpdateNode n) {
result = n.getPreUpdateNode()
or
none()
}
query predicate postIsNotPre(PostUpdateNode n, string msg) {
getPre(n) = n and
msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
not any(ConsistencyConfiguration conf).postHasUniquePreExclude(n) and
exists(int c |
c = count(n.getPreUpdateNode()) and
c != 1 and
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
)
}
query predicate uniquePostUpdate(Node n, string msg) {
not any(ConsistencyConfiguration conf).uniquePostUpdateExclude(n) and
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
msg = "Node has multiple PostUpdateNodes."
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
query predicate reverseRead(Node n, string msg) {
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
msg = "Origin of readStep is missing a PostUpdateNode."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not any(ConsistencyConfiguration c).argHasPostUpdateExclude(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
// This predicate helps the compiler forget that in some languages
// it is impossible for a `PostUpdateNode` to be the target of
// `simpleLocalFlowStep`.
private predicate isPostUpdateNode(Node n) { n instanceof PostUpdateNode or none() }
query predicate postWithInFlow(Node n, string msg) {
isPostUpdateNode(n) and
not clearsContent(n, _) and
simpleLocalFlowStep(_, n) and
not any(ConsistencyConfiguration c).postWithInFlowExclude(n) and
msg = "PostUpdateNode should not be the target of local flow."
}
query predicate viableImplInCallContextTooLarge(
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
) {
callable = viableImplInCallContext(call, ctx) and
not callable = viableCallable(call) and
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
}
query predicate uniqueParameterNodeAtPosition(
DataFlowCallable c, ParameterPosition pos, Node p, string msg
) {
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
isParameterNode(p, c, pos) and
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
msg = "Parameters with overlapping positions."
}
query predicate uniqueParameterNodePosition(
DataFlowCallable c, ParameterPosition pos, Node p, string msg
) {
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
isParameterNode(p, c, pos) and
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
msg = "Parameter node with multiple positions."
}
query predicate uniqueContentApprox(Content c, string msg) {
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
msg = "Non-unique content approximation."
}
query predicate identityLocalStep(Node n, string msg) {
simpleLocalFlowStep(n, n) and
not any(ConsistencyConfiguration c).identityLocalStepExclude(n) and
msg = "Node steps to itself"
} }
} }
module Consistency = MakeConsistency<CppOldDataFlow, CppOldTaintTracking, Input>;

View File

@@ -276,8 +276,6 @@ private module Config implements FullStateConfigSig {
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
} }
predicate isSink(Node sink) { none() }
predicate isSink(Node sink, FlowState state) { predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state)) getConfig(state).isSink(sink, getState(state))
or or
@@ -315,8 +313,6 @@ private module Config implements FullStateConfigSig {
any(Configuration config).allowImplicitRead(node, c) any(Configuration config).allowImplicitRead(node, c)
} }
predicate neverSkip(Node node) { none() }
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) } int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() } FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }

View File

@@ -1,9 +1,6 @@
/** /**
* Provides C++-specific definitions for use in the data flow library. * Provides C++-specific definitions for use in the data flow library.
*/ */
private import codeql.dataflow.DataFlow
module Private { module Private {
import DataFlowPrivate import DataFlowPrivate
import DataFlowDispatch import DataFlowDispatch
@@ -12,10 +9,3 @@ module Private {
module Public { module Public {
import DataFlowUtil import DataFlowUtil
} }
module CppOldDataFlow implements InputSig {
import Private
import Public
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
}

View File

@@ -2,6 +2,7 @@ private import cpp
private import DataFlowUtil private import DataFlowUtil
private import DataFlowDispatch private import DataFlowDispatch
private import FlowVar private import FlowVar
private import DataFlowImplConsistency
private import codeql.util.Unit private import codeql.util.Unit
/** Gets the callable in which this node occurs. */ /** Gets the callable in which this node occurs. */
@@ -152,11 +153,10 @@ predicate jumpStep(Node n1, Node n2) { none() }
* Thus, `node2` references an object with a field `f` that contains the * Thus, `node2` references an object with a field `f` that contains the
* value of `node1`. * value of `node1`.
*/ */
predicate storeStep(Node node1, ContentSet f, Node node2) { predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
exists(ClassAggregateLiteral aggr, Field field | exists(ClassAggregateLiteral aggr, Field field |
// The following lines requires `node2` to be both an `ExprNode` and a // The following line requires `node2` to be both an `ExprNode` and a
// `PostUpdateNode`, which means it must be an `ObjectInitializerNode`. // `PostUpdateNode`, which means it must be an `ObjectInitializerNode`.
node2 instanceof PostUpdateNode and
node2.asExpr() = aggr and node2.asExpr() = aggr and
f.(FieldContent).getField() = field and f.(FieldContent).getField() = field and
aggr.getAFieldExpr(field) = node1.asExpr() aggr.getAFieldExpr(field) = node1.asExpr()
@@ -167,13 +167,12 @@ predicate storeStep(Node node1, ContentSet f, Node node2) {
node1.asExpr() = a and node1.asExpr() = a and
a.getLValue() = fa a.getLValue() = fa
) and ) and
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = fa.getQualifier() and node2.getPreUpdateNode().asExpr() = fa.getQualifier() and
f.(FieldContent).getField() = fa.getTarget() f.(FieldContent).getField() = fa.getTarget()
) )
or or
exists(ConstructorFieldInit cfi | exists(ConstructorFieldInit cfi |
node2.(PostUpdateNode).getPreUpdateNode().(PreConstructorInitThis).getConstructorFieldInit() = node2.getPreUpdateNode().(PreConstructorInitThis).getConstructorFieldInit() = cfi and
cfi and
f.(FieldContent).getField() = cfi.getTarget() and f.(FieldContent).getField() = cfi.getTarget() and
node1.asExpr() = cfi.getExpr() node1.asExpr() = cfi.getExpr()
) )
@@ -184,7 +183,7 @@ predicate storeStep(Node node1, ContentSet f, Node node2) {
* Thus, `node1` references an object with a field `f` whose value ends up in * Thus, `node1` references an object with a field `f` whose value ends up in
* `node2`. * `node2`.
*/ */
predicate readStep(Node node1, ContentSet f, Node node2) { predicate readStep(Node node1, Content f, Node node2) {
exists(FieldAccess fr | exists(FieldAccess fr |
node1.asExpr() = fr.getQualifier() and node1.asExpr() = fr.getQualifier() and
fr.getTarget() = f.(FieldContent).getField() and fr.getTarget() = f.(FieldContent).getField() and
@@ -196,7 +195,7 @@ predicate readStep(Node node1, ContentSet f, Node node2) {
/** /**
* Holds if values stored inside content `c` are cleared at node `n`. * Holds if values stored inside content `c` are cleared at node `n`.
*/ */
predicate clearsContent(Node n, ContentSet c) { predicate clearsContent(Node n, Content c) {
none() // stub implementation none() // stub implementation
} }
@@ -236,6 +235,12 @@ class CastNode extends Node {
CastNode() { none() } // stub implementation CastNode() { none() } // stub implementation
} }
/**
* Holds if `n` should never be skipped over in the `PathGraph` and in path
* explanations.
*/
predicate neverSkipInPathGraph(Node n) { none() }
class DataFlowCallable = Function; class DataFlowCallable = Function;
class DataFlowExpr = Expr; class DataFlowExpr = Expr;
@@ -260,6 +265,8 @@ class DataFlowCall extends Expr instanceof Call {
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
int accessPathLimit() { result = 5 }
/** /**
* Holds if access paths with `c` at their head always should be tracked at high * Holds if access paths with `c` at their head always should be tracked at high
* precision. This disables adaptive access path precision for such access paths. * precision. This disables adaptive access path precision for such access paths.
@@ -296,6 +303,22 @@ class ContentApprox = Unit;
pragma[inline] pragma[inline]
ContentApprox getContentApprox(Content c) { any() } ContentApprox getContentApprox(Content c) { any() }
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
override predicate argHasPostUpdateExclude(ArgumentNode n) {
// Is the null pointer (or something that's not really a pointer)
exists(n.asExpr().getValue())
or
// Isn't a pointer or is a pointer to const
forall(DerivedType dt | dt = n.asExpr().getActualType() |
dt.getBaseType().isConst()
or
dt.getBaseType() instanceof RoutineType
)
// The above list of cases isn't exhaustive, but it narrows down the
// consistency alerts enough that most of them are interesting.
}
}
/** /**
* Gets an additional term that is added to the `join` and `branch` computations to reflect * Gets an additional term that is added to the `join` and `branch` computations to reflect
* an additional forward or backwards branching factor that is not taken into account * an additional forward or backwards branching factor that is not taken into account

View File

@@ -1,10 +0,0 @@
/**
* Provides C++-specific definitions for use in the taint tracking library.
*/
private import codeql.dataflow.TaintTracking
private import DataFlowImplSpecific
module CppOldTaintTracking implements InputSig<CppOldDataFlow> {
import TaintTrackingUtil
}

View File

@@ -39,7 +39,7 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
* of `c` at sinks and inputs to additional taint steps. * of `c` at sinks and inputs to additional taint steps.
*/ */
bindingset[node] bindingset[node]
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() } predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
/** /**
* Holds if `node` should be a sanitizer in all global taint flow configurations * Holds if `node` should be a sanitizer in all global taint flow configurations

View File

@@ -0,0 +1,74 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
DataFlowInternal::FullStateConfigSig
{
import Config
predicate isBarrier(DataFlow::Node node) {
Config::isBarrier(node) or defaultTaintSanitizer(node)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
Config::isAdditionalFlowStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2)
}
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
Config::allowImplicitRead(node, c)
or
(
Config::isSink(node, _) or
Config::isAdditionalFlowStep(node, _) or
Config::isAdditionalFlowStep(node, _, _, _)
) and
defaultImplicitTaintRead(node, c)
}
}
/**
* Constructs a global taint tracking computation.
*/
module Global<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
private module Config0 implements DataFlowInternal::FullStateConfigSig {
import DataFlowInternal::DefaultState<Config>
import Config
}
private module C implements DataFlowInternal::FullStateConfigSig {
import AddTaintDefaults<Config0>
}
import DataFlowInternal::Impl<C>
}
/** DEPRECATED: Use `Global` instead. */
deprecated module Make<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
import Global<Config>
}
/**
* Constructs a global taint tracking computation using flow state.
*/
module GlobalWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
private module Config0 implements DataFlowInternal::FullStateConfigSig {
import Config
}
private module C implements DataFlowInternal::FullStateConfigSig {
import AddTaintDefaults<Config0>
}
import DataFlowInternal::Impl<C>
}
/** DEPRECATED: Use `GlobalWithState` instead. */
deprecated module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
import GlobalWithState<Config>
}

View File

@@ -26,8 +26,6 @@ import cpp
* global (inter-procedural) data flow analyses. * global (inter-procedural) data flow analyses.
*/ */
module DataFlow { module DataFlow {
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific import semmle.code.cpp.ir.dataflow.internal.DataFlow
private import codeql.dataflow.DataFlow
import DataFlowMake<CppDataFlow>
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1 import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
} }

View File

@@ -23,10 +23,6 @@ import semmle.code.cpp.dataflow.new.DataFlow2
* global (inter-procedural) taint-tracking analyses. * global (inter-procedural) taint-tracking analyses.
*/ */
module TaintTracking { module TaintTracking {
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingParameter::Public import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTracking
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
private import codeql.dataflow.TaintTracking
import TaintFlowMake<CppDataFlow, CppTaintTracking>
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
} }

View File

@@ -368,11 +368,6 @@ class FunctionAccess extends Access, @routineexpr {
/** Gets the accessed function. */ /** Gets the accessed function. */
override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) } override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) }
/**
* Gets the expression generating the function being accessed.
*/
Expr getQualifier() { this.getChild(-1) = result }
/** Gets a textual representation of this function access. */ /** Gets a textual representation of this function access. */
override string toString() { override string toString() {
if exists(this.getTarget()) if exists(this.getTarget())

View File

@@ -152,19 +152,7 @@ class Expr extends StmtParent, @expr {
else result = this.getValue() else result = this.getValue()
} }
/** /** Holds if this expression has a value that can be determined at compile time. */
* Holds if this expression has a value that can be determined at compile time.
*
* An expression has a value that can be determined at compile time when:
* - it is a compile-time constant, e.g., a literal value or the result of a constexpr
* compile-time constant;
* - it is an address of a (member) function, an address of a constexpr variable
* initialized to a constant address, or an address of an lvalue, or any of the
* previous with a constant value added to or subtracted from the address;
* - it is a reference to a (member) function, a reference to a constexpr variable
* initialized to a constant address, or a reference to an lvalue;
* - it is a non-template parameter of a uninstantiated template.
*/
cached cached
predicate isConstant() { predicate isConstant() {
valuebind(_, underlyingElement(this)) valuebind(_, underlyingElement(this))
@@ -932,30 +920,45 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
Expr getExtent() { result = this.getChild(2) } Expr getExtent() { result = this.getChild(2) }
} }
private class TDeleteOrDeleteArrayExpr = @delete_expr or @delete_array_expr;
/** /**
* A C++ `delete` or `delete[]` expression. * A C++ `delete` (non-array) expression.
* ```
* delete ptr;
* ```
*/ */
class DeleteOrDeleteArrayExpr extends Expr, TDeleteOrDeleteArrayExpr { class DeleteExpr extends Expr, @delete_expr {
override string toString() { result = "delete" }
override string getAPrimaryQlClass() { result = "DeleteExpr" }
override int getPrecedence() { result = 16 } override int getPrecedence() { result = 16 }
/**
* Gets the compile-time type of the object being deleted.
*/
Type getDeletedObjectType() {
result =
this.getExpr()
.getFullyConverted()
.getType()
.stripTopLevelSpecifiers()
.(PointerType)
.getBaseType()
}
/** /**
* Gets the call to a destructor that occurs prior to the object's memory being deallocated, if any. * Gets the call to a destructor that occurs prior to the object's memory being deallocated, if any.
*
* In the case of `delete[]` at runtime, the destructor will be called once for each element in the array, but the
* destructor call only exists once in the AST.
*/ */
DestructorCall getDestructorCall() { result = this.getChild(1) } DestructorCall getDestructorCall() { result = this.getChild(1) }
/** /**
* Gets the destructor to be called to destroy the object or array, if any. * Gets the destructor to be called to destroy the object, if any.
*/ */
Destructor getDestructor() { result = this.getDestructorCall().getTarget() } Destructor getDestructor() { result = this.getDestructorCall().getTarget() }
/** /**
* Gets the `operator delete` or `operator delete[]` that deallocates storage. * Gets the `operator delete` that deallocates storage. Does not hold
* Does not hold if the type being destroyed has a virtual destructor. In that case, the * if the type being destroyed has a virtual destructor. In that case, the
* `operator delete` that will be called is determined at runtime based on the * `operator delete` that will be called is determined at runtime based on the
* dynamic type of the object. * dynamic type of the object.
*/ */
@@ -963,19 +966,6 @@ class DeleteOrDeleteArrayExpr extends Expr, TDeleteOrDeleteArrayExpr {
expr_deallocator(underlyingElement(this), unresolveElement(result), _) expr_deallocator(underlyingElement(this), unresolveElement(result), _)
} }
/**
* DEPRECATED: use `getDeallocatorCall` instead.
*/
deprecated FunctionCall getAllocatorCall() { result = this.getChild(0) }
/**
* Gets the call to a non-default `operator delete`/`delete[]` that deallocates storage, if any.
*
* This will only be present when the type being deleted has a custom `operator delete` and
* does not have a virtual destructor.
*/
FunctionCall getDeallocatorCall() { result = this.getChild(0) }
/** /**
* Holds if the deallocation function expects a size argument. * Holds if the deallocation function expects a size argument.
*/ */
@@ -997,38 +987,16 @@ class DeleteOrDeleteArrayExpr extends Expr, TDeleteOrDeleteArrayExpr {
} }
/** /**
* Gets the object or array being deleted. * Gets the call to a non-default `operator delete` that deallocates storage, if any.
*
* This will only be present when the type being deleted has a custom `operator delete`.
*/ */
Expr getExpr() { FunctionCall getAllocatorCall() { result = this.getChild(0) }
// If there is a destructor call, the object being deleted is the qualifier
// otherwise it is the third child.
result = this.getChild(3) or result = this.getDestructorCall().getQualifier()
}
}
/**
* A C++ `delete` (non-array) expression.
* ```
* delete ptr;
* ```
*/
class DeleteExpr extends DeleteOrDeleteArrayExpr, @delete_expr {
override string toString() { result = "delete" }
override string getAPrimaryQlClass() { result = "DeleteExpr" }
/** /**
* Gets the compile-time type of the object being deleted. * Gets the object being deleted.
*/ */
Type getDeletedObjectType() { Expr getExpr() { result = this.getChild(3) or result = this.getChild(1).getChild(-1) }
result =
this.getExpr()
.getFullyConverted()
.getType()
.stripTopLevelSpecifiers()
.(PointerType)
.getBaseType()
}
} }
/** /**
@@ -1037,11 +1005,13 @@ class DeleteExpr extends DeleteOrDeleteArrayExpr, @delete_expr {
* delete[] arr; * delete[] arr;
* ``` * ```
*/ */
class DeleteArrayExpr extends DeleteOrDeleteArrayExpr, @delete_array_expr { class DeleteArrayExpr extends Expr, @delete_array_expr {
override string toString() { result = "delete[]" } override string toString() { result = "delete[]" }
override string getAPrimaryQlClass() { result = "DeleteArrayExpr" } override string getAPrimaryQlClass() { result = "DeleteArrayExpr" }
override int getPrecedence() { result = 16 }
/** /**
* Gets the element type of the array being deleted. * Gets the element type of the array being deleted.
*/ */
@@ -1054,6 +1024,58 @@ class DeleteArrayExpr extends DeleteOrDeleteArrayExpr, @delete_array_expr {
.(PointerType) .(PointerType)
.getBaseType() .getBaseType()
} }
/**
* Gets the call to a destructor that occurs prior to the array's memory being deallocated, if any.
*
* At runtime, the destructor will be called once for each element in the array, but the
* destructor call only exists once in the AST.
*/
DestructorCall getDestructorCall() { result = this.getChild(1) }
/**
* Gets the destructor to be called to destroy each element in the array, if any.
*/
Destructor getDestructor() { result = this.getDestructorCall().getTarget() }
/**
* Gets the `operator delete[]` that deallocates storage.
*/
Function getDeallocator() {
expr_deallocator(underlyingElement(this), unresolveElement(result), _)
}
/**
* Holds if the deallocation function expects a size argument.
*/
predicate hasSizedDeallocation() {
exists(int form |
expr_deallocator(underlyingElement(this), _, form) and
form.bitAnd(1) != 0 // Bit zero is the "size" bit
)
}
/**
* Holds if the deallocation function expects an alignment argument.
*/
predicate hasAlignedDeallocation() {
exists(int form |
expr_deallocator(underlyingElement(this), _, form) and
form.bitAnd(2) != 0 // Bit one is the "alignment" bit
)
}
/**
* Gets the call to a non-default `operator delete` that deallocates storage, if any.
*
* This will only be present when the type being deleted has a custom `operator delete`.
*/
FunctionCall getAllocatorCall() { result = this.getChild(0) }
/**
* Gets the array being deleted.
*/
Expr getExpr() { result = this.getChild(3) or result = this.getChild(1).getChild(-1) }
} }
/** /**

View File

@@ -4,8 +4,8 @@
* This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
* databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends * uses, however, it is better to write a query that imports `PrintIR.qll`, extends
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
* to dump. * dump.
*/ */
import implementation.aliased_ssa.PrintIR import implementation.aliased_ssa.PrintIR

View File

@@ -22,8 +22,6 @@
import cpp import cpp
module DataFlow { module DataFlow {
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific import semmle.code.cpp.ir.dataflow.internal.DataFlow
private import codeql.dataflow.DataFlow
import DataFlowMake<CppDataFlow>
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1 import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
} }

View File

@@ -19,10 +19,6 @@ import semmle.code.cpp.ir.dataflow.DataFlow
import semmle.code.cpp.ir.dataflow.DataFlow2 import semmle.code.cpp.ir.dataflow.DataFlow2
module TaintTracking { module TaintTracking {
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingParameter::Public import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTracking
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
private import codeql.dataflow.TaintTracking
import TaintFlowMake<CppDataFlow, CppTaintTracking>
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
} }

View File

@@ -0,0 +1,412 @@
/**
* Provides an implementation of global (interprocedural) data flow. This file
* re-exports the local (intraprocedural) data flow analysis from
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
* through the `Global` and `GlobalWithState` modules.
*/
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
import DataFlowImplCommonPublic
private import DataFlowImpl
/** An input configuration for data flow. */
signature module ConfigSig {
/**
* Holds if `source` is a relevant data flow source.
*/
predicate isSource(Node source);
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(Node sink);
/**
* Holds if data flow through `node` is prohibited. This completely removes
* `node` from the data flow graph.
*/
default predicate isBarrier(Node node) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrierIn(Node node) { none() }
/** Holds if data flow out of `node` is prohibited. */
default predicate isBarrierOut(Node node) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
default predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be
* taken at `node`.
*/
default predicate allowImplicitRead(Node node, ContentSet c) { none() }
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
default int fieldFlowBranchLimit() { result = 2 }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*
* These features are generally not relevant for typical end-to-end data flow
* queries, but should only be used for constructing paths that need to
* somehow be pluggable in another path context.
*/
default FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `flowPath`. */
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `flowPath`. */
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (as it is in a `path-problem` query).
*/
default predicate includeHiddenNodes() { none() }
}
/** An input configuration for data flow using flow state. */
signature module StateConfigSig {
bindingset[this]
class FlowState;
/**
* Holds if `source` is a relevant data flow source with the given initial
* `state`.
*/
predicate isSource(Node source, FlowState state);
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node sink, FlowState state);
/**
* Holds if data flow through `node` is prohibited. This completely removes
* `node` from the data flow graph.
*/
default predicate isBarrier(Node node) { none() }
/**
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state);
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrierIn(Node node) { none() }
/** Holds if data flow out of `node` is prohibited. */
default predicate isBarrierOut(Node node) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
default predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2);
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be
* taken at `node`.
*/
default predicate allowImplicitRead(Node node, ContentSet c) { none() }
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
default int fieldFlowBranchLimit() { result = 2 }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*
* These features are generally not relevant for typical end-to-end data flow
* queries, but should only be used for constructing paths that need to
* somehow be pluggable in another path context.
*/
default FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `flowPath`. */
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `flowPath`. */
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (as it is in a `path-problem` query).
*/
default predicate includeHiddenNodes() { none() }
}
/**
* Gets the exploration limit for `partialFlow` and `partialFlowRev`
* measured in approximate number of interprocedural steps.
*/
signature int explorationLimitSig();
/**
* The output of a global data flow computation.
*/
signature module GlobalFlowSig {
/**
* A `Node` augmented with a call context (except for sinks) and an access path.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
class PathNode;
/**
* Holds if data can flow from `source` to `sink`.
*
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate flowPath(PathNode source, PathNode sink);
/**
* Holds if data can flow from `source` to `sink`.
*/
predicate flow(Node source, Node sink);
/**
* Holds if data can flow from some source to `sink`.
*/
predicate flowTo(Node sink);
/**
* Holds if data can flow from some source to `sink`.
*/
predicate flowToExpr(DataFlowExpr sink);
}
/**
* Constructs a global data flow computation.
*/
module Global<ConfigSig Config> implements GlobalFlowSig {
private module C implements FullStateConfigSig {
import DefaultState<Config>
import Config
}
import Impl<C>
}
/** DEPRECATED: Use `Global` instead. */
deprecated module Make<ConfigSig Config> implements GlobalFlowSig {
import Global<Config>
}
/**
* Constructs a global data flow computation using flow state.
*/
module GlobalWithState<StateConfigSig Config> implements GlobalFlowSig {
private module C implements FullStateConfigSig {
import Config
}
import Impl<C>
}
/** DEPRECATED: Use `GlobalWithState` instead. */
deprecated module MakeWithState<StateConfigSig Config> implements GlobalFlowSig {
import GlobalWithState<Config>
}
signature class PathNodeSig {
/** Gets a textual representation of this element. */
string toString();
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
);
/** Gets the underlying `Node`. */
Node getNode();
}
signature module PathGraphSig<PathNodeSig PathNode> {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
predicate edges(PathNode a, PathNode b);
/** Holds if `n` is a node in the graph of data flow path explanations. */
predicate nodes(PathNode n, string key, string val);
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out);
}
/**
* Constructs a `PathGraph` from two `PathGraph`s by disjoint union.
*/
module MergePathGraph<
PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig<PathNode1> Graph1,
PathGraphSig<PathNode2> Graph2>
{
private newtype TPathNode =
TPathNode1(PathNode1 p) or
TPathNode2(PathNode2 p)
/** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */
class PathNode extends TPathNode {
/** Gets this as a projection on the first given `PathGraph`. */
PathNode1 asPathNode1() { this = TPathNode1(result) }
/** Gets this as a projection on the second given `PathGraph`. */
PathNode2 asPathNode2() { this = TPathNode2(result) }
/** Gets a textual representation of this element. */
string toString() {
result = this.asPathNode1().toString() or
result = this.asPathNode2().toString()
}
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or
this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
Node getNode() {
result = this.asPathNode1().getNode() or
result = this.asPathNode2().getNode()
}
}
/**
* Provides the query predicates needed to include a graph in a path-problem query.
*/
module PathGraph implements PathGraphSig<PathNode> {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) {
Graph1::edges(a.asPathNode1(), b.asPathNode1()) or
Graph2::edges(a.asPathNode2(), b.asPathNode2())
}
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
Graph1::nodes(n.asPathNode1(), key, val) or
Graph2::nodes(n.asPathNode2(), key, val)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or
Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2())
}
}
}
/**
* Constructs a `PathGraph` from three `PathGraph`s by disjoint union.
*/
module MergePathGraph3<
PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3,
PathGraphSig<PathNode1> Graph1, PathGraphSig<PathNode2> Graph2, PathGraphSig<PathNode3> Graph3>
{
private module MergedInner = MergePathGraph<PathNode1, PathNode2, Graph1, Graph2>;
private module Merged =
MergePathGraph<MergedInner::PathNode, PathNode3, MergedInner::PathGraph, Graph3>;
/** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */
class PathNode instanceof Merged::PathNode {
/** Gets this as a projection on the first given `PathGraph`. */
PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() }
/** Gets this as a projection on the second given `PathGraph`. */
PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() }
/** Gets this as a projection on the third given `PathGraph`. */
PathNode3 asPathNode3() { result = super.asPathNode2() }
/** Gets a textual representation of this element. */
string toString() { result = super.toString() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
Node getNode() { result = super.getNode() }
}
/**
* Provides the query predicates needed to include a graph in a path-problem query.
*/
module PathGraph = Merged::PathGraph;
}

View File

@@ -9,7 +9,7 @@ private import DataFlowImplCommon as DataFlowImplCommon
* Gets a function that might be called by `call`. * Gets a function that might be called by `call`.
*/ */
cached cached
DataFlowCallable viableCallable(DataFlowCall call) { Function viableCallable(CallInstruction call) {
DataFlowImplCommon::forceCachingInSameStage() and DataFlowImplCommon::forceCachingInSameStage() and
result = call.getStaticCallTarget() result = call.getStaticCallTarget()
or or
@@ -235,7 +235,7 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
* Holds if the set of viable implementations that can be called by `call` * Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. * might be improved by knowing the call context.
*/ */
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable f) { predicate mayBenefitFromCallContext(CallInstruction call, Function f) {
mayBenefitFromCallContext(call, f, _) mayBenefitFromCallContext(call, f, _)
} }
@@ -259,7 +259,7 @@ private predicate mayBenefitFromCallContext(
* Gets a viable dispatch target of `call` in the context `ctx`. This is * Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference. * restricted to those `call`s for which a context might make a difference.
*/ */
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
result = viableCallable(call) and result = viableCallable(call) and
exists(int i, Function f | exists(int i, Function f |
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and

View File

@@ -276,8 +276,6 @@ private module Config implements FullStateConfigSig {
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
} }
predicate isSink(Node sink) { none() }
predicate isSink(Node sink, FlowState state) { predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state)) getConfig(state).isSink(sink, getState(state))
or or
@@ -315,8 +313,6 @@ private module Config implements FullStateConfigSig {
any(Configuration config).allowImplicitRead(node, c) any(Configuration config).allowImplicitRead(node, c)
} }
predicate neverSkip(Node node) { none() }
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) } int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() } FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }

View File

@@ -276,8 +276,6 @@ private module Config implements FullStateConfigSig {
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
} }
predicate isSink(Node sink) { none() }
predicate isSink(Node sink, FlowState state) { predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state)) getConfig(state).isSink(sink, getState(state))
or or
@@ -315,8 +313,6 @@ private module Config implements FullStateConfigSig {
any(Configuration config).allowImplicitRead(node, c) any(Configuration config).allowImplicitRead(node, c)
} }
predicate neverSkip(Node node) { none() }
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) } int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() } FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }

View File

@@ -276,8 +276,6 @@ private module Config implements FullStateConfigSig {
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
} }
predicate isSink(Node sink) { none() }
predicate isSink(Node sink, FlowState state) { predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state)) getConfig(state).isSink(sink, getState(state))
or or
@@ -315,8 +313,6 @@ private module Config implements FullStateConfigSig {
any(Configuration config).allowImplicitRead(node, c) any(Configuration config).allowImplicitRead(node, c)
} }
predicate neverSkip(Node node) { none() }
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) } int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() } FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }

View File

@@ -276,8 +276,6 @@ private module Config implements FullStateConfigSig {
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
} }
predicate isSink(Node sink) { none() }
predicate isSink(Node sink, FlowState state) { predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state)) getConfig(state).isSink(sink, getState(state))
or or
@@ -315,8 +313,6 @@ private module Config implements FullStateConfigSig {
any(Configuration config).allowImplicitRead(node, c) any(Configuration config).allowImplicitRead(node, c)
} }
predicate neverSkip(Node node) { none() }
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) } int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() } FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }

View File

@@ -3,17 +3,297 @@
* data-flow classes and predicates. * data-flow classes and predicates.
*/ */
private import cpp private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific private import DataFlowImplSpecific::Public
private import TaintTrackingImplSpecific private import tainttracking1.TaintTrackingParameter::Private
private import codeql.dataflow.internal.DataFlowImplConsistency private import tainttracking1.TaintTrackingParameter::Public
private module Input implements InputSig<CppDataFlow> { module Consistency {
predicate argHasPostUpdateExclude(Private::ArgumentNode n) { private newtype TConsistencyConfiguration = MkConsistencyConfiguration()
// The rules for whether an IR argument gets a post-update node are too
// complex to model here. /** A class for configuring the consistency queries. */
any() class ConsistencyConfiguration extends TConsistencyConfiguration {
string toString() { none() }
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
predicate uniqueEnclosingCallableExclude(Node n) { none() }
/** Holds if `call` should be excluded from the consistency test `uniqueCallEnclosingCallable`. */
predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) { none() }
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
predicate uniqueNodeLocationExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
predicate missingLocationExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
predicate postWithInFlowExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
predicate reverseReadExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `postHasUniquePre`. */
predicate postHasUniquePreExclude(PostUpdateNode n) { none() }
/** Holds if `n` should be excluded from the consistency test `uniquePostUpdate`. */
predicate uniquePostUpdateExclude(Node n) { none() }
/** Holds if `(call, ctx)` should be excluded from the consistency test `viableImplInCallContextTooLargeExclude`. */
predicate viableImplInCallContextTooLargeExclude(
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
) {
none()
}
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
none()
}
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
none()
}
/** Holds if `n` should be excluded from the consistency test `identityLocalStep`. */
predicate identityLocalStepExclude(Node n) { none() }
}
private class RelevantNode extends Node {
RelevantNode() {
this instanceof ArgumentNode or
this instanceof ParameterNode or
this instanceof ReturnNode or
this = getAnOutNode(_, _) or
simpleLocalFlowStep(this, _) or
simpleLocalFlowStep(_, this) or
jumpStep(this, _) or
jumpStep(_, this) or
storeStep(this, _, _) or
storeStep(_, _, this) or
readStep(this, _, _) or
readStep(_, _, this) or
defaultAdditionalTaintStep(this, _) or
defaultAdditionalTaintStep(_, this)
}
}
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
msg = "Node should have one enclosing callable but has " + c + "."
)
}
query predicate uniqueCallEnclosingCallable(DataFlowCall call, string msg) {
exists(int c |
c = count(call.getEnclosingCallable()) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueCallEnclosingCallableExclude(call) and
msg = "Call should have one enclosing callable but has " + c + "."
)
}
query predicate uniqueType(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(getNodeType(n)) and
c != 1 and
msg = "Node should have one type but has " + c + "."
)
}
query predicate uniqueNodeLocation(Node n, string msg) {
exists(int c |
c =
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
msg = "Node should have one location but has " + c + "."
)
}
query predicate missingLocation(string msg) {
exists(int c |
c =
strictcount(Node n |
not n.hasLocationInfo(_, _, _, _, _) and
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
) and
msg = "Nodes without location: " + c
)
}
query predicate uniqueNodeToString(Node n, string msg) {
exists(int c |
c = count(n.toString()) and
c != 1 and
msg = "Node should have one toString but has " + c + "."
)
}
query predicate missingToString(string msg) {
exists(int c |
c = strictcount(Node n | not exists(n.toString())) and
msg = "Nodes without toString: " + c
)
}
query predicate parameterCallable(ParameterNode p, string msg) {
exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Local flow step does not preserve enclosing callable."
}
query predicate readStepIsLocal(Node n1, Node n2, string msg) {
readStep(n1, _, n2) and
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Read step does not preserve enclosing callable."
}
query predicate storeStepIsLocal(Node n1, Node n2, string msg) {
storeStep(n1, _, n2) and
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
msg = "Store step does not preserve enclosing callable."
}
private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
t = typeRepr() and
not compatibleTypes(t, t) and
msg = "Type compatibility predicate is not reflexive."
}
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
c = nodeGetEnclosingCallable(n) and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
}
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
(
n = getAnOutNode(call, _) and
msg = "OutNode and call does not share enclosing callable."
or
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
// it is impossible for a result of `getPreUpdateNode` to be an
// instance of `PostUpdateNode`.
private Node getPre(PostUpdateNode n) {
result = n.getPreUpdateNode()
or
none()
}
query predicate postIsNotPre(PostUpdateNode n, string msg) {
getPre(n) = n and
msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
not any(ConsistencyConfiguration conf).postHasUniquePreExclude(n) and
exists(int c |
c = count(n.getPreUpdateNode()) and
c != 1 and
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
)
}
query predicate uniquePostUpdate(Node n, string msg) {
not any(ConsistencyConfiguration conf).uniquePostUpdateExclude(n) and
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
msg = "Node has multiple PostUpdateNodes."
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
query predicate reverseRead(Node n, string msg) {
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
msg = "Origin of readStep is missing a PostUpdateNode."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not any(ConsistencyConfiguration c).argHasPostUpdateExclude(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
// This predicate helps the compiler forget that in some languages
// it is impossible for a `PostUpdateNode` to be the target of
// `simpleLocalFlowStep`.
private predicate isPostUpdateNode(Node n) { n instanceof PostUpdateNode or none() }
query predicate postWithInFlow(Node n, string msg) {
isPostUpdateNode(n) and
not clearsContent(n, _) and
simpleLocalFlowStep(_, n) and
not any(ConsistencyConfiguration c).postWithInFlowExclude(n) and
msg = "PostUpdateNode should not be the target of local flow."
}
query predicate viableImplInCallContextTooLarge(
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
) {
callable = viableImplInCallContext(call, ctx) and
not callable = viableCallable(call) and
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
}
query predicate uniqueParameterNodeAtPosition(
DataFlowCallable c, ParameterPosition pos, Node p, string msg
) {
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
isParameterNode(p, c, pos) and
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
msg = "Parameters with overlapping positions."
}
query predicate uniqueParameterNodePosition(
DataFlowCallable c, ParameterPosition pos, Node p, string msg
) {
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
isParameterNode(p, c, pos) and
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
msg = "Parameter node with multiple positions."
}
query predicate uniqueContentApprox(Content c, string msg) {
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
msg = "Non-unique content approximation."
}
query predicate identityLocalStep(Node n, string msg) {
simpleLocalFlowStep(n, n) and
not any(ConsistencyConfiguration c).identityLocalStepExclude(n) and
msg = "Node steps to itself"
} }
} }
module Consistency = MakeConsistency<CppDataFlow, CppTaintTracking, Input>;

View File

@@ -1,9 +1,6 @@
/** /**
* Provides IR-specific definitions for use in the data flow library. * Provides IR-specific definitions for use in the data flow library.
*/ */
private import codeql.dataflow.DataFlow
module Private { module Private {
import DataFlowPrivate import DataFlowPrivate
import DataFlowDispatch import DataFlowDispatch
@@ -12,10 +9,3 @@ module Private {
module Public { module Public {
import DataFlowUtil import DataFlowUtil
} }
module CppDataFlow implements InputSig {
import Private
import Public
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
}

View File

@@ -2,6 +2,7 @@ private import cpp as Cpp
private import DataFlowUtil private import DataFlowUtil
private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.IR
private import DataFlowDispatch private import DataFlowDispatch
private import DataFlowImplConsistency
private import semmle.code.cpp.ir.internal.IRCppLanguage private import semmle.code.cpp.ir.internal.IRCppLanguage
private import SsaInternals as Ssa private import SsaInternals as Ssa
private import DataFlowImplCommon as DataFlowImplCommon private import DataFlowImplCommon as DataFlowImplCommon
@@ -192,8 +193,7 @@ private class SingleUseOperandNode0 extends OperandNode0, TSingleUseOperandNode0
SingleUseOperandNode0() { this = TSingleUseOperandNode0(op) } SingleUseOperandNode0() { this = TSingleUseOperandNode0(op) }
} }
private module IndirectOperands { /**
/**
* INTERNAL: Do not use. * INTERNAL: Do not use.
* *
* A node that represents the indirect value of an operand in the IR * A node that represents the indirect value of an operand in the IR
@@ -202,40 +202,38 @@ private module IndirectOperands {
* Note: Unlike `RawIndirectOperand`, a value of type `IndirectOperand` may * Note: Unlike `RawIndirectOperand`, a value of type `IndirectOperand` may
* be an `OperandNode`. * be an `OperandNode`.
*/ */
abstract class IndirectOperand extends Node { class IndirectOperand extends Node {
/** Gets the underlying operand and the underlying indirection index. */
abstract predicate hasOperandAndIndirectionIndex(Operand operand, int indirectionIndex);
}
private class IndirectOperandFromRaw extends IndirectOperand instanceof RawIndirectOperand {
override predicate hasOperandAndIndirectionIndex(Operand operand, int indirectionIndex) {
operand = RawIndirectOperand.super.getOperand() and
indirectionIndex = RawIndirectOperand.super.getIndirectionIndex()
}
}
private class IndirectOperandFromIRRepr extends IndirectOperand {
Operand operand; Operand operand;
int indirectionIndex; int indirectionIndex;
IndirectOperandFromIRRepr() { IndirectOperand() {
exists(Operand repr, int indirectionIndexRepr | this.(RawIndirectOperand).getOperand() = operand and
Ssa::hasIRRepresentationOfIndirectOperand(operand, indirectionIndex, repr, this.(RawIndirectOperand).getIndirectionIndex() = indirectionIndex
indirectionIndexRepr) and or
nodeHasOperand(this, repr, indirectionIndexRepr) nodeHasOperand(this, Ssa::getIRRepresentationOfIndirectOperand(operand, indirectionIndex),
) indirectionIndex - 1)
} }
override predicate hasOperandAndIndirectionIndex(Operand op, int index) { /** Gets the underlying operand. */
op = operand and index = indirectionIndex Operand getOperand() { result = operand }
}
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
/**
* Holds if this `IndirectOperand` is represented directly in the IR instead of
* a `RawIndirectionOperand` with operand `op` and indirection index `index`.
*/
predicate isIRRepresentationOf(Operand op, int index) {
this instanceof OperandNode and
(
op = operand and
index = indirectionIndex
)
} }
} }
import IndirectOperands /**
private module IndirectInstructions {
/**
* INTERNAL: Do not use. * INTERNAL: Do not use.
* *
* A node that represents the indirect value of an instruction in the IR * A node that represents the indirect value of an instruction in the IR
@@ -244,39 +242,37 @@ private module IndirectInstructions {
* Note: Unlike `RawIndirectInstruction`, a value of type `IndirectInstruction` may * Note: Unlike `RawIndirectInstruction`, a value of type `IndirectInstruction` may
* be an `InstructionNode`. * be an `InstructionNode`.
*/ */
abstract class IndirectInstruction extends Node { class IndirectInstruction extends Node {
/** Gets the underlying operand and the underlying indirection index. */
abstract predicate hasInstructionAndIndirectionIndex(Instruction instr, int index);
}
private class IndirectInstructionFromRaw extends IndirectInstruction instanceof RawIndirectInstruction
{
override predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
instr = RawIndirectInstruction.super.getInstruction() and
index = RawIndirectInstruction.super.getIndirectionIndex()
}
}
private class IndirectInstructionFromIRRepr extends IndirectInstruction {
Instruction instr; Instruction instr;
int indirectionIndex; int indirectionIndex;
IndirectInstructionFromIRRepr() { IndirectInstruction() {
exists(Instruction repr, int indirectionIndexRepr | this.(RawIndirectInstruction).getInstruction() = instr and
Ssa::hasIRRepresentationOfIndirectInstruction(instr, indirectionIndex, repr, this.(RawIndirectInstruction).getIndirectionIndex() = indirectionIndex
indirectionIndexRepr) and or
nodeHasInstruction(this, repr, indirectionIndexRepr) nodeHasInstruction(this, Ssa::getIRRepresentationOfIndirectInstruction(instr, indirectionIndex),
indirectionIndex - 1)
}
/** Gets the underlying instruction. */
Instruction getInstruction() { result = instr }
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
/**
* Holds if this `IndirectInstruction` is represented directly in the IR instead of
* a `RawIndirectionInstruction` with instruction `i` and indirection index `index`.
*/
predicate isIRRepresentationOf(Instruction i, int index) {
this instanceof InstructionNode and
(
i = instr and
index = indirectionIndex
) )
} }
override predicate hasInstructionAndIndirectionIndex(Instruction i, int index) {
i = instr and index = indirectionIndex
}
}
} }
import IndirectInstructions
/** Gets the callable in which this node occurs. */ /** Gets the callable in which this node occurs. */
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() } DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
@@ -322,11 +318,9 @@ private class PrimaryArgumentNode extends ArgumentNode, OperandNode {
private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode { private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode {
override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) { override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) {
exists(int indirectionIndex |
pos = TIndirectionPosition(argumentIndex, pragma[only_bind_into](indirectionIndex)) and
this.getCallInstruction() = dfCall and this.getCallInstruction() = dfCall and
super.hasAddressOperandAndIndirectionIndex(_, pragma[only_bind_into](indirectionIndex)) pos.(IndirectionPosition).getArgumentIndex() = this.getArgumentIndex() and
) pos.(IndirectionPosition).getIndirectionIndex() = super.getIndirectionIndex()
} }
} }
@@ -654,16 +648,13 @@ predicate jumpStep(Node n1, Node n2) {
* Holds if data can flow from `node1` to `node2` via an assignment to `f`. * Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the * Thus, `node2` references an object with a field `f` that contains the
* value of `node1`. * value of `node1`.
*
* The boolean `certain` is true if the destination address does not involve
* any pointer arithmetic, and false otherwise.
*/ */
predicate storeStepImpl(Node node1, Content c, PostFieldUpdateNode node2, boolean certain) { predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) {
exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store | exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store |
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
node2.getIndirectionIndex() = 1 and node2.getIndirectionIndex() = 1 and
numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(), numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(),
numberOfLoads, certain) numberOfLoads)
| |
exists(FieldContent fc | fc = c | exists(FieldContent fc | fc = c |
fc.getField() = node2.getUpdatedField() and fc.getField() = node2.getUpdatedField() and
@@ -677,32 +668,21 @@ predicate storeStepImpl(Node node1, Content c, PostFieldUpdateNode node2, boolea
) )
} }
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*/
predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) }
/** /**
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
* operations and exactly `n` `LoadInstruction` operations. * operations and exactly `n` `LoadInstruction` operations.
*/ */
private predicate numberOfLoadsFromOperandRec( private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand operandTo, int ind) {
Operand operandFrom, Operand operandTo, int ind, boolean certain exists(Instruction load | Ssa::isDereference(load, operandFrom) |
) { operandTo = operandFrom and ind = 0
exists(Instruction load | Ssa::isDereference(load, operandFrom, _) |
operandTo = operandFrom and ind = 0 and certain = true
or or
numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1, certain) numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1)
) )
or or
exists(Operand op, Instruction instr, boolean isPointerArith, boolean certain0 | exists(Operand op, Instruction instr |
instr = op.getDef() and instr = op.getDef() and
conversionFlow(operandFrom, instr, isPointerArith, _) and conversionFlow(operandFrom, instr, _, _) and
numberOfLoadsFromOperand(op, operandTo, ind, certain0) numberOfLoadsFromOperand(op, operandTo, ind)
|
if isPointerArith = true then certain = false else certain = certain0
) )
} }
@@ -710,16 +690,13 @@ private predicate numberOfLoadsFromOperandRec(
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
* operations and exactly `n` `LoadInstruction` operations. * operations and exactly `n` `LoadInstruction` operations.
*/ */
private predicate numberOfLoadsFromOperand( private predicate numberOfLoadsFromOperand(Operand operandFrom, Operand operandTo, int n) {
Operand operandFrom, Operand operandTo, int n, boolean certain numberOfLoadsFromOperandRec(operandFrom, operandTo, n)
) {
numberOfLoadsFromOperandRec(operandFrom, operandTo, n, certain)
or or
not Ssa::isDereference(_, operandFrom, _) and not Ssa::isDereference(_, operandFrom) and
not conversionFlow(operandFrom, _, _, _) and not conversionFlow(operandFrom, _, _, _) and
operandFrom = operandTo and operandFrom = operandTo and
n = 0 and n = 0
certain = true
} }
// Needed to join on both an operand and an index at the same time. // Needed to join on both an operand and an index at the same time.
@@ -743,13 +720,13 @@ predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex)
* Thus, `node1` references an object with a field `f` whose value ends up in * Thus, `node1` references an object with a field `f` whose value ends up in
* `node2`. * `node2`.
*/ */
predicate readStep(Node node1, ContentSet c, Node node2) { predicate readStep(Node node1, Content c, Node node2) {
exists(FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2 | exists(FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2 |
nodeHasOperand(node2, operand, indirectionIndex2) and nodeHasOperand(node2, operand, indirectionIndex2) and
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct // The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
// in `storeStep`. // in `storeStep`.
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) numberOfLoadsFromOperand(fa1, operand, numberOfLoads)
| |
exists(FieldContent fc | fc = c | exists(FieldContent fc | fc = c |
fc.getField() = fa1.getField() and fc.getField() = fa1.getField() and
@@ -766,34 +743,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
/** /**
* Holds if values stored inside content `c` are cleared at node `n`. * Holds if values stored inside content `c` are cleared at node `n`.
*/ */
predicate clearsContent(Node n, ContentSet c) { predicate clearsContent(Node n, Content c) {
n = none() // stub implementation
any(PostUpdateNode pun, Content d | d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) | pun)
.getPreUpdateNode() and
(
// The crement operations and pointer addition and subtraction self-assign. We do not
// want to clear the contents if it is indirectly pointed at by any of these operations,
// as part of the contents might still be accessible afterwards. If there is no such
// indirection clearing the contents is safe.
not exists(Operand op, Cpp::Operation p |
n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and
(
p instanceof Cpp::AssignPointerAddExpr or
p instanceof Cpp::AssignPointerSubExpr or
p instanceof Cpp::CrementOperation
)
|
p.getAnOperand() = op.getUse().getAst()
)
or
forex(PostUpdateNode pun, Content d |
pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and
storeStepImpl(_, d, pun, true) and
pun.getPreUpdateNode() = n
|
c.(Content).getIndirectionIndex() = d.getIndirectionIndex()
)
)
} }
/** /**
@@ -832,6 +783,12 @@ class CastNode extends Node {
CastNode() { none() } // stub implementation CastNode() { none() } // stub implementation
} }
/**
* Holds if `n` should never be skipped over in the `PathGraph` and in path
* explanations.
*/
predicate neverSkipInPathGraph(Node n) { none() }
/** /**
* A function that may contain code or a variable that may contain itself. When * A function that may contain code or a variable that may contain itself. When
* flow crosses from one _enclosing callable_ to another, the interprocedural * flow crosses from one _enclosing callable_ to another, the interprocedural
@@ -846,76 +803,12 @@ class DataFlowType = Type;
/** A function call relevant for data flow. */ /** A function call relevant for data flow. */
class DataFlowCall extends CallInstruction { class DataFlowCall extends CallInstruction {
DataFlowCallable getEnclosingCallable() { result = this.getEnclosingFunction() } Function getEnclosingCallable() { result = this.getEnclosingFunction() }
} }
module IsUnreachableInCall { predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.controlflow.IRGuards as G
private class ConstantIntegralTypeArgumentNode extends PrimaryArgumentNode { int accessPathLimit() { result = 5 }
int value;
ConstantIntegralTypeArgumentNode() {
value = op.getDef().(IntegerConstantInstruction).getValue().toInt()
}
int getValue() { result = value }
}
pragma[nomagic]
private predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
any(G::IRGuardCondition guard).ensuresEq(left, right, k, block, areEqual)
}
pragma[nomagic]
private predicate ensuresLt(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
any(G::IRGuardCondition guard).ensuresLt(left, right, k, block, areEqual)
}
predicate isUnreachableInCall(Node n, DataFlowCall call) {
exists(
DirectParameterNode paramNode, ConstantIntegralTypeArgumentNode arg,
IntegerConstantInstruction constant, int k, Operand left, Operand right, IRBlock block
|
// arg flows into `paramNode`
DataFlowImplCommon::viableParamArg(call, paramNode, arg) and
left = constant.getAUse() and
right = valueNumber(paramNode.getInstruction()).getAUse() and
block = n.getBasicBlock()
|
// and there's a guard condition which ensures that the result of `left == right + k` is `areEqual`
exists(boolean areEqual |
ensuresEq(pragma[only_bind_into](left), pragma[only_bind_into](right),
pragma[only_bind_into](k), pragma[only_bind_into](block), areEqual)
|
// this block ensures that left = right + k, but it holds that `left != right + k`
areEqual = true and
constant.getValue().toInt() != arg.getValue() + k
or
// this block ensures that or `left != right + k`, but it holds that `left = right + k`
areEqual = false and
constant.getValue().toInt() = arg.getValue() + k
)
or
// or there's a guard condition which ensures that the result of `left < right + k` is `isLessThan`
exists(boolean isLessThan |
ensuresLt(pragma[only_bind_into](left), pragma[only_bind_into](right),
pragma[only_bind_into](k), pragma[only_bind_into](block), isLessThan)
|
isLessThan = true and
// this block ensures that `left < right + k`, but it holds that `left >= right + k`
constant.getValue().toInt() >= arg.getValue() + k
or
// this block ensures that `left >= right + k`, but it holds that `left < right + k`
isLessThan = false and
constant.getValue().toInt() < arg.getValue() + k
)
)
}
}
import IsUnreachableInCall
/** /**
* Holds if access paths with `c` at their head always should be tracked at high * Holds if access paths with `c` at their head always should be tracked at high
@@ -952,7 +845,7 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
* by default as a heuristic. * by default as a heuristic.
*/ */
predicate allowParameterReturnInSelf(ParameterNode p) { p instanceof IndirectParameterNode } predicate allowParameterReturnInSelf(ParameterNode p) { none() }
private predicate fieldHasApproxName(Field f, string s) { private predicate fieldHasApproxName(Field f, string s) {
s = f.getName().charAt(0) and s = f.getName().charAt(0) and
@@ -1012,6 +905,14 @@ ContentApprox getContentApprox(Content c) {
) )
} }
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
override predicate argHasPostUpdateExclude(ArgumentNode n) {
// The rules for whether an IR argument gets a post-update node are too
// complex to model here.
any()
}
}
/** /**
* A local flow relation that includes both local steps, read steps and * A local flow relation that includes both local steps, read steps and
* argument-to-return flow through summarized functions. * argument-to-return flow through summarized functions.
@@ -1071,7 +972,7 @@ private IRVariable getIRVariableForParameterNode(ParameterNode p) {
/** Holds if `v` is the source variable corresponding to the parameter represented by `p`. */ /** Holds if `v` is the source variable corresponding to the parameter represented by `p`. */
pragma[nomagic] pragma[nomagic]
private predicate parameterNodeHasSourceVariable(ParameterNode p, Ssa::SourceVariable v) { private predicate parameterNodeHasSourceVariable(ParameterNode p, Ssa::SourceIRVariable v) {
v.getIRVariable() = getIRVariableForParameterNode(p) and v.getIRVariable() = getIRVariableForParameterNode(p) and
exists(Position pos | p.isParameterOf(_, pos) | exists(Position pos | p.isParameterOf(_, pos) |
pos instanceof DirectPosition and pos instanceof DirectPosition and

View File

@@ -274,7 +274,7 @@ class Node extends TIRDataFlowNode {
* represents the value of `**x` going into `f`. * represents the value of `**x` going into `f`.
*/ */
Expr asIndirectArgument(int index) { Expr asIndirectArgument(int index) {
this.(SideEffectOperandNode).hasAddressOperandAndIndirectionIndex(_, index) and this.(SideEffectOperandNode).getIndirectionIndex() = index and
result = this.(SideEffectOperandNode).getArgument() result = this.(SideEffectOperandNode).getArgument()
} }
@@ -317,7 +317,7 @@ class Node extends TIRDataFlowNode {
index = 0 and index = 0 and
result = this.(ExplicitParameterNode).getParameter() result = this.(ExplicitParameterNode).getParameter()
or or
this.(IndirectParameterNode).hasInstructionAndIndirectionIndex(_, index) and this.(IndirectParameterNode).getIndirectionIndex() = index and
result = this.(IndirectParameterNode).getParameter() result = this.(IndirectParameterNode).getParameter()
} }
@@ -550,14 +550,11 @@ class SsaPhiNode extends Node, TSsaPhiNode {
* `fromBackEdge` is true if data flows along a back-edge, * `fromBackEdge` is true if data flows along a back-edge,
* and `false` otherwise. * and `false` otherwise.
*/ */
cached
final Node getAnInput(boolean fromBackEdge) { final Node getAnInput(boolean fromBackEdge) {
localFlowStep(result, this) and localFlowStep(result, this) and
exists(IRBlock bPhi, IRBlock bResult | if phi.getBasicBlock().dominates(result.getBasicBlock())
bPhi = phi.getBasicBlock() and bResult = result.getBasicBlock() then fromBackEdge = true
| else fromBackEdge = false
if bPhi.dominates(bResult) then fromBackEdge = true else fromBackEdge = false
)
} }
/** Gets a node that is used as input to this phi node. */ /** Gets a node that is used as input to this phi node. */
@@ -580,20 +577,15 @@ class SsaPhiNode extends Node, TSsaPhiNode {
* *
* A node representing a value after leaving a function. * A node representing a value after leaving a function.
*/ */
class SideEffectOperandNode extends Node instanceof IndirectOperand { class SideEffectOperandNode extends Node, IndirectOperand {
CallInstruction call; CallInstruction call;
int argumentIndex; int argumentIndex;
SideEffectOperandNode() { SideEffectOperandNode() { operand = call.getArgumentOperand(argumentIndex) }
IndirectOperand.super.hasOperandAndIndirectionIndex(call.getArgumentOperand(argumentIndex), _)
}
CallInstruction getCallInstruction() { result = call } CallInstruction getCallInstruction() { result = call }
/** Gets the underlying operand and the underlying indirection index. */ Operand getAddressOperand() { result = operand }
predicate hasAddressOperandAndIndirectionIndex(Operand operand, int indirectionIndex) {
IndirectOperand.super.hasOperandAndIndirectionIndex(operand, indirectionIndex)
}
int getArgumentIndex() { result = argumentIndex } int getArgumentIndex() { result = argumentIndex }
@@ -673,10 +665,10 @@ class InitialGlobalValue extends Node, TInitialGlobalValue {
* *
* A node representing an indirection of a parameter. * A node representing an indirection of a parameter.
*/ */
class IndirectParameterNode extends Node instanceof IndirectInstruction { class IndirectParameterNode extends Node, IndirectInstruction {
InitializeParameterInstruction init; InitializeParameterInstruction init;
IndirectParameterNode() { IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _) } IndirectParameterNode() { this.getInstruction() = init }
int getArgumentIndex() { init.hasIndex(result) } int getArgumentIndex() { init.hasIndex(result) }
@@ -685,12 +677,7 @@ class IndirectParameterNode extends Node instanceof IndirectInstruction {
override Declaration getEnclosingCallable() { result = this.getFunction() } override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = init.getEnclosingFunction() } override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
/** Gets the underlying operand and the underlying indirection index. */
predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index)
}
override Location getLocationImpl() { result = this.getParameter().getLocation() } override Location getLocationImpl() { result = this.getParameter().getLocation() }
@@ -712,8 +699,7 @@ class IndirectReturnNode extends Node {
IndirectReturnNode() { IndirectReturnNode() {
this instanceof FinalParameterNode this instanceof FinalParameterNode
or or
this.(IndirectOperand) this.(IndirectOperand).getOperand() = any(ReturnValueInstruction ret).getReturnAddressOperand()
.hasOperandAndIndirectionIndex(any(ReturnValueInstruction ret).getReturnAddressOperand(), _)
} }
override Declaration getEnclosingCallable() { result = this.getFunction() } override Declaration getEnclosingCallable() { result = this.getFunction() }
@@ -736,7 +722,7 @@ class IndirectReturnNode extends Node {
int getIndirectionIndex() { int getIndirectionIndex() {
result = this.(FinalParameterNode).getIndirectionIndex() result = this.(FinalParameterNode).getIndirectionIndex()
or or
this.(IndirectOperand).hasOperandAndIndirectionIndex(_, result) result = this.(IndirectOperand).getIndirectionIndex()
} }
} }
@@ -784,12 +770,26 @@ class IndirectArgumentOutNode extends Node, TIndirectArgumentOutNode, PartialDef
override Expr getDefinedExpr() { result = operand.getDef().getUnconvertedResultExpression() } override Expr getDefinedExpr() { result = operand.getDef().getUnconvertedResultExpression() }
} }
pragma[nomagic]
predicate indirectReturnOutNodeOperand0(CallInstruction call, Operand operand, int indirectionIndex) {
Ssa::hasRawIndirectInstruction(call, indirectionIndex) and
operandForFullyConvertedCall(operand, call)
}
pragma[nomagic]
predicate indirectReturnOutNodeInstruction0(
CallInstruction call, Instruction instr, int indirectionIndex
) {
Ssa::hasRawIndirectInstruction(call, indirectionIndex) and
instructionForFullyConvertedCall(instr, call)
}
/** /**
* Holds if `node` is an indirect operand with columns `(operand, indirectionIndex)`, and * Holds if `node` is an indirect operand with columns `(operand, indirectionIndex)`, and
* `operand` represents a use of the fully converted value of `call`. * `operand` represents a use of the fully converted value of `call`.
*/ */
private predicate hasOperand(Node node, CallInstruction call, int indirectionIndex, Operand operand) { private predicate hasOperand(Node node, CallInstruction call, int indirectionIndex, Operand operand) {
operandForFullyConvertedCall(operand, call) and indirectReturnOutNodeOperand0(call, operand, indirectionIndex) and
hasOperandAndIndex(node, operand, indirectionIndex) hasOperandAndIndex(node, operand, indirectionIndex)
} }
@@ -802,7 +802,7 @@ private predicate hasOperand(Node node, CallInstruction call, int indirectionInd
private predicate hasInstruction( private predicate hasInstruction(
Node node, CallInstruction call, int indirectionIndex, Instruction instr Node node, CallInstruction call, int indirectionIndex, Instruction instr
) { ) {
instructionForFullyConvertedCall(instr, call) and indirectReturnOutNodeInstruction0(call, instr, indirectionIndex) and
hasInstructionAndIndex(node, instr, indirectionIndex) hasInstructionAndIndex(node, instr, indirectionIndex)
} }
@@ -1106,8 +1106,7 @@ predicate exprNodeShouldBeInstruction(Node node, Expr e) {
/** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */ /** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */
predicate indirectExprNodeShouldBeIndirectInstruction(IndirectInstruction node, Expr e) { predicate indirectExprNodeShouldBeIndirectInstruction(IndirectInstruction node, Expr e) {
exists(Instruction instr | exists(Instruction instr |
node.hasInstructionAndIndirectionIndex(instr, _) and instr = node.getInstruction() and not indirectExprNodeShouldBeIndirectOperand(_, e)
not indirectExprNodeShouldBeIndirectOperand(_, e)
| |
e = instr.(VariableAddressInstruction).getAst().(Expr).getFullyConverted() e = instr.(VariableAddressInstruction).getAst().(Expr).getFullyConverted()
or or
@@ -1308,8 +1307,8 @@ pragma[noinline]
private predicate indirectParameterNodeHasArgumentIndexAndIndex( private predicate indirectParameterNodeHasArgumentIndexAndIndex(
IndirectParameterNode node, int argumentIndex, int indirectionIndex IndirectParameterNode node, int argumentIndex, int indirectionIndex
) { ) {
node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and node.getArgumentIndex() = argumentIndex and
node.getArgumentIndex() = argumentIndex node.getIndirectionIndex() = indirectionIndex
} }
/** A synthetic parameter to model the pointed-to object of a pointer parameter. */ /** A synthetic parameter to model the pointed-to object of a pointer parameter. */
@@ -1480,14 +1479,18 @@ VariableNode variableNode(Variable v) {
*/ */
Node uninitializedNode(LocalVariable v) { none() } Node uninitializedNode(LocalVariable v) { none() }
pragma[noinline]
predicate hasOperandAndIndex(IndirectOperand indirectOperand, Operand operand, int indirectionIndex) { predicate hasOperandAndIndex(IndirectOperand indirectOperand, Operand operand, int indirectionIndex) {
indirectOperand.hasOperandAndIndirectionIndex(operand, indirectionIndex) indirectOperand.getOperand() = operand and
indirectOperand.getIndirectionIndex() = indirectionIndex
} }
pragma[noinline]
predicate hasInstructionAndIndex( predicate hasInstructionAndIndex(
IndirectInstruction indirectInstr, Instruction instr, int indirectionIndex IndirectInstruction indirectInstr, Instruction instr, int indirectionIndex
) { ) {
indirectInstr.hasInstructionAndIndirectionIndex(instr, indirectionIndex) indirectInstr.getInstruction() = instr and
indirectInstr.getIndirectionIndex() = indirectionIndex
} }
cached cached
@@ -1523,25 +1526,6 @@ private module Cached {
) )
} }
/**
* Holds if `operand.getDef() = instr`, but there exists a `StoreInstruction` that
* writes to an address that is equivalent to the value computed by `instr` in
* between `instr` and `operand`, and therefore there should not be flow from `*instr`
* to `*operand`.
*/
pragma[nomagic]
private predicate isStoredToBetween(Instruction instr, Operand operand) {
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand)) and
exists(StoreInstruction store, IRBlock block, int storeIndex, int instrIndex, int operandIndex |
store.getDestinationAddress() = instr and
block.getInstruction(storeIndex) = store and
block.getInstruction(instrIndex) = instr and
block.getInstruction(operandIndex) = operand.getUse() and
instrIndex < storeIndex and
storeIndex < operandIndex
)
}
private predicate indirectionInstructionFlow( private predicate indirectionInstructionFlow(
RawIndirectInstruction nodeFrom, IndirectOperand nodeTo RawIndirectInstruction nodeFrom, IndirectOperand nodeTo
) { ) {
@@ -1551,8 +1535,7 @@ private module Cached {
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand)) simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand))
| |
hasOperandAndIndex(nodeTo, operand, pragma[only_bind_into](indirectionIndex)) and hasOperandAndIndex(nodeTo, operand, pragma[only_bind_into](indirectionIndex)) and
hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex)) and hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex))
not isStoredToBetween(instr, operand)
) )
} }
@@ -1673,7 +1656,8 @@ module ExprFlowCached {
private predicate isIndirectBaseOfArrayAccess(IndirectOperand n, Expr e) { private predicate isIndirectBaseOfArrayAccess(IndirectOperand n, Expr e) {
exists(LoadInstruction load, PointerArithmeticInstruction pai | exists(LoadInstruction load, PointerArithmeticInstruction pai |
pai = load.getSourceAddress() and pai = load.getSourceAddress() and
n.hasOperandAndIndirectionIndex(pai.getLeftOperand(), 1) and pai.getLeftOperand() = n.getOperand() and
n.getIndirectionIndex() = 1 and
e = load.getConvertedResultExpression() e = load.getConvertedResultExpression()
) )
} }
@@ -1841,20 +1825,6 @@ class Content extends TContent {
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
} }
/** Gets the indirection index of this `Content`. */
abstract int getIndirectionIndex();
/**
* INTERNAL: Do not use.
*
* Holds if a write to this `Content` implies that `c` is
* also cleared.
*
* For example, a write to a field `f` implies that any content of
* the form `*f` is also cleared.
*/
abstract predicate impliesClearOf(Content c);
} }
/** A reference through a non-union instance field. */ /** A reference through a non-union instance field. */
@@ -1872,21 +1842,10 @@ class FieldContent extends Content, TFieldContent {
Field getField() { result = f } Field getField() { result = f }
/** Gets the indirection index of this `FieldContent`. */
pragma[inline] pragma[inline]
override int getIndirectionIndex() { int getIndirectionIndex() {
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex) pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
} }
override predicate impliesClearOf(Content c) {
exists(FieldContent fc |
fc = c and
fc.getField() = f and
// If `this` is `f` then `c` is cleared if it's of the
// form `*f`, `**f`, etc.
fc.getIndirectionIndex() >= indirectionIndex
)
}
} }
/** A reference through an instance field of a union. */ /** A reference through an instance field of a union. */
@@ -1911,21 +1870,9 @@ class UnionContent extends Content, TUnionContent {
/** Gets the indirection index of this `UnionContent`. */ /** Gets the indirection index of this `UnionContent`. */
pragma[inline] pragma[inline]
override int getIndirectionIndex() { int getIndirectionIndex() {
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex) pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
} }
override predicate impliesClearOf(Content c) {
exists(UnionContent uc |
uc = c and
uc.getUnion() = u and
// If `this` is `u` then `c` is cleared if it's of the
// form `*u`, `**u`, etc. (and we ignore `bytes` because
// we know the entire union is overwritten because it's a
// union).
uc.getIndirectionIndex() >= indirectionIndex
)
}
} }
/** /**

View File

@@ -448,8 +448,6 @@ module TaintedWithPath {
} }
predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) } predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
predicate neverSkip(Node node) { none() }
} }
private module AdjustedFlow = TaintTracking::Global<AdjustedConfig>; private module AdjustedFlow = TaintTracking::Global<AdjustedConfig>;

View File

@@ -13,7 +13,7 @@ class FieldFlowPropertyProvider extends IRPropertyProvider {
override string getOperandProperty(Operand operand, string key) { override string getOperandProperty(Operand operand, string key) {
exists(PostFieldUpdateNode pfun, Content content | exists(PostFieldUpdateNode pfun, Content content |
key = "store " + content.toString() and key = "store " + content.toString() and
pfun.getPreUpdateNode().(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and operand = pfun.getPreUpdateNode().(IndirectOperand).getOperand() and
result = result =
strictconcat(string element, Node node | strictconcat(string element, Node node |
storeStep(node, content, pfun) and storeStep(node, content, pfun) and
@@ -25,7 +25,7 @@ class FieldFlowPropertyProvider extends IRPropertyProvider {
or or
exists(Node node2, Content content | exists(Node node2, Content content |
key = "read " + content.toString() and key = "read " + content.toString() and
node2.(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and operand = node2.(IndirectOperand).getOperand() and
result = result =
strictconcat(string element, Node node1 | strictconcat(string element, Node node1 |
readStep(node1, content, node2) and readStep(node1, content, node2) and

View File

@@ -18,12 +18,9 @@ private string stars(int k) {
} }
string starsForNode(Node node) { string starsForNode(Node node) {
exists(int indirectionIndex | result = stars(node.(IndirectInstruction).getIndirectionIndex())
node.(IndirectInstruction).hasInstructionAndIndirectionIndex(_, indirectionIndex) or or
node.(IndirectOperand).hasOperandAndIndirectionIndex(_, indirectionIndex) result = stars(node.(IndirectOperand).getIndirectionIndex())
|
result = stars(indirectionIndex)
)
or or
not node instanceof IndirectInstruction and not node instanceof IndirectInstruction and
not node instanceof IndirectOperand and not node instanceof IndirectOperand and

View File

@@ -87,30 +87,6 @@ module ProductFlow {
* dataflow graph. * dataflow graph.
*/ */
default predicate isBarrierIn2(DataFlow::Node node) { none() } default predicate isBarrierIn2(DataFlow::Node node) { none() }
/**
* Gets the virtual dispatch branching limit when calculating field flow in the first
* projection of the product dataflow graph.
*
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
default int fieldFlowBranchLimit1() {
// NOTE: This should be synchronized with the default value in the shared dataflow library
result = 2
}
/**
* Gets the virtual dispatch branching limit when calculating field flow in the second
* projection of the product dataflow graph.
*
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
default int fieldFlowBranchLimit2() {
// NOTE: This should be synchronized with the default value in the shared dataflow library
result = 2
}
} }
/** /**
@@ -216,13 +192,13 @@ module ProductFlow {
* Holds if data flow through `node` is prohibited through the first projection of the product * Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph when the flow state is `state`. * dataflow graph when the flow state is `state`.
*/ */
default predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() } predicate isBarrier1(DataFlow::Node node, FlowState1 state);
/** /**
* Holds if data flow through `node` is prohibited through the second projection of the product * Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph when the flow state is `state`. * dataflow graph when the flow state is `state`.
*/ */
default predicate isBarrier2(DataFlow::Node node, FlowState2 state) { none() } predicate isBarrier2(DataFlow::Node node, FlowState2 state);
/** /**
* Holds if data flow through `node` is prohibited through the first projection of the product * Holds if data flow through `node` is prohibited through the first projection of the product
@@ -261,11 +237,9 @@ module ProductFlow {
* *
* This step is only applicable in `state1` and updates the flow state to `state2`. * This step is only applicable in `state1` and updates the flow state to `state2`.
*/ */
default predicate isAdditionalFlowStep1( predicate isAdditionalFlowStep1(
DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2 DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2
) { );
none()
}
/** /**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
@@ -279,11 +253,9 @@ module ProductFlow {
* *
* This step is only applicable in `state1` and updates the flow state to `state2`. * This step is only applicable in `state1` and updates the flow state to `state2`.
*/ */
default predicate isAdditionalFlowStep2( predicate isAdditionalFlowStep2(
DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2 DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2
) { );
none()
}
/** /**
* Holds if data flow into `node` is prohibited in the first projection of the product * Holds if data flow into `node` is prohibited in the first projection of the product
@@ -296,30 +268,6 @@ module ProductFlow {
* dataflow graph. * dataflow graph.
*/ */
default predicate isBarrierIn2(DataFlow::Node node) { none() } default predicate isBarrierIn2(DataFlow::Node node) { none() }
/**
* Gets the virtual dispatch branching limit when calculating field flow in the first
* projection of the product dataflow graph.
*
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
default int fieldFlowBranchLimit1() {
// NOTE: This should be synchronized with the default value in the shared dataflow library
result = 2
}
/**
* Gets the virtual dispatch branching limit when calculating field flow in the second
* projection of the product dataflow graph.
*
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
default int fieldFlowBranchLimit2() {
// NOTE: This should be synchronized with the default value in the shared dataflow library
result = 2
}
} }
/** /**
@@ -345,22 +293,6 @@ module ProductFlow {
reachable(source1, source2, sink1, sink2) reachable(source1, source2, sink1, sink2)
} }
/** Holds if data can flow from `(source1, source2)` to `(sink1, sink2)`. */
predicate flow(
DataFlow::Node source1, DataFlow::Node source2, DataFlow::Node sink1, DataFlow::Node sink2
) {
exists(
Flow1::PathNode pSource1, Flow2::PathNode pSource2, Flow1::PathNode pSink1,
Flow2::PathNode pSink2
|
pSource1.getNode() = source1 and
pSource2.getNode() = source2 and
pSink1.getNode() = sink1 and
pSink2.getNode() = sink2 and
flowPath(pSource1, pSource2, pSink1, pSink2)
)
}
private module Config1 implements DataFlow::StateConfigSig { private module Config1 implements DataFlow::StateConfigSig {
class FlowState = FlowState1; class FlowState = FlowState1;
@@ -383,8 +315,6 @@ module ProductFlow {
} }
predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn1(node) } predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn1(node) }
int fieldFlowBranchLimit() { result = Config::fieldFlowBranchLimit1() }
} }
private module Flow1 = DataFlow::GlobalWithState<Config1>; private module Flow1 = DataFlow::GlobalWithState<Config1>;
@@ -417,8 +347,6 @@ module ProductFlow {
} }
predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn2(node) } predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn2(node) }
int fieldFlowBranchLimit() { result = Config::fieldFlowBranchLimit2() }
} }
private module Flow2 = DataFlow::GlobalWithState<Config2>; private module Flow2 = DataFlow::GlobalWithState<Config2>;
@@ -431,6 +359,7 @@ module ProductFlow {
Config::isSinkPair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState()) Config::isSinkPair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState())
} }
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate fwdReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) { private predicate fwdReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) {
isSourcePair(node1, node2) isSourcePair(node1, node2)
@@ -467,6 +396,7 @@ module ProductFlow {
fwdIsSuccessorExit(pragma[only_bind_into](mid1), pragma[only_bind_into](mid2), succ1, succ2) fwdIsSuccessorExit(pragma[only_bind_into](mid1), pragma[only_bind_into](mid2), succ1, succ2)
} }
pragma[assume_small_delta]
private predicate fwdIsSuccessor( private predicate fwdIsSuccessor(
Flow1::PathNode pred1, Flow2::PathNode pred2, Flow1::PathNode succ1, Flow2::PathNode succ2 Flow1::PathNode pred1, Flow2::PathNode pred2, Flow1::PathNode succ1, Flow2::PathNode succ2
) { ) {
@@ -476,6 +406,7 @@ module ProductFlow {
) )
} }
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate revReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) { private predicate revReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) {
fwdReachableInterprocEntry(node1, node2) and fwdReachableInterprocEntry(node1, node2) and

View File

@@ -10,35 +10,32 @@ private import ssa0.SsaInternals as SsaInternals0
import SsaInternalsCommon import SsaInternalsCommon
private module SourceVariables { private module SourceVariables {
int getMaxIndirectionForIRVariable(IRVariable var) {
exists(Type type, boolean isGLValue |
var.getLanguageType().hasType(type, isGLValue) and
if isGLValue = true
then result = 1 + getMaxIndirectionsForType(type)
else result = getMaxIndirectionsForType(type)
)
}
cached cached
private newtype TSourceVariable = private newtype TSourceVariable =
TMkSourceVariable(SsaInternals0::SourceVariable base, int ind) { TSourceIRVariable(BaseIRVariable baseVar, int ind) {
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1] ind = [0 .. getMaxIndirectionForIRVariable(baseVar.getIRVariable())]
} or
TCallVariable(AllocationInstruction call, int ind) {
ind = [0 .. countIndirectionsForCppType(getResultLanguageType(call))]
} }
class SourceVariable extends TSourceVariable { abstract class SourceVariable extends TSourceVariable {
SsaInternals0::SourceVariable base;
int ind; int ind;
SourceVariable() { this = TMkSourceVariable(base, ind) } bindingset[ind]
SourceVariable() { any() }
/** Gets the IR variable associated with this `SourceVariable`, if any. */
IRVariable getIRVariable() { result = base.(BaseIRVariable).getIRVariable() }
/**
* Gets the base source variable (i.e., the variable without any
* indirections) of this source variable.
*/
SsaInternals0::SourceVariable getBaseVariable() { result = base }
/** Gets a textual representation of this element. */ /** Gets a textual representation of this element. */
string toString() { abstract string toString();
ind = 0 and
result = this.getBaseVariable().toString()
or
ind > 0 and
result = this.getBaseVariable().toString() + " indirection"
}
/** /**
* Gets the number of loads performed on the base source variable * Gets the number of loads performed on the base source variable
@@ -46,19 +43,65 @@ private module SourceVariables {
*/ */
int getIndirection() { result = ind } int getIndirection() { result = ind }
/**
* Gets the base source variable (i.e., the variable without any
* indirections) of this source variable.
*/
abstract BaseSourceVariable getBaseVariable();
/** Holds if this variable is a glvalue. */ /** Holds if this variable is a glvalue. */
predicate isGLValue() { ind = 0 } predicate isGLValue() { none() }
/** /**
* Gets the type of this source variable. If `isGLValue()` holds, then * Gets the type of this source variable. If `isGLValue()` holds, then
* the type of this source variable should be thought of as "pointer * the type of this source variable should be thought of as "pointer
* to `getType()`". * to `getType()`".
*/ */
DataFlowType getType() { abstract DataFlowType getType();
if this.isGLValue()
then result = base.getType()
else result = getTypeImpl(base.getType(), ind - 1)
} }
class SourceIRVariable extends SourceVariable, TSourceIRVariable {
BaseIRVariable var;
SourceIRVariable() { this = TSourceIRVariable(var, ind) }
IRVariable getIRVariable() { result = var.getIRVariable() }
override BaseIRVariable getBaseVariable() { result.getIRVariable() = this.getIRVariable() }
override string toString() {
ind = 0 and
result = this.getIRVariable().toString()
or
ind > 0 and
result = this.getIRVariable().toString() + " indirection"
}
override predicate isGLValue() { ind = 0 }
override DataFlowType getType() {
if ind = 0 then result = var.getType() else result = getTypeImpl(var.getType(), ind - 1)
}
}
class CallVariable extends SourceVariable, TCallVariable {
AllocationInstruction call;
CallVariable() { this = TCallVariable(call, ind) }
AllocationInstruction getCall() { result = call }
override BaseCallVariable getBaseVariable() { result.getCallInstruction() = call }
override string toString() {
ind = 0 and
result = "Call"
or
ind > 0 and
result = "Call indirection"
}
override DataFlowType getType() { result = getTypeImpl(call.getResultType(), ind) }
} }
} }
@@ -74,7 +117,7 @@ predicate hasRawIndirectOperand(Operand op, int indirectionIndex) {
type = getLanguageType(op) and type = getLanguageType(op) and
m = countIndirectionsForCppType(type) and m = countIndirectionsForCppType(type) and
indirectionIndex = [1 .. m] and indirectionIndex = [1 .. m] and
not hasIRRepresentationOfIndirectOperand(op, indirectionIndex, _, _) not exists(getIRRepresentationOfIndirectOperand(op, indirectionIndex))
) )
} }
@@ -88,15 +131,14 @@ predicate hasRawIndirectInstruction(Instruction instr, int indirectionIndex) {
type = getResultLanguageType(instr) and type = getResultLanguageType(instr) and
m = countIndirectionsForCppType(type) and m = countIndirectionsForCppType(type) and
indirectionIndex = [1 .. m] and indirectionIndex = [1 .. m] and
not hasIRRepresentationOfIndirectInstruction(instr, indirectionIndex, _, _) not exists(getIRRepresentationOfIndirectInstruction(instr, indirectionIndex))
) )
} }
cached cached
private newtype TDefOrUseImpl = private newtype TDefOrUseImpl =
TDefImpl(BaseSourceVariableInstruction base, Operand address, int indirectionIndex) { TDefImpl(Operand address, int indirectionIndex) {
isDef(_, _, address, base, _, indirectionIndex) and exists(Instruction base | isDef(_, _, address, base, _, indirectionIndex) |
(
// We only include the definition if the SSA pruning stage // We only include the definition if the SSA pruning stage
// concluded that the definition is live after the write. // concluded that the definition is live after the write.
any(SsaInternals0::Def def).getAddressOperand() = address any(SsaInternals0::Def def).getAddressOperand() = address
@@ -106,9 +148,9 @@ private newtype TDefOrUseImpl =
base.(VariableAddressInstruction).getAstVariable() instanceof GlobalLikeVariable base.(VariableAddressInstruction).getAstVariable() instanceof GlobalLikeVariable
) )
} or } or
TUseImpl(BaseSourceVariableInstruction base, Operand operand, int indirectionIndex) { TUseImpl(Operand operand, int indirectionIndex) {
isUse(_, operand, base, _, indirectionIndex) and isUse(_, operand, _, _, indirectionIndex) and
not isDef(true, _, operand, _, _, _) not isDef(_, _, operand, _, _, _)
} or } or
TGlobalUse(GlobalLikeVariable v, IRFunction f, int indirectionIndex) { TGlobalUse(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
// Represents a final "use" of a global variable to ensure that // Represents a final "use" of a global variable to ensure that
@@ -194,7 +236,7 @@ abstract private class DefOrUseImpl extends TDefOrUseImpl {
/** /**
* Gets the instruction that computes the base of this definition or use. * Gets the instruction that computes the base of this definition or use.
* This is always a `VariableAddressInstruction` or an `CallInstruction`. * This is always a `VariableAddressInstruction` or an `AllocationInstruction`.
*/ */
abstract BaseSourceVariableInstruction getBase(); abstract BaseSourceVariableInstruction getBase();
@@ -266,17 +308,15 @@ abstract class DefImpl extends DefOrUseImpl {
} }
private class DirectDef extends DefImpl, TDefImpl { private class DirectDef extends DefImpl, TDefImpl {
BaseSourceVariableInstruction base; DirectDef() { this = TDefImpl(address, ind) }
DirectDef() { this = TDefImpl(base, address, ind) } override BaseSourceVariableInstruction getBase() { isDef(_, _, address, result, _, _) }
override BaseSourceVariableInstruction getBase() { result = base } override int getIndirection() { isDef(_, _, address, _, result, ind) }
override int getIndirection() { isDef(_, _, address, base, result, ind) } override Node0Impl getValue() { isDef(_, result, address, _, _, _) }
override Node0Impl getValue() { isDef(_, result, address, base, _, _) } override predicate isCertain() { isDef(true, _, address, _, _, ind) }
override predicate isCertain() { isDef(true, _, address, base, _, ind) }
} }
private class IteratorDef extends DefImpl, TIteratorDef { private class IteratorDef extends DefImpl, TIteratorDef {
@@ -319,7 +359,6 @@ abstract class UseImpl extends DefOrUseImpl {
abstract private class OperandBasedUse extends UseImpl { abstract private class OperandBasedUse extends UseImpl {
Operand operand; Operand operand;
BaseSourceVariableInstruction base;
bindingset[ind] bindingset[ind]
OperandBasedUse() { any() } OperandBasedUse() { any() }
@@ -327,6 +366,7 @@ abstract private class OperandBasedUse extends UseImpl {
final override predicate hasIndexInBlock(IRBlock block, int index) { final override predicate hasIndexInBlock(IRBlock block, int index) {
// See the comment in `ssa0`'s `OperandBasedUse` for an explanation of this // See the comment in `ssa0`'s `OperandBasedUse` for an explanation of this
// predicate's implementation. // predicate's implementation.
exists(BaseSourceVariableInstruction base | base = this.getBase() |
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand() if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
then then
exists(Operand op, int indirectionIndex, int indirection | exists(Operand op, int indirectionIndex, int indirection |
@@ -342,29 +382,34 @@ abstract private class OperandBasedUse extends UseImpl {
block.getInstruction(index) = op.getUse() block.getInstruction(index) = op.getUse()
) )
else operand.getUse() = block.getInstruction(index) else operand.getUse() = block.getInstruction(index)
)
} }
final override BaseSourceVariableInstruction getBase() { result = base }
final Operand getOperand() { result = operand } final Operand getOperand() { result = operand }
final override Cpp::Location getLocation() { result = operand.getLocation() } final override Cpp::Location getLocation() { result = operand.getLocation() }
} }
private class DirectUse extends OperandBasedUse, TUseImpl { private class DirectUse extends OperandBasedUse, TUseImpl {
DirectUse() { this = TUseImpl(base, operand, ind) } DirectUse() { this = TUseImpl(operand, ind) }
override int getIndirection() { isUse(_, operand, base, result, ind) } override int getIndirection() { isUse(_, operand, _, result, ind) }
override predicate isCertain() { isUse(true, operand, base, _, ind) } override BaseSourceVariableInstruction getBase() { isUse(_, operand, result, _, ind) }
override predicate isCertain() { isUse(true, operand, _, _, ind) }
override Node getNode() { nodeHasOperand(result, operand, ind) } override Node getNode() { nodeHasOperand(result, operand, ind) }
} }
private class IteratorUse extends OperandBasedUse, TIteratorUse { private class IteratorUse extends OperandBasedUse, TIteratorUse {
IteratorUse() { this = TIteratorUse(operand, base, ind) } BaseSourceVariableInstruction container;
override int getIndirection() { isIteratorUse(base, operand, result, ind) } IteratorUse() { this = TIteratorUse(operand, container, ind) }
override int getIndirection() { isIteratorUse(container, operand, result, ind) }
override BaseSourceVariableInstruction getBase() { result = container }
override predicate isCertain() { none() } override predicate isCertain() { none() }
@@ -610,7 +655,7 @@ private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and
hasOperandAndIndex(nTo, op2, indirectionIndex - 1) and hasOperandAndIndex(nTo, op2, indirectionIndex - 1) and
instr = op2.getDef() and instr = op2.getDef() and
isDereference(instr, op1, _) isDereference(instr, op1)
) )
) )
} }
@@ -638,24 +683,12 @@ private predicate adjustForPointerArith(PostUpdateNode pun, UseOrPhi use) {
) )
} }
/** private predicate ssaFlowImpl(SsaDefOrUse defOrUse, Node nodeFrom, Node nodeTo, boolean uncertain) {
* Holds if `nodeFrom` flows to `nodeTo` because there is `def-use` or exists(UseOrPhi use |
* `use-use` flow from `defOrUse` to `use`.
*
* `uncertain` is `true` if the `defOrUse` is an uncertain definition.
*/
private predicate localSsaFlow(
SsaDefOrUse defOrUse, Node nodeFrom, UseOrPhi use, Node nodeTo, boolean uncertain
) {
nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and
adjacentDefRead(defOrUse, use) and adjacentDefRead(defOrUse, use) and
useToNode(use, nodeTo) and useToNode(use, nodeTo) and
nodeFrom != nodeTo nodeFrom != nodeTo
}
private predicate ssaFlowImpl(SsaDefOrUse defOrUse, Node nodeFrom, Node nodeTo, boolean uncertain) {
exists(UseOrPhi use |
localSsaFlow(defOrUse, nodeFrom, use, nodeTo, uncertain)
or or
// Initial global variable value to a first use // Initial global variable value to a first use
nodeFrom.(InitialGlobalValue).getGlobalDef() = defOrUse and nodeFrom.(InitialGlobalValue).getGlobalDef() = defOrUse and
@@ -696,99 +729,19 @@ predicate ssaFlow(Node nodeFrom, Node nodeTo) {
) )
} }
private predicate isArgumentOfCallableInstruction(DataFlowCall call, Instruction instr) { private predicate isArgumentOfCallable(DataFlowCall call, ArgumentNode arg) {
isArgumentOfCallableOperand(call, unique( | | getAUse(instr))) arg.argumentOf(call, _)
} }
private predicate isArgumentOfCallableOperand(DataFlowCall call, Operand operand) { /** Holds if there is def-use or use-use flow from `pun` to `nodeTo`. */
operand.(ArgumentOperand).getCall() = call
or
exists(FieldAddressInstruction fai |
fai.getObjectAddressOperand() = operand and
isArgumentOfCallableInstruction(call, fai)
)
or
exists(Instruction deref |
isArgumentOfCallableInstruction(call, deref) and
isDereference(deref, operand, _)
)
or
exists(Instruction instr |
isArgumentOfCallableInstruction(call, instr) and
conversionFlow(operand, instr, _, _)
)
}
private predicate isArgumentOfCallable(DataFlowCall call, Node n) {
isArgumentOfCallableOperand(call, n.asOperand())
or
exists(Operand op |
n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and
isArgumentOfCallableOperand(call, op)
)
or
exists(Instruction instr |
n.(IndirectInstruction).hasInstructionAndIndirectionIndex(instr, _) and
isArgumentOfCallableInstruction(call, instr)
)
}
/**
* Holds if there is use-use flow from `pun`'s pre-update node to `n`.
*/
private predicate postUpdateNodeToFirstUse(PostUpdateNode pun, Node n) {
exists(UseOrPhi use |
adjustForPointerArith(pun, use) and
useToNode(use, n)
)
}
private predicate stepUntilNotInCall(DataFlowCall call, Node n1, Node n2) {
isArgumentOfCallable(call, n1) and
exists(Node mid | localSsaFlow(_, n1, _, mid, _) |
isArgumentOfCallable(call, mid) and
stepUntilNotInCall(call, mid, n2)
or
not isArgumentOfCallable(call, mid) and
mid = n2
)
}
bindingset[n1, n2]
pragma[inline_late]
private predicate isArgumentOfSameCall(DataFlowCall call, Node n1, Node n2) {
isArgumentOfCallable(call, n1) and isArgumentOfCallable(call, n2)
}
/**
* Holds if there is def-use or use-use flow from `pun` to `nodeTo`.
*
* Note: This is more complex than it sounds. Consider a call such as:
* ```cpp
* write_first_argument(x, x);
* sink(x);
* ```
* Assume flow comes out of the first argument to `write_first_argument`. We
* don't want flow to go to the `x` that's also an argument to
* `write_first_argument` (because we just flowed out of that function, and we
* don't want to flow back into it again).
*
* We do, however, want flow from the output argument to `x` on the next line, and
* similarly we want flow from the second argument of `write_first_argument` to `x`
* on the next line.
*/
predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) { predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
exists(Node preUpdate, Node mid | exists(UseOrPhi use, Node preUpdate |
adjustForPointerArith(pun, use) and
useToNode(use, nodeTo) and
preUpdate = pun.getPreUpdateNode() and preUpdate = pun.getPreUpdateNode() and
postUpdateNodeToFirstUse(pun, mid) not exists(DataFlowCall call |
| isArgumentOfCallable(call, preUpdate) and isArgumentOfCallable(call, nodeTo)
exists(DataFlowCall call |
isArgumentOfSameCall(call, preUpdate, mid) and
stepUntilNotInCall(call, mid, nodeTo)
) )
or
not isArgumentOfSameCall(_, preUpdate, mid) and
nodeTo = mid
) )
} }

View File

@@ -6,7 +6,6 @@ private import DataFlowImplCommon as DataFlowImplCommon
private import DataFlowUtil private import DataFlowUtil
private import semmle.code.cpp.models.interfaces.PointerWrapper private import semmle.code.cpp.models.interfaces.PointerWrapper
private import DataFlowPrivate private import DataFlowPrivate
private import semmle.code.cpp.ir.ValueNumbering
/** /**
* Holds if `operand` is an operand that is not used by the dataflow library. * Holds if `operand` is an operand that is not used by the dataflow library.
@@ -118,16 +117,6 @@ private int countIndirections(Type t) {
else ( else (
result = any(Indirection ind | ind.getType() = t).getNumberOfIndirections() result = any(Indirection ind | ind.getType() = t).getNumberOfIndirections()
or or
// If there is an indirection for the type, but we cannot count the number of indirections
// it means we couldn't reach a non-indirection type by stripping off indirections. This
// can occur if an iterator specifies itself as the value type. In this case we default to
// 1 indirection fore the type.
exists(Indirection ind |
ind.getType() = t and
not exists(ind.getNumberOfIndirections()) and
result = 1
)
or
not exists(Indirection ind | ind.getType() = t) and not exists(Indirection ind | ind.getType() = t) and
result = 0 result = 0
) )
@@ -147,6 +136,14 @@ int countIndirectionsForCppType(LanguageType langType) {
) )
} }
/**
* A `CallInstruction` that calls an allocation function such
* as `malloc` or `operator new`.
*/
class AllocationInstruction extends CallInstruction {
AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction }
}
private predicate isIndirectionType(Type t) { t instanceof Indirection } private predicate isIndirectionType(Type t) { t instanceof Indirection }
private predicate hasUnspecifiedBaseType(Indirection t, Type base) { private predicate hasUnspecifiedBaseType(Indirection t, Type base) {
@@ -266,7 +263,7 @@ private module IteratorIndirections {
// Taint through `operator+=` and `operator-=` on iterators. // Taint through `operator+=` and `operator-=` on iterators.
call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and
node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and
node1.(IndirectOperand).hasOperandAndIndirectionIndex(call.getArgumentOperand(0), _) and node1.(IndirectOperand).getOperand() = call.getArgumentOperand(0) and
node1.getType().getUnspecifiedType() = this node1.getType().getUnspecifiedType() = this
) )
} }
@@ -320,20 +317,10 @@ private module IteratorIndirections {
} }
} }
/** predicate isDereference(Instruction deref, Operand address) {
* Holds if `deref` is the result of loading the value at the address any(Indirection ind).isAdditionalDereference(deref, address)
* represented by `address`.
*
* If `additional = true` then the dereference comes from an `Indirection`
* class (such as a call to an iterator's `operator*`), and if
* `additional = false` the dereference is a `LoadInstruction`.
*/
predicate isDereference(Instruction deref, Operand address, boolean additional) {
any(Indirection ind).isAdditionalDereference(deref, address) and
additional = true
or or
deref.(LoadInstruction).getSourceAddressOperand() = address and deref.(LoadInstruction).getSourceAddressOperand() = address
additional = false
} }
predicate isWrite(Node0Impl value, Operand address, boolean certain) { predicate isWrite(Node0Impl value, Operand address, boolean certain) {
@@ -371,22 +358,17 @@ newtype TBaseSourceVariable =
// Each IR variable gets its own source variable // Each IR variable gets its own source variable
TBaseIRVariable(IRVariable var) or TBaseIRVariable(IRVariable var) or
// Each allocation gets its own source variable // Each allocation gets its own source variable
TBaseCallVariable(CallInstruction call) { not call.getResultIRType() instanceof IRVoidType } TBaseCallVariable(AllocationInstruction call)
abstract private class AbstractBaseSourceVariable extends TBaseSourceVariable { abstract class BaseSourceVariable extends TBaseSourceVariable {
/** Gets a textual representation of this element. */ /** Gets a textual representation of this element. */
abstract string toString(); abstract string toString();
/** Gets the type of this base source variable. */ /** Gets the type of this base source variable. */
final DataFlowType getType() { this.getLanguageType().hasUnspecifiedType(result, _) } abstract DataFlowType getType();
/** Gets the `CppType` of this base source variable. */
abstract CppType getLanguageType();
} }
final class BaseSourceVariable = AbstractBaseSourceVariable; class BaseIRVariable extends BaseSourceVariable, TBaseIRVariable {
class BaseIRVariable extends AbstractBaseSourceVariable, TBaseIRVariable {
IRVariable var; IRVariable var;
IRVariable getIRVariable() { result = var } IRVariable getIRVariable() { result = var }
@@ -395,19 +377,19 @@ class BaseIRVariable extends AbstractBaseSourceVariable, TBaseIRVariable {
override string toString() { result = var.toString() } override string toString() { result = var.toString() }
override CppType getLanguageType() { result = var.getLanguageType() } override DataFlowType getType() { result = var.getType() }
} }
class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable { class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable {
CallInstruction call; AllocationInstruction call;
BaseCallVariable() { this = TBaseCallVariable(call) } BaseCallVariable() { this = TBaseCallVariable(call) }
CallInstruction getCallInstruction() { result = call } AllocationInstruction getCallInstruction() { result = call }
override string toString() { result = call.toString() } override string toString() { result = call.toString() }
override CppType getLanguageType() { result = getResultLanguageType(call) } override DataFlowType getType() { result = call.getResultType() }
} }
/** /**
@@ -507,7 +489,8 @@ private class BaseIRVariableInstruction extends BaseSourceVariableInstruction,
override BaseIRVariable getBaseSourceVariable() { result.getIRVariable() = this.getIRVariable() } override BaseIRVariable getBaseSourceVariable() { result.getIRVariable() = this.getIRVariable() }
} }
private class BaseCallInstruction extends BaseSourceVariableInstruction, CallInstruction { private class BaseAllocationInstruction extends BaseSourceVariableInstruction, AllocationInstruction
{
override BaseCallVariable getBaseSourceVariable() { result.getCallInstruction() = this } override BaseCallVariable getBaseSourceVariable() { result.getCallInstruction() = this }
} }
@@ -555,7 +538,7 @@ private module Cached {
isDef(_, value, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and isDef(_, value, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
iteratorBase.getResultType() instanceof Interfaces::Iterator and iteratorBase.getResultType() instanceof Interfaces::Iterator and
isDereference(iteratorAddress.getDef(), read.getArgumentDef().getAUse(), _) and isDereference(iteratorAddress.getDef(), read.getArgumentDef().getAUse()) and
memory = read.getSideEffectOperand().getAnyDef() memory = read.getSideEffectOperand().getAnyDef()
) )
} }
@@ -595,6 +578,7 @@ private module Cached {
) )
} }
pragma[assume_small_delta]
private predicate convertsIntoArgumentRev(Instruction instr) { private predicate convertsIntoArgumentRev(Instruction instr) {
convertsIntoArgumentFwd(instr) and convertsIntoArgumentFwd(instr) and
( (
@@ -791,14 +775,11 @@ private module Cached {
* instead associated with the operand returned by this predicate. * instead associated with the operand returned by this predicate.
*/ */
cached cached
predicate hasIRRepresentationOfIndirectOperand( Operand getIRRepresentationOfIndirectOperand(Operand operand, int indirectionIndex) {
Operand operand, int indirectionIndex, Operand operandRepr, int indirectionIndexRepr
) {
indirectionIndex = [1 .. countIndirectionsForCppType(getLanguageType(operand))] and
exists(Instruction load | exists(Instruction load |
isDereference(load, operand, false) and isDereference(load, operand) and
operandRepr = unique( | | getAUse(load)) and result = unique( | | getAUse(load)) and
indirectionIndexRepr = indirectionIndex - 1 isUseImpl(operand, _, indirectionIndex - 1)
) )
} }
@@ -810,15 +791,12 @@ private module Cached {
* instead associated with the instruction returned by this predicate. * instead associated with the instruction returned by this predicate.
*/ */
cached cached
predicate hasIRRepresentationOfIndirectInstruction( Instruction getIRRepresentationOfIndirectInstruction(Instruction instr, int indirectionIndex) {
Instruction instr, int indirectionIndex, Instruction instrRepr, int indirectionIndexRepr
) {
indirectionIndex = [1 .. countIndirectionsForCppType(getResultLanguageType(instr))] and
exists(Instruction load, Operand address | exists(Instruction load, Operand address |
address.getDef() = instr and address.getDef() = instr and
isDereference(load, address, false) and isDereference(load, address) and
instrRepr = load and isUseImpl(address, _, indirectionIndex - 1) and
indirectionIndexRepr = indirectionIndex - 1 result = instr
) )
} }
@@ -839,7 +817,7 @@ private module Cached {
or or
exists(int ind0 | exists(int ind0 |
exists(Operand address | exists(Operand address |
isDereference(operand.getDef(), address, _) and isDereference(operand.getDef(), address) and
isUseImpl(address, base, ind0) isUseImpl(address, base, ind0)
) )
or or
@@ -881,7 +859,7 @@ private module Cached {
* to a specific address. * to a specific address.
*/ */
private predicate isCertainAddress(Operand operand) { private predicate isCertainAddress(Operand operand) {
valueNumberOfOperand(operand).getAnInstruction() instanceof VariableAddressInstruction operand.getDef() instanceof VariableAddressInstruction
or or
operand.getType() instanceof Cpp::ReferenceType operand.getType() instanceof Cpp::ReferenceType
} }
@@ -909,7 +887,7 @@ private module Cached {
) )
or or
exists(Operand address, boolean certain0 | exists(Operand address, boolean certain0 |
isDereference(operand.getDef(), address, _) and isDereference(operand.getDef(), address) and
isDefImpl(address, base, ind - 1, certain0) isDefImpl(address, base, ind - 1, certain0)
| |
if isCertainAddress(operand) then certain = certain0 else certain = false if isCertainAddress(operand) then certain = certain0 else certain = false

View File

@@ -1,10 +0,0 @@
/**
* Provides C++-specific definitions for use in the taint tracking library.
*/
private import codeql.dataflow.TaintTracking
private import DataFlowImplSpecific
module CppTaintTracking implements InputSig<CppDataFlow> {
import TaintTrackingUtil
}

View File

@@ -57,7 +57,7 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
) )
or or
// Taint flow from an address to its dereference. // Taint flow from an address to its dereference.
Ssa::isDereference(instrTo, opFrom, _) Ssa::isDereference(instrTo, opFrom)
or or
// Unary instructions tend to preserve enough information in practice that we // Unary instructions tend to preserve enough information in practice that we
// want taint to flow through. // want taint to flow through.
@@ -112,7 +112,7 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
* of `c` at sinks and inputs to additional taint steps. * of `c` at sinks and inputs to additional taint steps.
*/ */
bindingset[node] bindingset[node]
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() } predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
/** /**
* Holds if `node` should be a sanitizer in all global taint flow configurations * Holds if `node` should be a sanitizer in all global taint flow configurations
@@ -160,7 +160,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
FunctionInput modelIn, FunctionOutput modelOut FunctionInput modelIn, FunctionOutput modelOut
| |
indirectArgument = callInput(call, modelIn) and indirectArgument = callInput(call, modelIn) and
indirectArgument.hasAddressOperandAndIndirectionIndex(nodeIn.asOperand(), _) and indirectArgument.getAddressOperand() = nodeIn.asOperand() and
call.getStaticCallTarget() = func and call.getStaticCallTarget() = func and
( (
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)

View File

@@ -15,12 +15,15 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon private import semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon
private module SourceVariables { private module SourceVariables {
class SourceVariable extends BaseSourceVariable { class SourceVariable instanceof BaseSourceVariable {
/** string toString() { result = BaseSourceVariable.super.toString() }
* Gets the base source variable of this `SourceVariable`.
*/
BaseSourceVariable getBaseVariable() { result = this } BaseSourceVariable getBaseVariable() { result = this }
} }
class SourceIRVariable = BaseIRVariable;
class CallVariable = BaseCallVariable;
} }
import SourceVariables import SourceVariables

View File

@@ -0,0 +1,74 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
DataFlowInternal::FullStateConfigSig
{
import Config
predicate isBarrier(DataFlow::Node node) {
Config::isBarrier(node) or defaultTaintSanitizer(node)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
Config::isAdditionalFlowStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2)
}
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
Config::allowImplicitRead(node, c)
or
(
Config::isSink(node, _) or
Config::isAdditionalFlowStep(node, _) or
Config::isAdditionalFlowStep(node, _, _, _)
) and
defaultImplicitTaintRead(node, c)
}
}
/**
* Constructs a global taint tracking computation.
*/
module Global<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
private module Config0 implements DataFlowInternal::FullStateConfigSig {
import DataFlowInternal::DefaultState<Config>
import Config
}
private module C implements DataFlowInternal::FullStateConfigSig {
import AddTaintDefaults<Config0>
}
import DataFlowInternal::Impl<C>
}
/** DEPRECATED: Use `Global` instead. */
deprecated module Make<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
import Global<Config>
}
/**
* Constructs a global taint tracking computation using flow state.
*/
module GlobalWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
private module Config0 implements DataFlowInternal::FullStateConfigSig {
import Config
}
private module C implements DataFlowInternal::FullStateConfigSig {
import AddTaintDefaults<Config0>
}
import DataFlowInternal::Impl<C>
}
/** DEPRECATED: Use `GlobalWithState` instead. */
deprecated module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
import GlobalWithState<Config>
}

View File

@@ -55,7 +55,6 @@ private newtype TOpcode =
TVariableAddress() or TVariableAddress() or
TFieldAddress() or TFieldAddress() or
TFunctionAddress() or TFunctionAddress() or
TVirtualDeleteFunctionAddress() or
TElementsAddress() or TElementsAddress() or
TConstant() or TConstant() or
TStringConstant() or TStringConstant() or
@@ -888,15 +887,6 @@ module Opcode {
final override string toString() { result = "FunctionAddress" } final override string toString() { result = "FunctionAddress" }
} }
/**
* The `Opcode` for a `VirtualDeleteFunctionAddress`.
*
* See the `VirtualDeleteFunctionAddressInstruction` documentation for more details.
*/
class VirtualDeleteFunctionAddress extends Opcode, TVirtualDeleteFunctionAddress {
final override string toString() { result = "VirtualDeleteFunctionAddress" }
}
/** /**
* The `Opcode` for a `ConstantInstruction`. * The `Opcode` for a `ConstantInstruction`.
* *

View File

@@ -576,22 +576,6 @@ class FunctionAddressInstruction extends FunctionInstruction {
FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress } FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress }
} }
/**
* An instruction that returns the address of a "virtual" delete function.
*
* This function, which does not actually exist in the source code, is used to
* delete objects of a class with a virtual destructor. In that case the deacllocation
* function is selected at runtime based on the dynamic type of the object. So this
* function dynamically dispatches to the correct deallocation function.
* It also should pass in the required extra arguments to the deallocation function
* which may differ dynamically depending on the type of the object.
*/
class VirtualDeleteFunctionAddressInstruction extends Instruction {
VirtualDeleteFunctionAddressInstruction() {
this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress
}
}
/** /**
* An instruction that initializes a parameter of the enclosing function with the value of the * An instruction that initializes a parameter of the enclosing function with the value of the
* corresponding argument passed by the caller. * corresponding argument passed by the caller.

View File

@@ -4,8 +4,8 @@
* This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
* databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends * uses, however, it is better to write a query that imports `PrintIR.qll`, extends
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
* to dump. * dump.
*/ */
private import internal.IRInternal private import internal.IRInternal
@@ -16,7 +16,7 @@ import Imports::IRConfiguration
private newtype TPrintIRConfiguration = MkPrintIRConfiguration() private newtype TPrintIRConfiguration = MkPrintIRConfiguration()
/** /**
* The query can extend this class to control which declarations are printed. * The query can extend this class to control which functions are printed.
*/ */
class PrintIRConfiguration extends TPrintIRConfiguration { class PrintIRConfiguration extends TPrintIRConfiguration {
/** Gets a textual representation of this configuration. */ /** Gets a textual representation of this configuration. */
@@ -24,9 +24,9 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
/** /**
* Holds if the IR for `func` should be printed. By default, holds for all * Holds if the IR for `func` should be printed. By default, holds for all
* functions, global and namespace variables, and static local variables. * functions.
*/ */
predicate shouldPrintDeclaration(Language::Declaration decl) { any() } predicate shouldPrintFunction(Language::Declaration decl) { any() }
} }
/** /**
@@ -34,12 +34,12 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
*/ */
private class FilteredIRConfiguration extends IRConfiguration { private class FilteredIRConfiguration extends IRConfiguration {
override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) {
shouldPrintDeclaration(func) shouldPrintFunction(func)
} }
} }
private predicate shouldPrintDeclaration(Language::Declaration decl) { private predicate shouldPrintFunction(Language::Declaration decl) {
exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl)) exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
} }
private predicate shouldPrintInstruction(Instruction i) { private predicate shouldPrintInstruction(Instruction i) {
@@ -90,10 +90,10 @@ private string getOperandPropertyString(Operand operand) {
} }
private newtype TPrintableIRNode = private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
TPrintableInstruction(Instruction instr) { TPrintableInstruction(Instruction instr) {
shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction()) shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction())
} }
/** /**

View File

@@ -176,6 +176,7 @@ private predicate binaryValueNumber0(
) )
} }
pragma[assume_small_delta]
private predicate binaryValueNumber( private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand TValueNumber rightOperand
@@ -201,6 +202,7 @@ private predicate pointerArithmeticValueNumber0(
) )
} }
pragma[assume_small_delta]
private predicate pointerArithmeticValueNumber( private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize, PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand TValueNumber leftOperand, TValueNumber rightOperand
@@ -247,6 +249,7 @@ private predicate loadTotalOverlapValueNumber0(
) )
} }
pragma[assume_small_delta]
private predicate loadTotalOverlapValueNumber( private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand, LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand TValueNumber operand

View File

@@ -576,22 +576,6 @@ class FunctionAddressInstruction extends FunctionInstruction {
FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress } FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress }
} }
/**
* An instruction that returns the address of a "virtual" delete function.
*
* This function, which does not actually exist in the source code, is used to
* delete objects of a class with a virtual destructor. In that case the deacllocation
* function is selected at runtime based on the dynamic type of the object. So this
* function dynamically dispatches to the correct deallocation function.
* It also should pass in the required extra arguments to the deallocation function
* which may differ dynamically depending on the type of the object.
*/
class VirtualDeleteFunctionAddressInstruction extends Instruction {
VirtualDeleteFunctionAddressInstruction() {
this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress
}
}
/** /**
* An instruction that initializes a parameter of the enclosing function with the value of the * An instruction that initializes a parameter of the enclosing function with the value of the
* corresponding argument passed by the caller. * corresponding argument passed by the caller.

View File

@@ -4,8 +4,8 @@
* This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
* databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends * uses, however, it is better to write a query that imports `PrintIR.qll`, extends
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
* to dump. * dump.
*/ */
private import internal.IRInternal private import internal.IRInternal
@@ -16,7 +16,7 @@ import Imports::IRConfiguration
private newtype TPrintIRConfiguration = MkPrintIRConfiguration() private newtype TPrintIRConfiguration = MkPrintIRConfiguration()
/** /**
* The query can extend this class to control which declarations are printed. * The query can extend this class to control which functions are printed.
*/ */
class PrintIRConfiguration extends TPrintIRConfiguration { class PrintIRConfiguration extends TPrintIRConfiguration {
/** Gets a textual representation of this configuration. */ /** Gets a textual representation of this configuration. */
@@ -24,9 +24,9 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
/** /**
* Holds if the IR for `func` should be printed. By default, holds for all * Holds if the IR for `func` should be printed. By default, holds for all
* functions, global and namespace variables, and static local variables. * functions.
*/ */
predicate shouldPrintDeclaration(Language::Declaration decl) { any() } predicate shouldPrintFunction(Language::Declaration decl) { any() }
} }
/** /**
@@ -34,12 +34,12 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
*/ */
private class FilteredIRConfiguration extends IRConfiguration { private class FilteredIRConfiguration extends IRConfiguration {
override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) {
shouldPrintDeclaration(func) shouldPrintFunction(func)
} }
} }
private predicate shouldPrintDeclaration(Language::Declaration decl) { private predicate shouldPrintFunction(Language::Declaration decl) {
exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl)) exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
} }
private predicate shouldPrintInstruction(Instruction i) { private predicate shouldPrintInstruction(Instruction i) {
@@ -90,10 +90,10 @@ private string getOperandPropertyString(Operand operand) {
} }
private newtype TPrintableIRNode = private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
TPrintableInstruction(Instruction instr) { TPrintableInstruction(Instruction instr) {
shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction()) shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction())
} }
/** /**

View File

@@ -176,6 +176,7 @@ private predicate binaryValueNumber0(
) )
} }
pragma[assume_small_delta]
private predicate binaryValueNumber( private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand TValueNumber rightOperand
@@ -201,6 +202,7 @@ private predicate pointerArithmeticValueNumber0(
) )
} }
pragma[assume_small_delta]
private predicate pointerArithmeticValueNumber( private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize, PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand TValueNumber leftOperand, TValueNumber rightOperand
@@ -247,6 +249,7 @@ private predicate loadTotalOverlapValueNumber0(
) )
} }
pragma[assume_small_delta]
private predicate loadTotalOverlapValueNumber( private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand, LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand TValueNumber operand

View File

@@ -120,9 +120,9 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff
} }
/** /**
* A `Call` or `NewOrNewArrayExpr` or `DeleteOrDeleteArrayExpr`. * A `Call` or `NewOrNewArrayExpr`.
* *
* All kinds of expression invoke a function as part of their evaluation. This class provides a * Both kinds of expression invoke a function as part of their evaluation. This class provides a
* way to treat both kinds of function similarly, and to get the invoked `Function`. * way to treat both kinds of function similarly, and to get the invoked `Function`.
*/ */
class CallOrAllocationExpr extends Expr { class CallOrAllocationExpr extends Expr {
@@ -130,8 +130,6 @@ class CallOrAllocationExpr extends Expr {
this instanceof Call this instanceof Call
or or
this instanceof NewOrNewArrayExpr this instanceof NewOrNewArrayExpr
or
this instanceof DeleteOrDeleteArrayExpr
} }
/** Gets the `Function` invoked by this expression, if known. */ /** Gets the `Function` invoked by this expression, if known. */
@@ -139,8 +137,6 @@ class CallOrAllocationExpr extends Expr {
result = this.(Call).getTarget() result = this.(Call).getTarget()
or or
result = this.(NewOrNewArrayExpr).getAllocator() result = this.(NewOrNewArrayExpr).getAllocator()
or
result = this.(DeleteOrDeleteArrayExpr).getDeallocator()
} }
} }

View File

@@ -350,9 +350,6 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi
or or
expr instanceof NewOrNewArrayExpr and expr instanceof NewOrNewArrayExpr and
result = getTranslatedAllocatorCall(expr).getInstruction(CallTag()) result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
or
expr instanceof DeleteOrDeleteArrayExpr and
result = getTranslatedDeleteOrDeleteArray(expr).getInstruction(CallTag())
} }
} }

View File

@@ -77,17 +77,17 @@ private predicate ignoreExprAndDescendants(Expr expr) {
newExpr.getInitializer().getFullyConverted() = expr newExpr.getInitializer().getFullyConverted() = expr
) )
or or
exists(DeleteOrDeleteArrayExpr deleteExpr |
// Ignore the deallocator call, because we always synthesize it.
deleteExpr.getDeallocatorCall() = expr
)
or
// Do not translate input/output variables in GNU asm statements // Do not translate input/output variables in GNU asm statements
// getRealParent(expr) instanceof AsmStmt // getRealParent(expr) instanceof AsmStmt
// or // or
ignoreExprAndDescendants(getRealParent(expr)) // recursive case ignoreExprAndDescendants(getRealParent(expr)) // recursive case
or or
// va_start doesn't evaluate its argument, so we don't need to translate it. // We do not yet translate destructors properly, so for now we ignore any
// custom deallocator call, if present.
exists(DeleteExpr deleteExpr | deleteExpr.getAllocatorCall() = expr)
or
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getAllocatorCall() = expr)
or
exists(BuiltInVarArgsStart vaStartExpr | exists(BuiltInVarArgsStart vaStartExpr |
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
) )
@@ -104,12 +104,6 @@ private predicate ignoreExprOnly(Expr expr) {
newExpr.getAllocatorCall() = expr newExpr.getAllocatorCall() = expr
) )
or or
exists(DeleteOrDeleteArrayExpr deleteExpr |
// Ignore the destructor call as we don't model it yet. Don't ignore
// its arguments, though, as they are the arguments to the deallocator.
deleteExpr.getDestructorCall() = expr
)
or
// The extractor deliberately emits an `ErrorExpr` as the first argument to // The extractor deliberately emits an `ErrorExpr` as the first argument to
// the allocator call, if any, of a `NewOrNewArrayExpr`. That `ErrorExpr` // the allocator call, if any, of a `NewOrNewArrayExpr`. That `ErrorExpr`
// should not be translated. // should not be translated.
@@ -117,6 +111,13 @@ private predicate ignoreExprOnly(Expr expr) {
or or
not translateFunction(getEnclosingFunction(expr)) and not translateFunction(getEnclosingFunction(expr)) and
not Raw::varHasIRFunc(getEnclosingVariable(expr)) not Raw::varHasIRFunc(getEnclosingVariable(expr))
or
// We do not yet translate destructors properly, so for now we ignore the
// destructor call. We do, however, translate the expression being
// destructed, and that expression can be a child of the destructor call.
exists(DeleteExpr deleteExpr | deleteExpr.getDestructorCall() = expr)
or
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getDestructorCall() = expr)
} }
/** /**
@@ -415,9 +416,7 @@ predicate hasTranslatedLoad(Expr expr) {
not ignoreExpr(expr) and not ignoreExpr(expr) and
not isNativeCondition(expr) and not isNativeCondition(expr) and
not isFlexibleCondition(expr) and not isFlexibleCondition(expr) and
not ignoreLoad(expr) and not ignoreLoad(expr)
// don't insert a load since we'll just substitute the constant value.
not isIRConstant(expr)
} }
/** /**

View File

@@ -991,19 +991,9 @@ class TranslatedStructuredBindingVariableAccess extends TranslatedNonConstantExp
class TranslatedFunctionAccess extends TranslatedNonConstantExpr { class TranslatedFunctionAccess extends TranslatedNonConstantExpr {
override FunctionAccess expr; override FunctionAccess expr;
override TranslatedElement getChild(int id) { override TranslatedElement getChild(int id) { none() }
id = 0 and result = this.getQualifier() // Might not exist
}
final TranslatedExpr getQualifier() { override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
result = getTranslatedExpr(expr.getQualifier().getFullyConverted())
}
override Instruction getFirstInstruction() {
if exists(this.getQualifier())
then result = this.getQualifier().getFirstInstruction()
else result = this.getInstruction(OnlyInstructionTag())
}
override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) }
@@ -1024,9 +1014,7 @@ class TranslatedFunctionAccess extends TranslatedNonConstantExpr {
result = expr.getTarget() result = expr.getTarget()
} }
override Instruction getChildSuccessor(TranslatedElement child) { override Instruction getChildSuccessor(TranslatedElement child) { none() }
child = this.getQualifier() and result = this.getInstruction(OnlyInstructionTag())
}
} }
/** /**
@@ -2017,66 +2005,6 @@ TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
result.getAst() = newExpr result.getAst() = newExpr
} }
/**
* The IR translation of a `delete` or `delete[]`
* expression.
*/
class TranslatedDeleteOrDeleteArrayExpr extends TranslatedNonConstantExpr, TranslatedCall {
override DeleteOrDeleteArrayExpr expr;
final override Instruction getFirstCallTargetInstruction() {
result = this.getInstruction(CallTargetTag())
}
final override Instruction getCallTargetResult() { result = this.getInstruction(CallTargetTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
TranslatedCall.super.hasInstruction(opcode, tag, resultType)
or
tag = CallTargetTag() and
resultType = getFunctionGLValueType() and
if exists(expr.getDeallocator())
then opcode instanceof Opcode::FunctionAddress
else opcode instanceof Opcode::VirtualDeleteFunctionAddress
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
result = TranslatedCall.super.getInstructionSuccessor(tag, kind)
or
tag = CallTargetTag() and
kind instanceof GotoEdge and
result = this.getFirstArgumentOrCallInstruction()
}
override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = expr.getDeallocator()
}
final override Type getCallResultType() { result = expr.getType() }
final override TranslatedExpr getQualifier() { none() }
final override predicate hasArguments() {
// All deallocator calls have at least one argument.
any()
}
final override int getNumberOfArguments() {
// We ignore the other arguments for now as we would have to synthesize them.
result = 1
}
final override TranslatedExpr getArgument(int index) {
// The only argument we define is the pointer to be deallocated.
index = 0 and
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
}
}
TranslatedDeleteOrDeleteArrayExpr getTranslatedDeleteOrDeleteArray(DeleteOrDeleteArrayExpr newExpr) {
result.getAst() = newExpr
}
/** /**
* Abstract class implemented by any `TranslatedElement` that has a child * Abstract class implemented by any `TranslatedElement` that has a child
* expression that is a call to a constructor or destructor, in order to * expression that is a call to a constructor or destructor, in order to
@@ -3014,6 +2942,78 @@ class TranslatedNewArrayExpr extends TranslatedNewOrNewArrayExpr {
} }
} }
/**
* A placeholder for the translation of a `delete[]` expression.
*
* Proper translation is not yet implemented, but this stub implementation
* ensures that code following a `delete[]` is not unreachable.
*/
class TranslatedDeleteArrayExprPlaceHolder extends TranslatedSingleInstructionExpr {
override DeleteArrayExpr expr;
final override Instruction getFirstInstruction() {
result = this.getOperand().getFirstInstruction()
}
final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() }
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = this.getOperand() and result = this.getInstruction(OnlyInstructionTag())
}
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
none()
}
final override Opcode getOpcode() { result instanceof Opcode::NoOp }
private TranslatedExpr getOperand() {
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
}
}
/**
* A placeholder for the translation of a `delete` expression.
*
* Proper translation is not yet implemented, but this stub implementation
* ensures that code following a `delete` is not unreachable.
*/
class TranslatedDeleteExprPlaceHolder extends TranslatedSingleInstructionExpr {
override DeleteExpr expr;
final override Instruction getFirstInstruction() {
result = this.getOperand().getFirstInstruction()
}
final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() }
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = this.getOperand() and result = this.getInstruction(OnlyInstructionTag())
}
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
none()
}
final override Opcode getOpcode() { result instanceof Opcode::NoOp }
private TranslatedExpr getOperand() {
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
}
}
/** /**
* The IR translation of a `ConditionDeclExpr`, which represents the value of the declared variable * The IR translation of a `ConditionDeclExpr`, which represents the value of the declared variable
* after conversion to `bool` in code such as: * after conversion to `bool` in code such as:

View File

@@ -576,22 +576,6 @@ class FunctionAddressInstruction extends FunctionInstruction {
FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress } FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress }
} }
/**
* An instruction that returns the address of a "virtual" delete function.
*
* This function, which does not actually exist in the source code, is used to
* delete objects of a class with a virtual destructor. In that case the deacllocation
* function is selected at runtime based on the dynamic type of the object. So this
* function dynamically dispatches to the correct deallocation function.
* It also should pass in the required extra arguments to the deallocation function
* which may differ dynamically depending on the type of the object.
*/
class VirtualDeleteFunctionAddressInstruction extends Instruction {
VirtualDeleteFunctionAddressInstruction() {
this.getOpcode() instanceof Opcode::VirtualDeleteFunctionAddress
}
}
/** /**
* An instruction that initializes a parameter of the enclosing function with the value of the * An instruction that initializes a parameter of the enclosing function with the value of the
* corresponding argument passed by the caller. * corresponding argument passed by the caller.

View File

@@ -4,8 +4,8 @@
* This file contains the actual implementation of `PrintIR.ql`. For test cases and very small * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
* databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends * uses, however, it is better to write a query that imports `PrintIR.qll`, extends
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
* to dump. * dump.
*/ */
private import internal.IRInternal private import internal.IRInternal
@@ -16,7 +16,7 @@ import Imports::IRConfiguration
private newtype TPrintIRConfiguration = MkPrintIRConfiguration() private newtype TPrintIRConfiguration = MkPrintIRConfiguration()
/** /**
* The query can extend this class to control which declarations are printed. * The query can extend this class to control which functions are printed.
*/ */
class PrintIRConfiguration extends TPrintIRConfiguration { class PrintIRConfiguration extends TPrintIRConfiguration {
/** Gets a textual representation of this configuration. */ /** Gets a textual representation of this configuration. */
@@ -24,9 +24,9 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
/** /**
* Holds if the IR for `func` should be printed. By default, holds for all * Holds if the IR for `func` should be printed. By default, holds for all
* functions, global and namespace variables, and static local variables. * functions.
*/ */
predicate shouldPrintDeclaration(Language::Declaration decl) { any() } predicate shouldPrintFunction(Language::Declaration decl) { any() }
} }
/** /**
@@ -34,12 +34,12 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
*/ */
private class FilteredIRConfiguration extends IRConfiguration { private class FilteredIRConfiguration extends IRConfiguration {
override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) {
shouldPrintDeclaration(func) shouldPrintFunction(func)
} }
} }
private predicate shouldPrintDeclaration(Language::Declaration decl) { private predicate shouldPrintFunction(Language::Declaration decl) {
exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl)) exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
} }
private predicate shouldPrintInstruction(Instruction i) { private predicate shouldPrintInstruction(Instruction i) {
@@ -90,10 +90,10 @@ private string getOperandPropertyString(Operand operand) {
} }
private newtype TPrintableIRNode = private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
TPrintableInstruction(Instruction instr) { TPrintableInstruction(Instruction instr) {
shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction()) shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction())
} }
/** /**

View File

@@ -176,6 +176,7 @@ private predicate binaryValueNumber0(
) )
} }
pragma[assume_small_delta]
private predicate binaryValueNumber( private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand TValueNumber rightOperand
@@ -201,6 +202,7 @@ private predicate pointerArithmeticValueNumber0(
) )
} }
pragma[assume_small_delta]
private predicate pointerArithmeticValueNumber( private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize, PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand TValueNumber leftOperand, TValueNumber rightOperand
@@ -247,6 +249,7 @@ private predicate loadTotalOverlapValueNumber0(
) )
} }
pragma[assume_small_delta]
private predicate loadTotalOverlapValueNumber( private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand, LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand TValueNumber operand

View File

@@ -108,7 +108,7 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
// these may do only a partial copy of the input buffer to the output // these may do only a partial copy of the input buffer to the output
// buffer // buffer
exists(this.getParamSize()) and exists(this.getParamSize()) and
input.isParameterDeref(this.getParamSrc()) and input.isParameter(this.getParamSrc()) and
( (
output.isParameterDeref(this.getParamDest()) or output.isParameterDeref(this.getParamDest()) or
output.isReturnValueDeref() output.isReturnValueDeref()

View File

@@ -1,57 +0,0 @@
/**
* This file contains the range-analysis specific parts of the `cpp/invalid-pointer-deref`
* and `cpp/overrun-write` query.
*/
private import cpp
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysis
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific
private import semmle.code.cpp.ir.IR
pragma[nomagic]
private Instruction getABoundIn(SemBound b, IRFunction func) {
getSemanticExpr(result) = b.getExpr(0) and
result.getEnclosingIRFunction() = func
}
/**
* Holds if `i <= b + delta`.
*/
pragma[inline]
private predicate boundedImplCand(Instruction i, Instruction b, int delta) {
exists(SemBound bound, IRFunction func |
semBounded(getSemanticExpr(i), bound, delta, true, _) and
b = getABoundIn(bound, func) and
i.getEnclosingIRFunction() = func
)
}
/**
* Holds if `i <= b + delta` and `delta` is the smallest integer that satisfies
* this condition.
*/
pragma[inline]
private predicate boundedImpl(Instruction i, Instruction b, int delta) {
delta = min(int cand | boundedImplCand(i, b, cand))
}
/**
* Holds if `i <= b + delta`.
*
* This predicate enforces a join-order that ensures that `i` has already been bound.
*/
bindingset[i]
pragma[inline_late]
predicate bounded1(Instruction i, Instruction b, int delta) { boundedImpl(i, b, delta) }
/**
* Holds if `i <= b + delta`.
*
* This predicate enforces a join-order that ensures that `b` has already been bound.
*/
bindingset[b]
pragma[inline_late]
predicate bounded2(Instruction i, Instruction b, int delta) { boundedImpl(i, b, delta) }
/** Holds if `i <= b + delta`. */
predicate bounded = boundedImpl/3;

View File

@@ -188,9 +188,6 @@ module SemanticExprConfig {
none() none()
} }
/** Holds if no range analysis should be performed on the phi edges in `f`. */
private predicate excludeFunction(Cpp::Function f) { count(f.getEntryPoint()) > 1 }
SemType getUnknownExprType(Expr expr) { result = getSemanticType(expr.getResultIRType()) } SemType getUnknownExprType(Expr expr) { result = getSemanticType(expr.getResultIRType()) }
class BasicBlock = IR::IRBlock; class BasicBlock = IR::IRBlock;
@@ -273,13 +270,7 @@ module SemanticExprConfig {
getSemanticExpr(v.asInstruction()) = sourceExpr getSemanticExpr(v.asInstruction()) = sourceExpr
} }
predicate phi(SsaVariable v) { predicate phi(SsaVariable v) { v.asInstruction() instanceof IR::PhiInstruction }
exists(IR::PhiInstruction phi, Cpp::Function f |
phi = v.asInstruction() and
f = phi.getEnclosingFunction() and
not excludeFunction(f)
)
}
SsaVariable getAPhiInput(SsaVariable v) { SsaVariable getAPhiInput(SsaVariable v) {
exists(IR::PhiInstruction instr | v.asInstruction() = instr | exists(IR::PhiInstruction instr | v.asInstruction() = instr |

View File

@@ -39,7 +39,6 @@ predicate semImplies_v2(SemGuard g1, boolean b1, SemGuard g2, boolean b2) {
* Holds if `guard` directly controls the position `controlled` with the * Holds if `guard` directly controls the position `controlled` with the
* value `testIsTrue`. * value `testIsTrue`.
*/ */
pragma[nomagic]
predicate semGuardDirectlyControlsSsaRead( predicate semGuardDirectlyControlsSsaRead(
SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue
) { ) {

Some files were not shown because too many files have changed in this diff Show More