Merge branch 'main' into maikypedia/ldap-injection

This commit is contained in:
Alex Ford
2023-07-14 12:05:17 +01:00
committed by GitHub
3221 changed files with 143975 additions and 54664 deletions

View File

@@ -1,3 +1,9 @@
build --repo_env=CC=clang --repo_env=CXX=clang++ --cxxopt="-std=c++17"
common --enable_platform_specific_config
build --repo_env=CC=clang --repo_env=CXX=clang++
build:linux --cxxopt=-std=c++20
build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
try-import %workspace%/local.bazelrc

3
.github/labeler.yml vendored
View File

@@ -11,7 +11,7 @@ Go:
- change-notes/**/*go.*
Java:
- any: [ 'java/**/*', '!java/kotlin-extractor/**/*', '!java/kotlin-explorer/**/*', '!java/ql/test/kotlin/**/*' ]
- any: [ 'java/**/*', '!java/kotlin-extractor/**/*', '!java/ql/test/kotlin/**/*' ]
- change-notes/**/*java.*
JS:
@@ -20,7 +20,6 @@ JS:
Kotlin:
- java/kotlin-extractor/**/*
- java/kotlin-explorer/**/*
- java/ql/test/kotlin/**/*
Python:

View File

@@ -11,7 +11,6 @@ on:
- "*/ql/lib/**/*.yml"
- "!**/experimental/**"
- "!ql/**"
- "!swift/**"
- ".github/workflows/check-change-note.yml"
jobs:
@@ -27,9 +26,9 @@ jobs:
run: |
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' |
grep true -c
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md' or 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text.
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md', 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text, or released/x.y.z.md for released change-notes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$"))' |
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$"))' |
grep true -c

View File

@@ -0,0 +1,29 @@
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

@@ -10,6 +10,7 @@ on:
- "*/ql/src/**/*.qll"
- "*/ql/lib/**/*.ql"
- "*/ql/lib/**/*.qll"
- "*/ql/lib/ext/**/*.yml"
- "misc/scripts/library-coverage/*.py"
# input data files
- "*/documentation/library-coverage/cwe-sink.csv"

View File

@@ -32,7 +32,7 @@ jobs:
path: |
ql/extractor-pack/
ql/target/release/buramu
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ql/**/*.rs') }}
- name: Cache cargo
if: steps.cache-extractor.outputs.cache-hit != 'true'
uses: actions/cache@v3

View File

@@ -61,7 +61,7 @@ jobs:
ruby/extractor/target/release/codeql-extractor-ruby
ruby/extractor/target/release/codeql-extractor-ruby.exe
ruby/extractor/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}--${{ hashFiles('ruby/extractor/**/*.rs') }}
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ruby/extractor/**/*.rs') }}
- uses: actions/cache@v3
if: steps.cache-extractor.outputs.cache-hit != 'true'
with:

View File

@@ -16,6 +16,7 @@ on:
branches:
- main
- rc/*
- codeql-cli-*
push:
paths:
- "swift/**"
@@ -30,6 +31,7 @@ on:
branches:
- main
- rc/*
- codeql-cli-*
jobs:
# not using a matrix as you cannot depend on a specific job in a matrix, and we want to start linux checks

View File

@@ -17,4 +17,6 @@ jobs:
- uses: actions/checkout@v3
- name: Check synchronized files
run: python config/sync-files.py
- name: Check dbscheme fragments
run: python config/sync-dbscheme-fragments.py

View File

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

18
.vscode/tasks.json vendored
View File

@@ -22,6 +22,22 @@
"command": "${config:python.pythonPath}",
},
"problemMatcher": []
},
{
"label": "Accept .expected changes from CI",
"type": "process",
// Non-Windows OS will usually have Python 3 already installed at /usr/bin/python3.
"command": "python3",
"args": [
"misc/scripts/accept-expected-changes-from-ci.py"
],
"group": "build",
"windows": {
// On Windows, use whatever Python interpreter is configured for this workspace. The default is
// just `python`, so if Python is already on the path, this will find it.
"command": "${config:python.pythonPath}",
},
"problemMatcher": []
}
]
}
}

View File

@@ -8,7 +8,6 @@
/swift/ @github/codeql-swift
/misc/codegen/ @github/codeql-swift
/java/kotlin-extractor/ @github/codeql-kotlin
/java/kotlin-explorer/ @github/codeql-kotlin
# ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
@@ -40,3 +39,6 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
/.github/workflows/ruby-* @github/codeql-ruby
/.github/workflows/swift.yml @github/codeql-swift
# Misc
/misc/scripts/accept-expected-changes-from-ci.py @RasmusWL

View File

@@ -14,14 +14,16 @@ If you have an idea for a query that you would like to share with other CodeQL u
1. **Directory structure**
There are six language-specific query directories in this repository:
There are eight language-specific query directories in this repository:
* C/C++: `cpp/ql/src`
* C#: `csharp/ql/src`
* Java: `java/ql/src`
* Go: `go/ql/src`
* Java/Kotlin: `java/ql/src`
* JavaScript: `javascript/ql/src`
* Python: `python/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.
- 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

@@ -0,0 +1,33 @@
{
"files": [
"javascript/ql/lib/semmlecode.javascript.dbscheme",
"python/ql/lib/semmlecode.python.dbscheme",
"ruby/ql/lib/ruby.dbscheme",
"ql/ql/src/ql.dbscheme"
],
"fragments": [
"/*- External data -*/",
"/*- Files and folders -*/",
"/*- Diagnostic messages -*/",
"/*- Diagnostic messages: severity -*/",
"/*- Source location prefix -*/",
"/*- Lines of code -*/",
"/*- Configuration files with key value pairs -*/",
"/*- YAML -*/",
"/*- XML Files -*/",
"/*- XML: sourceline -*/",
"/*- DEPRECATED: External defects and metrics -*/",
"/*- DEPRECATED: Snapshot date -*/",
"/*- DEPRECATED: Duplicate code -*/",
"/*- DEPRECATED: Version control data -*/",
"/*- JavaScript-specific part -*/",
"/*- Ruby dbscheme -*/",
"/*- Erb dbscheme -*/",
"/*- QL dbscheme -*/",
"/*- Dbscheme dbscheme -*/",
"/*- Yaml dbscheme -*/",
"/*- Blame dbscheme -*/",
"/*- JSON dbscheme -*/",
"/*- Python dbscheme -*/"
]
}

View File

@@ -511,7 +511,8 @@
"SensitiveDataHeuristics Python/JS": [
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll",
"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"
],
"CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
@@ -522,6 +523,10 @@
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
],
"SummaryTypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
],
"AccessPathSyntax": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
@@ -598,4 +603,4 @@
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
]
}
}

View File

@@ -0,0 +1,86 @@
#!/usr/bin/env python3
import argparse
import json
import os
import pathlib
import re
def make_groups(blocks):
groups = {}
for block in blocks:
groups.setdefault("".join(block["lines"]), []).append(block)
return list(groups.values())
def validate_fragments(fragments):
ok = True
for header, blocks in fragments.items():
groups = make_groups(blocks)
if len(groups) > 1:
ok = False
print("Warning: dbscheme fragments with header '{}' are different for {}".format(header, ["{}:{}:{}".format(
group[0]["file"], group[0]["start"], group[0]["end"]) for group in groups]))
return ok
def main():
script_path = os.path.realpath(__file__)
script_dir = os.path.dirname(script_path)
parser = argparse.ArgumentParser(
prog=os.path.basename(script_path),
description='Sync dbscheme fragments across files.'
)
parser.add_argument('files', metavar='dbscheme_file', type=pathlib.Path, nargs='*', default=[],
help='dbscheme files to check')
args = parser.parse_args()
with open(os.path.join(script_dir, "dbscheme-fragments.json"), "r") as f:
config = json.load(f)
fragment_headers = set(config["fragments"])
fragments = {}
ok = True
for file in args.files + config["files"]:
with open(os.path.join(os.path.dirname(script_dir), file), "r") as dbscheme:
header = None
line_number = 1
block = {"file": file, "start": line_number,
"end": None, "lines": []}
def end_block():
block["end"] = line_number - 1
if len(block["lines"]) > 0:
if header is None:
if re.match(r'(?m)\A(\s|//.*$|/\*(\**[^\*])*\*+/)*\Z', "".join(block["lines"])):
# Ignore comments at the beginning of the file
pass
else:
ok = False
print("Warning: dbscheme fragment without header: {}:{}:{}".format(
block["file"], block["start"], block["end"]))
else:
fragments.setdefault(header, []).append(block)
for line in dbscheme:
m = re.match(r"^\/\*-.*-\*\/$", line)
if m:
end_block()
header = line.strip()
if header not in fragment_headers:
ok = False
print("Warning: unknown header for dbscheme fragment: '{}': {}:{}".format(
header, file, line_number))
block = {"file": file, "start": line_number,
"end": None, "lines": []}
block["lines"].append(line)
line_number += 1
block["lines"].append('\n')
line_number += 1
end_block()
if not ok or not validate_fragments(fragments):
exit(1)
if __name__ == "__main__":
main()

View File

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

View File

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

View File

@@ -1,3 +1,41 @@
## 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
### Minor Analysis Improvements
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
* Deleted the deprecated `CodeDuplication.qll` file.
## 0.7.2
### New Features
* Added an AST-based interface (`semmle.code.cpp.rangeanalysis.new.RangeAnalysis`) for the relative range analysis library.
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.
### Major Analysis Improvements
* In the intermediate representation, handling of control flow after non-returning calls has been improved. This should remove false positives in queries that use the intermedite representation or libraries based on it, including the new data flow library.
### Minor Analysis Improvements
* The `StdNamespace` class now also includes all inline namespaces that are children of `std` namespace.
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.
## 0.7.1
No user-facing changes.

View File

@@ -1,4 +0,0 @@
---
category: feature
---
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.

View File

@@ -1,4 +0,0 @@
---
category: majorAnalysis
---
* In the intermediate representation, handling of control flow after non-returning calls has been improved. This should remove false positives in queries that use the intermedite representation or libraries based on it, including the new data flow library.

View File

@@ -1,4 +0,0 @@
---
category: feature
---
* Added an AST-based interface (`semmle.code.cpp.rangeanalysis.new.RangeAnalysis`) for the relative range analysis library.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `IRGuards` library has improved handling of pointer addition and subtraction operations.

View File

@@ -0,0 +1,6 @@
---
category: feature
---
* 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.

View File

@@ -0,0 +1,15 @@
## 0.7.2
### New Features
* Added an AST-based interface (`semmle.code.cpp.rangeanalysis.new.RangeAnalysis`) for the relative range analysis library.
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.
### Major Analysis Improvements
* In the intermediate representation, handling of control flow after non-returning calls has been improved. This should remove false positives in queries that use the intermedite representation or libraries based on it, including the new data flow library.
### Minor Analysis Improvements
* The `StdNamespace` class now also includes all inline namespaces that are children of `std` namespace.
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.

View File

@@ -0,0 +1,7 @@
## 0.7.3
### Minor Analysis Improvements
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
* Deleted the deprecated `CodeDuplication.qll` file.

View File

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

View File

@@ -0,0 +1,9 @@
## 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,2 +1,2 @@
---
lastReleaseVersion: 0.7.1
lastReleaseVersion: 0.8.0

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 0.7.2-dev
version: 0.8.1-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -176,20 +176,6 @@ class Class extends UserType {
/** Holds if this class, struct or union has a constructor. */
predicate hasConstructor() { exists(this.getAConstructor()) }
/**
* Holds if this class has a copy constructor that is either explicitly
* declared (though possibly `= delete`) or is auto-generated, non-trivial
* and called from somewhere.
*
* DEPRECATED: There is more than one reasonable definition of what it means
* to have a copy constructor, and we do not want to promote one particular
* definition by naming it with this predicate. Having a copy constructor
* could mean that such a member is declared or defined in the source or that
* it is callable by a particular caller. For C++11, there's also a question
* of whether to include members that are defaulted or deleted.
*/
deprecated predicate hasCopyConstructor() { this.getAMemberFunction() instanceof CopyConstructor }
/**
* Like accessOfBaseMember but returns multiple results if there are multiple
* paths to `base` through the inheritance graph.

View File

@@ -34,14 +34,6 @@ class Container extends Locatable, @container {
*/
string getAbsolutePath() { none() } // overridden by subclasses
/**
* DEPRECATED: Use `getLocation` instead.
* Gets a URL representing the location of this container.
*
* For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls).
*/
deprecated string getURL() { none() } // overridden by subclasses
/**
* Gets the relative path of this file or folder from the root folder of the
* analyzed source location. The relative path of the root folder itself is
@@ -183,12 +175,6 @@ class Folder extends Container, @folder {
}
override string getAPrimaryQlClass() { result = "Folder" }
/**
* DEPRECATED: Use `getLocation` instead.
* Gets the URL of this folder.
*/
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
}
/**
@@ -213,12 +199,6 @@ class File extends Container, @file {
result.hasLocationInfo(_, 0, 0, 0, 0)
}
/**
* DEPRECATED: Use `getLocation` instead.
* Gets the URL of this file.
*/
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
/** Holds if this file was compiled as C (at any point). */
predicate compiledAsC() { fileannotations(underlyingElement(this), 1, "compiled as c", "1") }

View File

@@ -34,7 +34,7 @@ class Macro extends PreprocessorDirective, @ppd_define {
* Gets the name of the macro. For example, `MAX` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
*/
string getName() { result = this.getHead().splitAt("(", 0) }
string getName() { result = this.getHead().regexpCapture("([^(]*+).*", 1) }
/** Holds if the macro has name `name`. */
predicate hasName(string name) { this.getName() = name }

View File

@@ -230,8 +230,12 @@ class GlobalNamespace extends Namespace {
}
/**
* The C++ `std::` namespace.
* The C++ `std::` namespace and its inline namespaces.
*/
class StdNamespace extends Namespace {
StdNamespace() { this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace }
StdNamespace() {
this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace
or
this.isInline() and this.getParentNamespace() instanceof StdNamespace
}
}

View File

@@ -27,9 +27,6 @@ class PrintAstConfiguration extends TPrintAstConfiguration {
predicate shouldPrintFunction(Function func) { any() }
}
/** DEPRECATED: Alias for PrintAstConfiguration */
deprecated class PrintASTConfiguration = PrintAstConfiguration;
private predicate shouldPrintFunction(Function func) {
exists(PrintAstConfiguration config | config.shouldPrintFunction(func))
}
@@ -239,9 +236,6 @@ class PrintAstNode extends TPrintAstNode {
}
}
/** DEPRECATED: Alias for PrintAstNode */
deprecated class PrintASTNode = PrintAstNode;
/**
* Class that restricts the elements that we compute `qlClass` for.
*/
@@ -286,9 +280,6 @@ abstract class BaseAstNode extends PrintAstNode {
deprecated Locatable getAST() { result = this.getAst() }
}
/** DEPRECATED: Alias for BaseAstNode */
deprecated class BaseASTNode = BaseAstNode;
/**
* A node representing an AST node other than a `DeclarationEntry`.
*/
@@ -296,9 +287,6 @@ abstract class AstNode extends BaseAstNode, TAstNode {
AstNode() { this = TAstNode(ast) }
}
/** DEPRECATED: Alias for AstNode */
deprecated class ASTNode = AstNode;
/**
* A node representing an `Expr`.
*/

View File

@@ -1699,7 +1699,28 @@ class AutoType extends TemplateParameter {
private predicate suppressUnusedThis(Type t) { any() }
/** A source code location referring to a type */
/**
* A source code location referring to a user-defined type.
*
* Note that only _user-defined_ types have `TypeMention`s. In particular,
* built-in types, and derived types with built-in types as their base don't
* have any `TypeMention`s. For example, given
* ```cpp
* struct S { ... };
* void f(S s1, int i1) {
* S s2;
* S* s3;
* S& s4 = s2;
* decltype(s2) s5;
*
* int i2;
* int* i3;
* int i4[10];
* }
* ```
* there will be a `TypeMention` for the mention of `S` at `S s1`, `S s2`, and `S& s4 = s2`,
* but not at `decltype(s2) s5`. Additionally, there will be no `TypeMention`s for `int`.
*/
class TypeMention extends Locatable, @type_mention {
override string toString() { result = "type mention" }

View File

@@ -627,6 +627,20 @@ private predicate sub_lt(
x = int_value(rhs.getRight()) and
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)
@@ -653,6 +667,26 @@ private predicate add_lt(
) and
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)
@@ -673,6 +707,20 @@ private predicate sub_eq(
x = int_value(rhs.getRight()) and
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)
@@ -699,6 +747,26 @@ private predicate add_eq(
) and
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. */

View File

@@ -14,9 +14,6 @@ library class StandardSsa extends SsaHelper {
StandardSsa() { this = 0 }
}
/** DEPRECATED: Alias for StandardSsa */
deprecated class StandardSSA = StandardSsa;
/**
* A definition of one or more SSA variables, including phi node definitions.
* An _SSA variable_, as defined in the literature, is effectively the pair of

View File

@@ -312,6 +312,3 @@ library class SsaHelper extends int {
ssa_use(v, result, _, _)
}
}
/** DEPRECATED: Alias for SsaHelper */
deprecated class SSAHelper = SsaHelper;

View File

@@ -1385,9 +1385,6 @@ private module Cached {
conditionalSuccessor(n1, _, n2)
}
/** DEPRECATED: Alias for qlCfgSuccessor */
deprecated predicate qlCFGSuccessor = qlCfgSuccessor/2;
/**
* Holds if `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is true.
@@ -1398,9 +1395,6 @@ private module Cached {
not conditionalSuccessor(n1, false, n2)
}
/** DEPRECATED: Alias for qlCfgTrueSuccessor */
deprecated predicate qlCFGTrueSuccessor = qlCfgTrueSuccessor/2;
/**
* Holds if `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is false.
@@ -1410,7 +1404,4 @@ private module Cached {
conditionalSuccessor(n1, false, n2) and
not conditionalSuccessor(n1, true, n2)
}
/** DEPRECATED: Alias for qlCfgFalseSuccessor */
deprecated predicate qlCFGFalseSuccessor = qlCfgFalseSuccessor/2;
}

View File

@@ -114,7 +114,7 @@ signature module StateConfigSig {
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state);
default predicate isBarrier(Node node, FlowState state) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrierIn(Node node) { none() }
@@ -131,7 +131,9 @@ signature module StateConfigSig {
* 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);
default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
}
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be

View File

@@ -254,6 +254,11 @@ module Impl<FullStateConfigSig Config> {
not fullBarrier(node2)
}
pragma[nomagic]
private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) {
isUnreachableInCallCached(n.asNode(), cc.getCall())
}
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
@@ -460,7 +465,6 @@ module Impl<FullStateConfigSig Config> {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
pragma[assume_small_delta]
private predicate fwdFlow(NodeEx node, Cc cc) {
sourceNode(node, _) and
if hasSourceCallCtx() then cc = true else cc = false
@@ -570,7 +574,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Holds if `c` is the target of a store in the flow covered by `fwdFlow`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlowConsCand(Content c) {
exists(NodeEx mid, NodeEx node |
@@ -1135,8 +1138,8 @@ module Impl<FullStateConfigSig Config> {
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow
);
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap);
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t);
bindingset[typ, contentType]
predicate typecheckStore(Typ typ, DataFlowType contentType);
@@ -1199,20 +1202,23 @@ module Impl<FullStateConfigSig Config> {
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
ApOption argAp, Typ t, Ap ap, ApApprox apa
) {
fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t, ap, apa) and
PrevStage::revFlow(node, state, apa) and
filter(node, state, t, ap)
fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa)
}
pragma[inline]
additional predicate fwdFlow(
private predicate fwdFlow1(
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
ApOption argAp, Typ t, Ap ap
ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa
) {
fwdFlow(node, state, cc, summaryCtx, argT, argAp, t, ap, _)
fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and
PrevStage::revFlow(node, state, apa) and
filter(node, state, t0, ap, t)
}
pragma[nomagic]
private predicate typeStrengthen(Typ t0, Ap ap, Typ t) {
fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
@@ -1339,6 +1345,11 @@ module Impl<FullStateConfigSig Config> {
private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) {
fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and
cons = apCons(c, t1, tail)
or
exists(Typ t0 |
typeStrengthen(t0, cons, t2) and
fwdFlowConsCand(t0, cons, c, t1, tail)
)
}
pragma[nomagic]
@@ -1359,7 +1370,7 @@ module Impl<FullStateConfigSig Config> {
ParamNodeOption summaryCtx, TypOption argT, ApOption argAp
) {
exists(ApHeadContent apc |
fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap) and
fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and
apc = getHeadContent(ap) and
readStepCand0(node1, apc, c, node2)
)
@@ -1520,14 +1531,14 @@ module Impl<FullStateConfigSig Config> {
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap
) {
revFlow0(node, state, returnCtx, returnAp, ap) and
fwdFlow(node, state, _, _, _, _, _, ap)
fwdFlow(node, state, _, _, _, _, _, ap, _)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap
) {
fwdFlow(node, state, _, _, _, _, _, ap) and
fwdFlow(node, state, _, _, _, _, _, ap, _) and
sinkNode(node, state) and
(
if hasSinkCallCtx()
@@ -1780,13 +1791,13 @@ module Impl<FullStateConfigSig Config> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and
fields = count(Content f0 | fwdConsCand(f0, _, _)) and
conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap))
ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and
@@ -1963,10 +1974,10 @@ module Impl<FullStateConfigSig Config> {
)
}
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
PrevStage::revFlowState(state) and
exists(t) and
t0 = t and
exists(ap) and
not stateBarrier(node, state) and
(
@@ -2012,7 +2023,8 @@ module Impl<FullStateConfigSig Config> {
FlowCheckNode() {
castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
expectsContentCached(this.asNode(), _) or
neverSkipInPathGraph(this.asNode())
}
}
@@ -2101,7 +2113,7 @@ module Impl<FullStateConfigSig Config> {
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
not isUnreachableInCall1(node2, cc) and
(
localFlowEntry(node1, pragma[only_bind_into](state)) and
(
@@ -2116,7 +2128,7 @@ module Impl<FullStateConfigSig Config> {
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
not isUnreachableInCall1(node1, cc)
or
exists(NodeEx mid |
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and
@@ -2153,10 +2165,8 @@ module Impl<FullStateConfigSig Config> {
preservesValue = false and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
not isUnreachableInCall1(node1, callContext) and
not isUnreachableInCall1(node2, callContext)
}
}
@@ -2197,8 +2207,8 @@ module Impl<FullStateConfigSig Config> {
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, LocalCc lcc
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and
exists(lcc)
@@ -2218,10 +2228,16 @@ module Impl<FullStateConfigSig Config> {
)
}
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
exists(state) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
// We can get away with not using type strengthening here, since we aren't
// going to use the tracked types in the construction of Stage 4 access
// paths. For Stage 4 and onwards, the tracked types must be consistent as
// the cons candidates including types are used to construct subsequent
// access path approximations.
t0 = t and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and
(
notExpectsContent(node)
or
@@ -2241,6 +2257,16 @@ module Impl<FullStateConfigSig Config> {
import MkStage<Stage2>::Stage<Stage3Param>
}
bindingset[node, t0]
private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) {
if castingNodeEx(node)
then
exists(DataFlowType nt | nt = node.getDataFlowType() |
if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0)
)
else t = t0
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
@@ -2274,8 +2300,8 @@ module Impl<FullStateConfigSig Config> {
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, LocalCc lcc
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
@@ -2333,11 +2359,11 @@ module Impl<FullStateConfigSig Config> {
)
}
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
exists(state) and
not clear(node, ap) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
strengthenType(node, t0, t) and
(
notExpectsContent(node)
or
@@ -2365,7 +2391,7 @@ module Impl<FullStateConfigSig Config> {
exists(AccessPathFront apf |
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _,
apf)
apf, _)
)
}
@@ -2579,8 +2605,8 @@ module Impl<FullStateConfigSig Config> {
import LocalCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, LocalCc lcc
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
@@ -2609,9 +2635,9 @@ module Impl<FullStateConfigSig Config> {
)
}
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
strengthenType(node, t0, t) and
exists(state) and
exists(ap)
}
@@ -2632,7 +2658,7 @@ module Impl<FullStateConfigSig Config> {
Stage5::parameterMayFlowThrough(p, _) and
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _,
TAccessPathApproxSome(apa), _, apa0)
TAccessPathApproxSome(apa), _, apa0, _)
)
}
@@ -2649,7 +2675,7 @@ module Impl<FullStateConfigSig Config> {
TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) {
exists(AccessPathApprox apa | ap.getApprox() = apa |
Stage5::parameterMayFlowThrough(p, apa) and
Stage5::fwdFlow(p, state, _, _, _, _, t, apa) and
Stage5::fwdFlow(p, state, _, _, Option<DataFlowType>::some(t), _, _, apa, _) and
Stage5::revFlow(p, state, _)
)
}
@@ -2680,7 +2706,7 @@ module Impl<FullStateConfigSig Config> {
ParamNodeEx getParamNode() { result = p }
override string toString() { result = p + ": " + ap }
override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -2732,12 +2758,21 @@ module Impl<FullStateConfigSig Config> {
)
}
private predicate forceUnfold(AccessPathApprox apa) {
forceHighPrecision(apa.getHead())
or
exists(Content c2 |
apa = TConsCons(_, _, c2, _) and
forceHighPrecision(c2)
)
}
/**
* Holds with `unfold = false` if a precise head-tail representation of `apa` is
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold) {
if forceHighPrecision(apa.getHead())
if forceUnfold(apa)
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
@@ -2751,7 +2786,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Gets the number of `AccessPath`s that correspond to `apa`.
*/
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa) {
evalUnfold(apa, false) and
result = 1 and
@@ -2770,7 +2804,6 @@ module Impl<FullStateConfigSig Config> {
* that it is expanded to a precise head-tail representation.
*/
language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa) {
apa instanceof AccessPathApproxNil and result = 1
or
@@ -2807,7 +2840,6 @@ module Impl<FullStateConfigSig Config> {
}
private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap
) {
@@ -2820,9 +2852,7 @@ module Impl<FullStateConfigSig Config> {
ap = TAccessPathNil()
or
// ... or a step from an existing PathNode to another node.
pathStep(_, node, state, cc, sc, t, ap) and
Stage5::revFlow(node, state, ap.getApprox()) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any())
pathStep(_, node, state, cc, sc, t, ap)
} or
TPathNodeSink(NodeEx node, FlowState state) {
exists(PathNodeMid sink |
@@ -2894,7 +2924,6 @@ module Impl<FullStateConfigSig Config> {
override AccessPathFrontHead getFront() { result = TFrontHead(head_) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() {
result = TConsNil(head_, t) and tail_ = TAccessPathNil()
or
@@ -2903,7 +2932,6 @@ module Impl<FullStateConfigSig Config> {
result = TCons1(head_, this.length())
}
pragma[assume_small_delta]
override int length() { result = 1 + tail_.length() }
private string toStringImpl(boolean needsSuffix) {
@@ -3073,6 +3101,12 @@ module Impl<FullStateConfigSig Config> {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
private string ppSummaryCtx() {
this instanceof PathNodeSink and result = ""
or
result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">"
}
/** Gets a textual representation of this element. */
string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() }
@@ -3081,7 +3115,9 @@ module Impl<FullStateConfigSig Config> {
* representation of the call context.
*/
string toStringWithContext() {
result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx()
result =
this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() +
this.ppSummaryCtx()
}
/**
@@ -3340,13 +3376,23 @@ module Impl<FullStateConfigSig Config> {
ap = mid.getAp()
}
private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
AccessPath ap
) {
exists(DataFlowType t0 |
pathStep0(mid, node, state, cc, sc, t0, ap) and
Stage5::revFlow(node, state, ap.getApprox()) and
strengthenType(node, t0, t)
)
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathStep(
private predicate pathStep0(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
AccessPath ap
) {
@@ -3557,7 +3603,6 @@ module Impl<FullStateConfigSig Config> {
)
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathThroughCallable0(
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc,
@@ -3964,7 +4009,7 @@ module Impl<FullStateConfigSig Config> {
ap = TPartialNil() and
exists(explorationLimit())
or
partialPathNodeMk0(node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
distSrc(node.getEnclosingCallable()) <= explorationLimit()
} or
TPartialPathNodeRev(
@@ -3990,11 +4035,20 @@ module Impl<FullStateConfigSig Config> {
}
pragma[nomagic]
private predicate partialPathNodeMk0(
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
private predicate partialPathStep(
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
) {
partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap)
}
pragma[nomagic]
private predicate partialPathStep1(
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t,
PartialAccessPath ap
) {
partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and
not fullBarrier(node) and
not stateBarrier(node, state) and
not clearsContentEx(node, ap.getHead()) and
@@ -4002,9 +4056,14 @@ module Impl<FullStateConfigSig Config> {
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), t)
else any()
strengthenType(node, t0, t)
}
pragma[nomagic]
private predicate partialPathTypeStrengthen(
DataFlowType t0, PartialAccessPath ap, DataFlowType t
) {
partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t
}
/**
@@ -4183,7 +4242,8 @@ module Impl<FullStateConfigSig Config> {
}
}
private predicate partialPathStep(
pragma[nomagic]
private predicate partialPathStep0(
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
) {
@@ -4309,6 +4369,11 @@ module Impl<FullStateConfigSig Config> {
DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2
) {
partialPathStoreStep(_, t1, ap1, c, _, t2, ap2)
or
exists(DataFlowType t0 |
partialPathTypeStrengthen(t0, ap2, t2) and
apConsFwd(t1, ap1, c, t0, ap2)
)
}
pragma[nomagic]

View File

@@ -187,7 +187,6 @@ private module LambdaFlow {
else any()
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlow0(
DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
@@ -274,7 +273,6 @@ private module LambdaFlow {
)
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlowOut(
DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,

View File

@@ -205,6 +205,8 @@ predicate clearsContent(Node n, Content c) {
*/
predicate expectsContent(Node n, ContentSet c) { none() }
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
/** Gets the type of `n` used for type pruning. */
Type getNodeType(Node n) {
suppressUnusedNode(n) and
@@ -233,6 +235,12 @@ class CastNode extends Node {
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 DataFlowExpr = Expr;

View File

@@ -114,7 +114,7 @@ signature module StateConfigSig {
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state);
default predicate isBarrier(Node node, FlowState state) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrierIn(Node node) { none() }
@@ -131,7 +131,9 @@ signature module StateConfigSig {
* 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);
default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
}
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be

View File

@@ -254,6 +254,11 @@ module Impl<FullStateConfigSig Config> {
not fullBarrier(node2)
}
pragma[nomagic]
private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) {
isUnreachableInCallCached(n.asNode(), cc.getCall())
}
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
@@ -460,7 +465,6 @@ module Impl<FullStateConfigSig Config> {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
pragma[assume_small_delta]
private predicate fwdFlow(NodeEx node, Cc cc) {
sourceNode(node, _) and
if hasSourceCallCtx() then cc = true else cc = false
@@ -570,7 +574,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Holds if `c` is the target of a store in the flow covered by `fwdFlow`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlowConsCand(Content c) {
exists(NodeEx mid, NodeEx node |
@@ -1135,8 +1138,8 @@ module Impl<FullStateConfigSig Config> {
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow
);
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap);
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t);
bindingset[typ, contentType]
predicate typecheckStore(Typ typ, DataFlowType contentType);
@@ -1199,20 +1202,23 @@ module Impl<FullStateConfigSig Config> {
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
ApOption argAp, Typ t, Ap ap, ApApprox apa
) {
fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t, ap, apa) and
PrevStage::revFlow(node, state, apa) and
filter(node, state, t, ap)
fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa)
}
pragma[inline]
additional predicate fwdFlow(
private predicate fwdFlow1(
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
ApOption argAp, Typ t, Ap ap
ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa
) {
fwdFlow(node, state, cc, summaryCtx, argT, argAp, t, ap, _)
fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and
PrevStage::revFlow(node, state, apa) and
filter(node, state, t0, ap, t)
}
pragma[nomagic]
private predicate typeStrengthen(Typ t0, Ap ap, Typ t) {
fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
@@ -1339,6 +1345,11 @@ module Impl<FullStateConfigSig Config> {
private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) {
fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and
cons = apCons(c, t1, tail)
or
exists(Typ t0 |
typeStrengthen(t0, cons, t2) and
fwdFlowConsCand(t0, cons, c, t1, tail)
)
}
pragma[nomagic]
@@ -1359,7 +1370,7 @@ module Impl<FullStateConfigSig Config> {
ParamNodeOption summaryCtx, TypOption argT, ApOption argAp
) {
exists(ApHeadContent apc |
fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap) and
fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and
apc = getHeadContent(ap) and
readStepCand0(node1, apc, c, node2)
)
@@ -1520,14 +1531,14 @@ module Impl<FullStateConfigSig Config> {
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap
) {
revFlow0(node, state, returnCtx, returnAp, ap) and
fwdFlow(node, state, _, _, _, _, _, ap)
fwdFlow(node, state, _, _, _, _, _, ap, _)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap
) {
fwdFlow(node, state, _, _, _, _, _, ap) and
fwdFlow(node, state, _, _, _, _, _, ap, _) and
sinkNode(node, state) and
(
if hasSinkCallCtx()
@@ -1780,13 +1791,13 @@ module Impl<FullStateConfigSig Config> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and
fields = count(Content f0 | fwdConsCand(f0, _, _)) and
conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap))
ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and
@@ -1963,10 +1974,10 @@ module Impl<FullStateConfigSig Config> {
)
}
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
PrevStage::revFlowState(state) and
exists(t) and
t0 = t and
exists(ap) and
not stateBarrier(node, state) and
(
@@ -2012,7 +2023,8 @@ module Impl<FullStateConfigSig Config> {
FlowCheckNode() {
castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
expectsContentCached(this.asNode(), _) or
neverSkipInPathGraph(this.asNode())
}
}
@@ -2101,7 +2113,7 @@ module Impl<FullStateConfigSig Config> {
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
not isUnreachableInCall1(node2, cc) and
(
localFlowEntry(node1, pragma[only_bind_into](state)) and
(
@@ -2116,7 +2128,7 @@ module Impl<FullStateConfigSig Config> {
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
not isUnreachableInCall1(node1, cc)
or
exists(NodeEx mid |
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and
@@ -2153,10 +2165,8 @@ module Impl<FullStateConfigSig Config> {
preservesValue = false and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
not isUnreachableInCall1(node1, callContext) and
not isUnreachableInCall1(node2, callContext)
}
}
@@ -2197,8 +2207,8 @@ module Impl<FullStateConfigSig Config> {
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, LocalCc lcc
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and
exists(lcc)
@@ -2218,10 +2228,16 @@ module Impl<FullStateConfigSig Config> {
)
}
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
exists(state) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
// We can get away with not using type strengthening here, since we aren't
// going to use the tracked types in the construction of Stage 4 access
// paths. For Stage 4 and onwards, the tracked types must be consistent as
// the cons candidates including types are used to construct subsequent
// access path approximations.
t0 = t and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and
(
notExpectsContent(node)
or
@@ -2241,6 +2257,16 @@ module Impl<FullStateConfigSig Config> {
import MkStage<Stage2>::Stage<Stage3Param>
}
bindingset[node, t0]
private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) {
if castingNodeEx(node)
then
exists(DataFlowType nt | nt = node.getDataFlowType() |
if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0)
)
else t = t0
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
@@ -2274,8 +2300,8 @@ module Impl<FullStateConfigSig Config> {
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, LocalCc lcc
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
@@ -2333,11 +2359,11 @@ module Impl<FullStateConfigSig Config> {
)
}
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
exists(state) and
not clear(node, ap) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
strengthenType(node, t0, t) and
(
notExpectsContent(node)
or
@@ -2365,7 +2391,7 @@ module Impl<FullStateConfigSig Config> {
exists(AccessPathFront apf |
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _,
apf)
apf, _)
)
}
@@ -2579,8 +2605,8 @@ module Impl<FullStateConfigSig Config> {
import LocalCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, LocalCc lcc
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
@@ -2609,9 +2635,9 @@ module Impl<FullStateConfigSig Config> {
)
}
bindingset[node, state, t, ap]
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
bindingset[node, state, t0, ap]
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
strengthenType(node, t0, t) and
exists(state) and
exists(ap)
}
@@ -2632,7 +2658,7 @@ module Impl<FullStateConfigSig Config> {
Stage5::parameterMayFlowThrough(p, _) and
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _,
TAccessPathApproxSome(apa), _, apa0)
TAccessPathApproxSome(apa), _, apa0, _)
)
}
@@ -2649,7 +2675,7 @@ module Impl<FullStateConfigSig Config> {
TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) {
exists(AccessPathApprox apa | ap.getApprox() = apa |
Stage5::parameterMayFlowThrough(p, apa) and
Stage5::fwdFlow(p, state, _, _, _, _, t, apa) and
Stage5::fwdFlow(p, state, _, _, Option<DataFlowType>::some(t), _, _, apa, _) and
Stage5::revFlow(p, state, _)
)
}
@@ -2680,7 +2706,7 @@ module Impl<FullStateConfigSig Config> {
ParamNodeEx getParamNode() { result = p }
override string toString() { result = p + ": " + ap }
override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -2732,12 +2758,21 @@ module Impl<FullStateConfigSig Config> {
)
}
private predicate forceUnfold(AccessPathApprox apa) {
forceHighPrecision(apa.getHead())
or
exists(Content c2 |
apa = TConsCons(_, _, c2, _) and
forceHighPrecision(c2)
)
}
/**
* Holds with `unfold = false` if a precise head-tail representation of `apa` is
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold) {
if forceHighPrecision(apa.getHead())
if forceUnfold(apa)
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
@@ -2751,7 +2786,6 @@ module Impl<FullStateConfigSig Config> {
/**
* Gets the number of `AccessPath`s that correspond to `apa`.
*/
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa) {
evalUnfold(apa, false) and
result = 1 and
@@ -2770,7 +2804,6 @@ module Impl<FullStateConfigSig Config> {
* that it is expanded to a precise head-tail representation.
*/
language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa) {
apa instanceof AccessPathApproxNil and result = 1
or
@@ -2807,7 +2840,6 @@ module Impl<FullStateConfigSig Config> {
}
private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap
) {
@@ -2820,9 +2852,7 @@ module Impl<FullStateConfigSig Config> {
ap = TAccessPathNil()
or
// ... or a step from an existing PathNode to another node.
pathStep(_, node, state, cc, sc, t, ap) and
Stage5::revFlow(node, state, ap.getApprox()) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any())
pathStep(_, node, state, cc, sc, t, ap)
} or
TPathNodeSink(NodeEx node, FlowState state) {
exists(PathNodeMid sink |
@@ -2894,7 +2924,6 @@ module Impl<FullStateConfigSig Config> {
override AccessPathFrontHead getFront() { result = TFrontHead(head_) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() {
result = TConsNil(head_, t) and tail_ = TAccessPathNil()
or
@@ -2903,7 +2932,6 @@ module Impl<FullStateConfigSig Config> {
result = TCons1(head_, this.length())
}
pragma[assume_small_delta]
override int length() { result = 1 + tail_.length() }
private string toStringImpl(boolean needsSuffix) {
@@ -3073,6 +3101,12 @@ module Impl<FullStateConfigSig Config> {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
private string ppSummaryCtx() {
this instanceof PathNodeSink and result = ""
or
result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">"
}
/** Gets a textual representation of this element. */
string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() }
@@ -3081,7 +3115,9 @@ module Impl<FullStateConfigSig Config> {
* representation of the call context.
*/
string toStringWithContext() {
result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx()
result =
this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() +
this.ppSummaryCtx()
}
/**
@@ -3340,13 +3376,23 @@ module Impl<FullStateConfigSig Config> {
ap = mid.getAp()
}
private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
AccessPath ap
) {
exists(DataFlowType t0 |
pathStep0(mid, node, state, cc, sc, t0, ap) and
Stage5::revFlow(node, state, ap.getApprox()) and
strengthenType(node, t0, t)
)
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathStep(
private predicate pathStep0(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
AccessPath ap
) {
@@ -3557,7 +3603,6 @@ module Impl<FullStateConfigSig Config> {
)
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate pathThroughCallable0(
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc,
@@ -3964,7 +4009,7 @@ module Impl<FullStateConfigSig Config> {
ap = TPartialNil() and
exists(explorationLimit())
or
partialPathNodeMk0(node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
distSrc(node.getEnclosingCallable()) <= explorationLimit()
} or
TPartialPathNodeRev(
@@ -3990,11 +4035,20 @@ module Impl<FullStateConfigSig Config> {
}
pragma[nomagic]
private predicate partialPathNodeMk0(
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
private predicate partialPathStep(
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
) {
partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap)
}
pragma[nomagic]
private predicate partialPathStep1(
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t,
PartialAccessPath ap
) {
partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and
not fullBarrier(node) and
not stateBarrier(node, state) and
not clearsContentEx(node, ap.getHead()) and
@@ -4002,9 +4056,14 @@ module Impl<FullStateConfigSig Config> {
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), t)
else any()
strengthenType(node, t0, t)
}
pragma[nomagic]
private predicate partialPathTypeStrengthen(
DataFlowType t0, PartialAccessPath ap, DataFlowType t
) {
partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t
}
/**
@@ -4183,7 +4242,8 @@ module Impl<FullStateConfigSig Config> {
}
}
private predicate partialPathStep(
pragma[nomagic]
private predicate partialPathStep0(
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
) {
@@ -4309,6 +4369,11 @@ module Impl<FullStateConfigSig Config> {
DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2
) {
partialPathStoreStep(_, t1, ap1, c, _, t2, ap2)
or
exists(DataFlowType t0 |
partialPathTypeStrengthen(t0, ap2, t2) and
apConsFwd(t1, ap1, c, t0, ap2)
)
}
pragma[nomagic]

View File

@@ -187,7 +187,6 @@ private module LambdaFlow {
else any()
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlow0(
DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
@@ -274,7 +273,6 @@ private module LambdaFlow {
)
}
pragma[assume_small_delta]
pragma[nomagic]
predicate revLambdaFlowOut(
DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,

View File

@@ -193,86 +193,89 @@ private class SingleUseOperandNode0 extends OperandNode0, TSingleUseOperandNode0
SingleUseOperandNode0() { this = TSingleUseOperandNode0(op) }
}
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an operand in the IR
* after `index` number of loads.
*
* Note: Unlike `RawIndirectOperand`, a value of type `IndirectOperand` may
* be an `OperandNode`.
*/
class IndirectOperand extends Node {
Operand operand;
int indirectionIndex;
IndirectOperand() {
this.(RawIndirectOperand).getOperand() = operand and
this.(RawIndirectOperand).getIndirectionIndex() = indirectionIndex
or
this.(OperandNode).getOperand() =
Ssa::getIRRepresentationOfIndirectOperand(operand, indirectionIndex)
private module IndirectOperands {
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an operand in the IR
* after `index` number of loads.
*
* Note: Unlike `RawIndirectOperand`, a value of type `IndirectOperand` may
* be an `OperandNode`.
*/
abstract class IndirectOperand extends Node {
/** Gets the underlying operand and the underlying indirection index. */
abstract predicate hasOperandAndIndirectionIndex(Operand operand, int indirectionIndex);
}
/** Gets the underlying operand. */
Operand getOperand() { result = operand }
private class IndirectOperandFromRaw extends IndirectOperand instanceof RawIndirectOperand {
override predicate hasOperandAndIndirectionIndex(Operand operand, int indirectionIndex) {
operand = RawIndirectOperand.super.getOperand() and
indirectionIndex = RawIndirectOperand.super.getIndirectionIndex()
}
}
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
private class IndirectOperandFromIRRepr extends IndirectOperand {
Operand operand;
int 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
)
IndirectOperandFromIRRepr() {
exists(Operand repr |
repr = Ssa::getIRRepresentationOfIndirectOperand(operand, indirectionIndex) and
nodeHasOperand(this, repr, indirectionIndex - 1)
)
}
override predicate hasOperandAndIndirectionIndex(Operand op, int index) {
op = operand and index = indirectionIndex
}
}
}
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an instruction in the IR
* after `index` number of loads.
*
* Note: Unlike `RawIndirectInstruction`, a value of type `IndirectInstruction` may
* be an `InstructionNode`.
*/
class IndirectInstruction extends Node {
Instruction instr;
int indirectionIndex;
import IndirectOperands
IndirectInstruction() {
this.(RawIndirectInstruction).getInstruction() = instr and
this.(RawIndirectInstruction).getIndirectionIndex() = indirectionIndex
or
this.(InstructionNode).getInstruction() =
Ssa::getIRRepresentationOfIndirectInstruction(instr, indirectionIndex)
private module IndirectInstructions {
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an instruction in the IR
* after `index` number of loads.
*
* Note: Unlike `RawIndirectInstruction`, a value of type `IndirectInstruction` may
* be an `InstructionNode`.
*/
abstract class IndirectInstruction extends Node {
/** Gets the underlying operand and the underlying indirection index. */
abstract predicate hasInstructionAndIndirectionIndex(Instruction instr, int index);
}
/** Gets the underlying instruction. */
Instruction getInstruction() { result = instr }
private class IndirectInstructionFromRaw extends IndirectInstruction instanceof RawIndirectInstruction
{
override predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
instr = RawIndirectInstruction.super.getInstruction() and
index = RawIndirectInstruction.super.getIndirectionIndex()
}
}
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
private class IndirectInstructionFromIRRepr extends IndirectInstruction {
Instruction instr;
int 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
)
IndirectInstructionFromIRRepr() {
exists(Instruction repr |
repr = Ssa::getIRRepresentationOfIndirectInstruction(instr, indirectionIndex) and
nodeHasInstruction(this, repr, indirectionIndex - 1)
)
}
override predicate hasInstructionAndIndirectionIndex(Instruction i, int index) {
i = instr and index = indirectionIndex
}
}
}
import IndirectInstructions
/** Gets the callable in which this node occurs. */
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
@@ -318,9 +321,11 @@ private class PrimaryArgumentNode extends ArgumentNode, OperandNode {
private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode {
override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) {
this.getCallInstruction() = dfCall and
pos.(IndirectionPosition).getArgumentIndex() = this.getArgumentIndex() and
pos.(IndirectionPosition).getIndirectionIndex() = super.getIndirectionIndex()
exists(int indirectionIndex |
pos = TIndirectionPosition(argumentIndex, pragma[only_bind_into](indirectionIndex)) and
this.getCallInstruction() = dfCall and
super.hasAddressOperandAndIndirectionIndex(_, pragma[only_bind_into](indirectionIndex))
)
}
}
@@ -648,13 +653,16 @@ predicate jumpStep(Node n1, Node n2) {
* 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`.
*
* The boolean `certain` is true if the destination address does not involve
* any pointer arithmetic, and false otherwise.
*/
predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) {
predicate storeStepImpl(Node node1, Content c, PostFieldUpdateNode node2, boolean certain) {
exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store |
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
node2.getIndirectionIndex() = 1 and
numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(),
numberOfLoads)
numberOfLoads, certain)
|
exists(FieldContent fc | fc = c |
fc.getField() = node2.getUpdatedField() and
@@ -668,21 +676,34 @@ predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) {
)
}
/**
* 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, Content c, PostFieldUpdateNode node2) {
storeStepImpl(node1, c, node2, _)
}
/**
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
* operations and exactly `n` `LoadInstruction` operations.
*/
private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand operandTo, int ind) {
private predicate numberOfLoadsFromOperandRec(
Operand operandFrom, Operand operandTo, int ind, boolean certain
) {
exists(Instruction load | Ssa::isDereference(load, operandFrom) |
operandTo = operandFrom and ind = 0
operandTo = operandFrom and ind = 0 and certain = true
or
numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1)
numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1, certain)
)
or
exists(Operand op, Instruction instr |
exists(Operand op, Instruction instr, boolean isPointerArith, boolean certain0 |
instr = op.getDef() and
conversionFlow(operandFrom, instr, _, _) and
numberOfLoadsFromOperand(op, operandTo, ind)
conversionFlow(operandFrom, instr, isPointerArith, _) and
numberOfLoadsFromOperand(op, operandTo, ind, certain0)
|
if isPointerArith = true then certain = false else certain = certain0
)
}
@@ -690,13 +711,16 @@ private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand opera
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
* operations and exactly `n` `LoadInstruction` operations.
*/
private predicate numberOfLoadsFromOperand(Operand operandFrom, Operand operandTo, int n) {
numberOfLoadsFromOperandRec(operandFrom, operandTo, n)
private predicate numberOfLoadsFromOperand(
Operand operandFrom, Operand operandTo, int n, boolean certain
) {
numberOfLoadsFromOperandRec(operandFrom, operandTo, n, certain)
or
not Ssa::isDereference(_, operandFrom) and
not conversionFlow(operandFrom, _, _, _) and
operandFrom = operandTo and
n = 0
n = 0 and
certain = true
}
// Needed to join on both an operand and an index at the same time.
@@ -726,7 +750,7 @@ predicate readStep(Node node1, Content c, Node node2) {
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
// in `storeStep`.
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
numberOfLoadsFromOperand(fa1, operand, numberOfLoads)
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _)
|
exists(FieldContent fc | fc = c |
fc.getField() = fa1.getField() and
@@ -744,7 +768,33 @@ predicate readStep(Node node1, Content c, Node node2) {
* Holds if values stored inside content `c` are cleared at node `n`.
*/
predicate clearsContent(Node n, Content c) {
none() // stub implementation
n =
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.getIndirectionIndex() = d.getIndirectionIndex()
)
)
}
/**
@@ -753,6 +803,8 @@ predicate clearsContent(Node n, Content c) {
*/
predicate expectsContent(Node n, ContentSet c) { none() }
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
/** Gets the type of `n` used for type pruning. */
DataFlowType getNodeType(Node n) {
suppressUnusedNode(n) and
@@ -781,6 +833,12 @@ class CastNode extends Node {
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
* flow crosses from one _enclosing callable_ to another, the interprocedural
@@ -798,7 +856,73 @@ class DataFlowCall extends CallInstruction {
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
}
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
module IsUnreachableInCall {
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.controlflow.IRGuards as G
private class ConstantIntegralTypeArgumentNode extends PrimaryArgumentNode {
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
int accessPathLimit() { result = 5 }
@@ -837,7 +961,7 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
* by default as a heuristic.
*/
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
predicate allowParameterReturnInSelf(ParameterNode p) { p instanceof IndirectParameterNode }
private predicate fieldHasApproxName(Field f, string s) {
s = f.getName().charAt(0) and

View File

@@ -274,7 +274,7 @@ class Node extends TIRDataFlowNode {
* represents the value of `**x` going into `f`.
*/
Expr asIndirectArgument(int index) {
this.(SideEffectOperandNode).getIndirectionIndex() = index and
this.(SideEffectOperandNode).hasAddressOperandAndIndirectionIndex(_, index) and
result = this.(SideEffectOperandNode).getArgument()
}
@@ -317,7 +317,7 @@ class Node extends TIRDataFlowNode {
index = 0 and
result = this.(ExplicitParameterNode).getParameter()
or
this.(IndirectParameterNode).getIndirectionIndex() = index and
this.(IndirectParameterNode).hasInstructionAndIndirectionIndex(_, index) and
result = this.(IndirectParameterNode).getParameter()
}
@@ -577,15 +577,20 @@ class SsaPhiNode extends Node, TSsaPhiNode {
*
* A node representing a value after leaving a function.
*/
class SideEffectOperandNode extends Node, IndirectOperand {
class SideEffectOperandNode extends Node instanceof IndirectOperand {
CallInstruction call;
int argumentIndex;
SideEffectOperandNode() { operand = call.getArgumentOperand(argumentIndex) }
SideEffectOperandNode() {
IndirectOperand.super.hasOperandAndIndirectionIndex(call.getArgumentOperand(argumentIndex), _)
}
CallInstruction getCallInstruction() { result = call }
Operand getAddressOperand() { result = operand }
/** Gets the underlying operand and the underlying indirection index. */
predicate hasAddressOperandAndIndirectionIndex(Operand operand, int indirectionIndex) {
IndirectOperand.super.hasOperandAndIndirectionIndex(operand, indirectionIndex)
}
int getArgumentIndex() { result = argumentIndex }
@@ -665,10 +670,10 @@ class InitialGlobalValue extends Node, TInitialGlobalValue {
*
* A node representing an indirection of a parameter.
*/
class IndirectParameterNode extends Node, IndirectInstruction {
class IndirectParameterNode extends Node instanceof IndirectInstruction {
InitializeParameterInstruction init;
IndirectParameterNode() { this.getInstruction() = init }
IndirectParameterNode() { IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _) }
int getArgumentIndex() { init.hasIndex(result) }
@@ -677,7 +682,12 @@ class IndirectParameterNode extends Node, IndirectInstruction {
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
override Declaration getFunction() { result = init.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() }
@@ -699,7 +709,8 @@ class IndirectReturnNode extends Node {
IndirectReturnNode() {
this instanceof FinalParameterNode
or
this.(IndirectOperand).getOperand() = any(ReturnValueInstruction ret).getReturnAddressOperand()
this.(IndirectOperand)
.hasOperandAndIndirectionIndex(any(ReturnValueInstruction ret).getReturnAddressOperand(), _)
}
override Declaration getEnclosingCallable() { result = this.getFunction() }
@@ -722,7 +733,7 @@ class IndirectReturnNode extends Node {
int getIndirectionIndex() {
result = this.(FinalParameterNode).getIndirectionIndex()
or
result = this.(IndirectOperand).getIndirectionIndex()
this.(IndirectOperand).hasOperandAndIndirectionIndex(_, result)
}
}
@@ -1106,7 +1117,8 @@ predicate exprNodeShouldBeInstruction(Node node, Expr e) {
/** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */
predicate indirectExprNodeShouldBeIndirectInstruction(IndirectInstruction node, Expr e) {
exists(Instruction instr |
instr = node.getInstruction() and not indirectExprNodeShouldBeIndirectOperand(_, e)
node.hasInstructionAndIndirectionIndex(instr, _) and
not indirectExprNodeShouldBeIndirectOperand(_, e)
|
e = instr.(VariableAddressInstruction).getAst().(Expr).getFullyConverted()
or
@@ -1307,8 +1319,8 @@ pragma[noinline]
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
IndirectParameterNode node, int argumentIndex, int indirectionIndex
) {
node.getArgumentIndex() = argumentIndex and
node.getIndirectionIndex() = indirectionIndex
node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and
node.getArgumentIndex() = argumentIndex
}
/** A synthetic parameter to model the pointed-to object of a pointer parameter. */
@@ -1479,18 +1491,14 @@ VariableNode variableNode(Variable v) {
*/
Node uninitializedNode(LocalVariable v) { none() }
pragma[noinline]
predicate hasOperandAndIndex(IndirectOperand indirectOperand, Operand operand, int indirectionIndex) {
indirectOperand.getOperand() = operand and
indirectOperand.getIndirectionIndex() = indirectionIndex
indirectOperand.hasOperandAndIndirectionIndex(operand, indirectionIndex)
}
pragma[noinline]
predicate hasInstructionAndIndex(
IndirectInstruction indirectInstr, Instruction instr, int indirectionIndex
) {
indirectInstr.getInstruction() = instr and
indirectInstr.getIndirectionIndex() = indirectionIndex
indirectInstr.hasInstructionAndIndirectionIndex(instr, indirectionIndex)
}
cached
@@ -1640,8 +1648,15 @@ predicate localInstructionFlow(Instruction e1, Instruction e2) {
localFlow(instructionNode(e1), instructionNode(e2))
}
/**
* INTERNAL: Do not use.
*
* Ideally this module would be private, but the `asExprInternal` predicate is
* needed in `DefaultTaintTrackingImpl`. Once `DefaultTaintTrackingImpl` is gone
* we can make this module private.
*/
cached
private module ExprFlowCached {
module ExprFlowCached {
/**
* Holds if `n` is an indirect operand of a `PointerArithmeticInstruction`, and
* `e` is the result of loading from the `PointerArithmeticInstruction`.
@@ -1649,8 +1664,7 @@ private module ExprFlowCached {
private predicate isIndirectBaseOfArrayAccess(IndirectOperand n, Expr e) {
exists(LoadInstruction load, PointerArithmeticInstruction pai |
pai = load.getSourceAddress() and
pai.getLeftOperand() = n.getOperand() and
n.getIndirectionIndex() = 1 and
n.hasOperandAndIndirectionIndex(pai.getLeftOperand(), 1) and
e = load.getConvertedResultExpression()
)
}
@@ -1692,7 +1706,8 @@ private module ExprFlowCached {
* `x[i]` steps to the expression `x[i - 1]` without traversing the
* entire chain.
*/
private Expr asExpr(Node n) {
cached
Expr asExprInternal(Node n) {
isIndirectBaseOfArrayAccess(n, result)
or
not isIndirectBaseOfArrayAccess(n, _) and
@@ -1704,7 +1719,7 @@ private module ExprFlowCached {
* dataflow step.
*/
private predicate localStepFromNonExpr(Node n1, Node n2) {
not exists(asExpr(n1)) and
not exists(asExprInternal(n1)) and
localFlowStep(n1, n2)
}
@@ -1715,7 +1730,7 @@ private module ExprFlowCached {
pragma[nomagic]
private predicate localStepsToExpr(Node n1, Node n2, Expr e2) {
localStepFromNonExpr*(n1, n2) and
e2 = asExpr(n2)
e2 = asExprInternal(n2)
}
/**
@@ -1726,7 +1741,7 @@ private module ExprFlowCached {
exists(Node mid |
localFlowStep(n1, mid) and
localStepsToExpr(mid, n2, e2) and
e1 = asExpr(n1)
e1 = asExprInternal(n1)
)
}
@@ -1817,6 +1832,20 @@ class Content extends TContent {
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
}
/** 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. */
@@ -1834,10 +1863,21 @@ class FieldContent extends Content, TFieldContent {
Field getField() { result = f }
/** Gets the indirection index of this `FieldContent`. */
pragma[inline]
int getIndirectionIndex() {
override int getIndirectionIndex() {
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. */
@@ -1862,9 +1902,21 @@ class UnionContent extends Content, TUnionContent {
/** Gets the indirection index of this `UnionContent`. */
pragma[inline]
int getIndirectionIndex() {
override int getIndirectionIndex() {
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

@@ -60,7 +60,7 @@ private DataFlow::Node getNodeForSource(Expr source) {
}
private DataFlow::Node getNodeForExpr(Expr node) {
result = DataFlow::exprNode(node)
node = DataFlow::ExprFlowCached::asExprInternal(result)
or
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
@@ -221,7 +221,7 @@ private module Cached {
predicate nodeIsBarrierIn(DataFlow::Node node) {
// don't use dataflow into taint sources, as this leads to duplicate results.
exists(Expr source | isUserInput(source, _) |
node = DataFlow::exprNode(source)
source = DataFlow::ExprFlowCached::asExprInternal(node)
or
// This case goes together with the similar (but not identical) rule in
// `getNodeForSource`.

View File

@@ -0,0 +1,38 @@
/**
* Print the dataflow local store steps in IR dumps.
*/
private import cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import PrintIRUtilities
/** A property provider for local IR dataflow store steps. */
class FieldFlowPropertyProvider extends IRPropertyProvider {
override string getOperandProperty(Operand operand, string key) {
exists(PostFieldUpdateNode pfun, Content content |
key = "store " + content.toString() and
pfun.getPreUpdateNode().(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and
result =
strictconcat(string element, Node node |
storeStep(node, content, pfun) and
element = nodeId(node, _, _)
|
element, ", "
)
)
or
exists(Node node2, Content content |
key = "read " + content.toString() and
node2.(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and
result =
strictconcat(string element, Node node1 |
readStep(node1, content, node2) and
element = nodeId(node1, _, _)
|
element, ", "
)
)
}
}

View File

@@ -1,119 +1,44 @@
private import cpp
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
// that the cached IR gets the same checksum here as it does in queries that use
// `ValueNumbering` without `DataFlow`.
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import SsaInternals as Ssa
private import PrintIRUtilities
/**
* Gets the local dataflow from other nodes in the same function to this node.
*/
private string getFromFlow(DataFlow::Node useNode, int order1, int order2) {
exists(DataFlow::Node defNode, string prefix |
(
simpleLocalFlowStep(defNode, useNode) and prefix = ""
or
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
prefix = "+"
) and
if defNode.asInstruction() = useNode.asOperand().getAnyDef()
then
// Shorthand for flow from the def of this operand.
result = prefix + "def" and
order1 = -1 and
order2 = 0
else
if defNode.asOperand().getUse() = useNode.asInstruction()
then
// Shorthand for flow from an operand of this instruction
result = prefix + defNode.asOperand().getDumpId() and
order1 = -1 and
order2 = defNode.asOperand().getDumpSortOrder()
else result = prefix + nodeId(defNode, order1, order2)
private string getFromFlow(Node node2, int order1, int order2) {
exists(Node node1 |
simpleLocalFlowStep(node1, node2) and
result = nodeId(node1, order1, order2)
)
}
/**
* Gets the local dataflow from this node to other nodes in the same function.
*/
private string getToFlow(DataFlow::Node defNode, int order1, int order2) {
exists(DataFlow::Node useNode, string prefix |
(
simpleLocalFlowStep(defNode, useNode) and prefix = ""
or
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
prefix = "+"
) and
if useNode.asInstruction() = defNode.asOperand().getUse()
then
// Shorthand for flow to this operand's instruction.
result = prefix + "result" and
order1 = -1 and
order2 = 0
else result = prefix + nodeId(useNode, order1, order2)
private string getToFlow(Node node1, int order1, int order2) {
exists(Node node2 |
simpleLocalFlowStep(node1, node2) and
result = nodeId(node2, order1, order2)
)
}
/**
* Gets the properties of the dataflow node `node`.
*/
private string getNodeProperty(DataFlow::Node node, string key) {
private string getNodeProperty(Node node, string key) {
// List dataflow into and out of this node. Flow into this node is printed as `src->@`, and flow
// out of this node is printed as `@->dest`.
key = "flow" and
result =
strictconcat(string flow, boolean to, int order1, int order2 |
flow = getFromFlow(node, order1, order2) + "->@" and to = false
flow = getFromFlow(node, order1, order2) + "->" + starsForNode(node) + "@" and to = false
or
flow = "@->" + getToFlow(node, order1, order2) and to = true
flow = starsForNode(node) + "@->" + getToFlow(node, order1, order2) and to = true
|
flow, ", " order by to, order1, order2, flow
)
or
// Is this node a dataflow sink?
key = "sink" and
any(DataFlow::Configuration cfg).isSink(node) and
result = "true"
or
// Is this node a dataflow source?
key = "source" and
any(DataFlow::Configuration cfg).isSource(node) and
result = "true"
or
// Is this node a dataflow barrier, and if so, what kind?
key = "barrier" and
result =
strictconcat(string kind |
any(DataFlow::Configuration cfg).isBarrier(node) and kind = "full"
or
any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in"
or
any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out"
|
kind, ", "
)
// or
// // Is there partial flow from a source to this node?
// // This property will only be emitted if partial flow is enabled by overriding
// // `DataFlow::Configuration::explorationLimit()`.
// key = "pflow" and
// result =
// strictconcat(DataFlow::PartialPathNode sourceNode, DataFlow::PartialPathNode destNode, int dist,
// int order1, int order2 |
// any(DataFlow::Configuration cfg).hasPartialFlow(sourceNode, destNode, dist) and
// destNode.getNode() = node and
// // Only print flow from a source in the same function.
// sourceNode.getNode().getEnclosingCallable() = node.getEnclosingCallable()
// |
// nodeId(sourceNode.getNode(), order1, order2) + "+" + dist.toString(), ", "
// order by
// order1, order2, dist desc
// )
}
/**
@@ -121,16 +46,21 @@ private string getNodeProperty(DataFlow::Node node, string key) {
*/
class LocalFlowPropertyProvider extends IRPropertyProvider {
override string getOperandProperty(Operand operand, string key) {
exists(DataFlow::Node node |
operand = node.asOperand() and
exists(Node node |
operand = [node.asOperand(), node.(RawIndirectOperand).getOperand()] and
result = getNodeProperty(node, key)
)
}
override string getInstructionProperty(Instruction instruction, string key) {
exists(DataFlow::Node node |
instruction = node.asInstruction() and
exists(Node node |
instruction = [node.asInstruction(), node.(RawIndirectInstruction).getInstruction()]
|
result = getNodeProperty(node, key)
)
}
override predicate shouldPrintOperand(Operand operand) { not Ssa::ignoreOperand(operand) }
override predicate shouldPrintInstruction(Instruction instr) { not Ssa::ignoreInstruction(instr) }
}

View File

@@ -1,33 +0,0 @@
/**
* Print the dataflow local store steps in IR dumps.
*/
private import cpp
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
// that the cached IR gets the same checksum here as it does in queries that use
// `ValueNumbering` without `DataFlow`.
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import PrintIRUtilities
/**
* Property provider for local IR dataflow store steps.
*/
class LocalFlowPropertyProvider extends IRPropertyProvider {
override string getInstructionProperty(Instruction instruction, string key) {
exists(DataFlow::Node objectNode, Content content |
key = "content[" + content.toString() + "]" and
instruction = objectNode.asInstruction() and
result =
strictconcat(string element, DataFlow::Node fieldNode |
storeStep(fieldNode, content, objectNode) and
element = nodeId(fieldNode, _, _)
|
element, ", "
)
)
}
}

View File

@@ -3,37 +3,62 @@
*/
private import cpp
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
// that the cached IR gets the same checksum here as it does in queries that use
// `ValueNumbering` without `DataFlow`.
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private string stars(int k) {
k =
[0 .. max([
any(RawIndirectInstruction n).getIndirectionIndex(),
any(RawIndirectOperand n).getIndirectionIndex()
]
)] and
(if k = 0 then result = "" else result = "*" + stars(k - 1))
}
string starsForNode(Node node) {
exists(int indirectionIndex |
node.(IndirectInstruction).hasInstructionAndIndirectionIndex(_, indirectionIndex) or
node.(IndirectOperand).hasOperandAndIndirectionIndex(_, indirectionIndex)
|
result = stars(indirectionIndex)
)
or
not node instanceof IndirectInstruction and
not node instanceof IndirectOperand and
result = ""
}
private Instruction getInstruction(Node n, string stars) {
result = [n.asInstruction(), n.(RawIndirectInstruction).getInstruction()] and
stars = starsForNode(n)
}
private Operand getOperand(Node n, string stars) {
result = [n.asOperand(), n.(RawIndirectOperand).getOperand()] and
stars = starsForNode(n)
}
/**
* Gets a short ID for an IR dataflow node.
* - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`).
* - For `Operand`s, this is the label of the operand, prefixed with the result ID of the
* instruction and a dot (e.g. `m128.left`).
* - For `Variable`s, this is the qualified name of the variable.
*/
string nodeId(DataFlow::Node node, int order1, int order2) {
exists(Instruction instruction | instruction = node.asInstruction() |
result = instruction.getResultId() and
string nodeId(Node node, int order1, int order2) {
exists(Instruction instruction, string stars | instruction = getInstruction(node, stars) |
result = stars + instruction.getResultId() and
order1 = instruction.getBlock().getDisplayIndex() and
order2 = instruction.getDisplayIndexInBlock()
)
or
exists(Operand operand, Instruction instruction |
operand = node.asOperand() and
exists(Operand operand, Instruction instruction, string stars |
operand = getOperand(node, stars) and
instruction = operand.getUse()
|
result = instruction.getResultId() + "." + operand.getDumpId() and
result = stars + instruction.getResultId() + "." + operand.getDumpId() and
order1 = instruction.getBlock().getDisplayIndex() and
order2 = instruction.getDisplayIndexInBlock()
)
or
result = "var(" + node.asVariable().getQualifiedName() + ")" and
order1 = 1000000 and
order2 = 0
}

View File

@@ -1,10 +1,29 @@
import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
/**
* Provides a library for global (inter-procedural) data flow analysis of two
* values "simultaneously". This can be used, for example, if you want to track
* a memory allocation as well as the size of the allocation.
*
* Intuitively, you can think of this as regular dataflow, but where each node
* in the dataflow graph has been replaced by a pair of nodes `(node1, node2)`,
* and two node pairs `(n11, n12)`, `(n21, n22)` is then connected by a dataflow
* edge if there's a regular dataflow edge between `n11` and `n21`, and `n12`
* and `n22`.
*
* Note that the above intuition does not reflect the actual implementation.
*/
import semmle.code.cpp.dataflow.new.DataFlow
private import DataFlowPrivate
private import DataFlowUtil
private import DataFlowImplCommon
private import codeql.util.Unit
/**
* Provides classes for performing global (inter-procedural) data flow analyses
* on a product dataflow graph.
*/
module ProductFlow {
/** An input configuration for product data-flow. */
signature module ConfigSig {
/**
* Holds if `(source1, source2)` is a relevant data flow source.
@@ -70,6 +89,9 @@ module ProductFlow {
default predicate isBarrierIn2(DataFlow::Node node) { none() }
}
/**
* The output of a global data flow computation.
*/
module Global<ConfigSig Config> {
private module StateConfig implements StateConfigSig {
class FlowState1 = Unit;
@@ -138,6 +160,7 @@ module ProductFlow {
import GlobalWithState<StateConfig>
}
/** An input configuration for data flow using flow state. */
signature module StateConfigSig {
bindingset[this]
class FlowState1;
@@ -169,13 +192,13 @@ module ProductFlow {
* Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier1(DataFlow::Node node, FlowState1 state);
default predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() }
/**
* Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier2(DataFlow::Node node, FlowState2 state);
default predicate isBarrier2(DataFlow::Node node, FlowState2 state) { none() }
/**
* Holds if data flow through `node` is prohibited through the first projection of the product
@@ -214,9 +237,11 @@ module ProductFlow {
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep1(
default predicate isAdditionalFlowStep1(
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
@@ -230,9 +255,11 @@ module ProductFlow {
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep2(
default predicate isAdditionalFlowStep2(
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
@@ -247,6 +274,9 @@ module ProductFlow {
default predicate isBarrierIn2(DataFlow::Node node) { none() }
}
/**
* The output of a global data flow computation.
*/
module GlobalWithState<StateConfigSig Config> {
class PathNode1 = Flow1::PathNode;
@@ -260,6 +290,7 @@ module ProductFlow {
class FlowState2 = Config::FlowState2;
/** Holds if data can flow from `(source1, source2)` to `(sink1, sink2)`. */
predicate flowPath(
Flow1::PathNode source1, Flow2::PathNode source2, Flow1::PathNode sink1, Flow2::PathNode sink2
) {
@@ -290,9 +321,9 @@ module ProductFlow {
predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn1(node) }
}
module Flow1 = DataFlow::GlobalWithState<Config1>;
private module Flow1 = DataFlow::GlobalWithState<Config1>;
module Config2 implements DataFlow::StateConfigSig {
private module Config2 implements DataFlow::StateConfigSig {
class FlowState = FlowState2;
predicate isSource(DataFlow::Node source, FlowState state) {
@@ -322,27 +353,87 @@ module ProductFlow {
predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn2(node) }
}
module Flow2 = DataFlow::GlobalWithState<Config2>;
private module Flow2 = DataFlow::GlobalWithState<Config2>;
private predicate isSourcePair(Flow1::PathNode node1, Flow2::PathNode node2) {
Config::isSourcePair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState())
}
private predicate isSinkPair(Flow1::PathNode node1, Flow2::PathNode node2) {
Config::isSinkPair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState())
}
pragma[nomagic]
private predicate reachableInterprocEntry(
Flow1::PathNode source1, Flow2::PathNode source2, Flow1::PathNode node1, Flow2::PathNode node2
) {
Config::isSourcePair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState()) and
node1 = source1 and
node2 = source2
private predicate fwdReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) {
isSourcePair(node1, node2)
or
exists(
Flow1::PathNode midEntry1, Flow2::PathNode midEntry2, Flow1::PathNode midExit1,
Flow2::PathNode midExit2
|
reachableInterprocEntry(source1, source2, midEntry1, midEntry2) and
interprocEdgePair(midExit1, midExit2, node1, node2) and
localPathStep1*(midEntry1, midExit1) and
localPathStep2*(midEntry2, midExit2)
fwdIsSuccessor(_, _, node1, node2)
}
pragma[nomagic]
private predicate fwdIsSuccessorExit(
Flow1::PathNode mid1, Flow2::PathNode mid2, Flow1::PathNode succ1, Flow2::PathNode succ2
) {
isSinkPair(mid1, mid2) and
succ1 = mid1 and
succ2 = mid2
or
interprocEdgePair(mid1, mid2, succ1, succ2)
}
private predicate fwdIsSuccessor1(
Flow1::PathNode pred1, Flow2::PathNode pred2, Flow1::PathNode mid1, Flow2::PathNode mid2,
Flow1::PathNode succ1, Flow2::PathNode succ2
) {
fwdReachableInterprocEntry(pred1, pred2) and
localPathStep1*(pred1, mid1) and
fwdIsSuccessorExit(pragma[only_bind_into](mid1), pragma[only_bind_into](mid2), succ1, succ2)
}
private predicate fwdIsSuccessor2(
Flow1::PathNode pred1, Flow2::PathNode pred2, Flow1::PathNode mid1, Flow2::PathNode mid2,
Flow1::PathNode succ1, Flow2::PathNode succ2
) {
fwdReachableInterprocEntry(pred1, pred2) and
localPathStep2*(pred2, mid2) and
fwdIsSuccessorExit(pragma[only_bind_into](mid1), pragma[only_bind_into](mid2), succ1, succ2)
}
private predicate fwdIsSuccessor(
Flow1::PathNode pred1, Flow2::PathNode pred2, Flow1::PathNode succ1, Flow2::PathNode succ2
) {
exists(Flow1::PathNode mid1, Flow2::PathNode mid2 |
fwdIsSuccessor1(pred1, pred2, mid1, mid2, succ1, succ2) and
fwdIsSuccessor2(pred1, pred2, mid1, mid2, succ1, succ2)
)
}
pragma[nomagic]
private predicate revReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) {
fwdReachableInterprocEntry(node1, node2) and
isSinkPair(node1, node2)
or
exists(Flow1::PathNode succ1, Flow2::PathNode succ2 |
revReachableInterprocEntry(succ1, succ2) and
fwdIsSuccessor(node1, node2, succ1, succ2)
)
}
private newtype TNodePair =
TMkNodePair(Flow1::PathNode node1, Flow2::PathNode node2) {
revReachableInterprocEntry(node1, node2)
}
private predicate pathSucc(TNodePair n1, TNodePair n2) {
exists(Flow1::PathNode n11, Flow2::PathNode n12, Flow1::PathNode n21, Flow2::PathNode n22 |
n1 = TMkNodePair(n11, n12) and
n2 = TMkNodePair(n21, n22) and
fwdIsSuccessor(n11, n12, n21, n22)
)
}
private predicate pathSuccPlus(TNodePair n1, TNodePair n2) = fastTC(pathSucc/2)(n1, n2)
private predicate localPathStep1(Flow1::PathNode pred, Flow1::PathNode succ) {
Flow1::PathGraph::edges(pred, succ) and
pragma[only_bind_out](pred.getNode().getEnclosingCallable()) =
@@ -474,11 +565,14 @@ module ProductFlow {
private predicate reachable(
Flow1::PathNode source1, Flow2::PathNode source2, Flow1::PathNode sink1, Flow2::PathNode sink2
) {
exists(Flow1::PathNode mid1, Flow2::PathNode mid2 |
reachableInterprocEntry(source1, source2, mid1, mid2) and
Config::isSinkPair(sink1.getNode(), sink1.getState(), sink2.getNode(), sink2.getState()) and
localPathStep1*(mid1, sink1) and
localPathStep2*(mid2, sink2)
isSourcePair(source1, source2) and
isSinkPair(sink1, sink2) and
exists(TNodePair n1, TNodePair n2 |
n1 = TMkNodePair(source1, source2) and
n2 = TMkNodePair(sink1, sink2)
|
pathSuccPlus(n1, n2) or
n1 = n2
)
}
}

View File

@@ -364,7 +364,25 @@ abstract private class OperandBasedUse extends UseImpl {
OperandBasedUse() { any() }
final override predicate hasIndexInBlock(IRBlock block, int index) {
operand.getUse() = block.getInstruction(index)
// See the comment in `ssa0`'s `OperandBasedUse` for an explanation of this
// predicate's implementation.
exists(BaseSourceVariableInstruction base | base = this.getBase() |
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
then
exists(Operand op, int indirectionIndex, int indirection |
indirectionIndex = this.getIndirectionIndex() and
indirection = this.getIndirection() and
op =
min(Operand cand, int i |
isUse(_, cand, base, indirection, indirectionIndex) and
block.getInstruction(i) = cand.getUse()
|
cand order by i
) and
block.getInstruction(index) = op.getUse()
)
else operand.getUse() = block.getInstruction(index)
)
}
final Operand getOperand() { result = operand }
@@ -657,24 +675,16 @@ private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
* So this predicate recurses back along conversions and `PointerArithmeticInstruction`s to find the
* first use that has provides use-use flow, and uses that target as the target of the `nodeFrom`.
*/
private predicate adjustForPointerArith(
DefOrUse defOrUse, Node nodeFrom, UseOrPhi use, boolean uncertain
) {
nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and
exists(Node adjusted |
indirectConversionFlowStep*(adjusted, nodeFrom) and
nodeToDefOrUse(adjusted, defOrUse, uncertain) and
private predicate adjustForPointerArith(PostUpdateNode pun, UseOrPhi use) {
exists(DefOrUse defOrUse, Node adjusted |
indirectConversionFlowStep*(adjusted, pun.getPreUpdateNode()) and
nodeToDefOrUse(adjusted, defOrUse, _) and
adjacentDefRead(defOrUse, use)
)
}
private predicate ssaFlowImpl(SsaDefOrUse defOrUse, Node nodeFrom, Node nodeTo, boolean uncertain) {
// `nodeFrom = any(PostUpdateNode pun).getPreUpdateNode()` is implied by adjustedForPointerArith.
exists(UseOrPhi use |
adjustForPointerArith(defOrUse, nodeFrom, use, uncertain) and
useToNode(use, nodeTo)
or
not nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and
nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and
adjacentDefRead(defOrUse, use) and
useToNode(use, nodeTo) and
@@ -719,14 +729,19 @@ predicate ssaFlow(Node nodeFrom, Node nodeTo) {
)
}
private predicate isArgumentOfCallable(DataFlowCall call, ArgumentNode arg) {
arg.argumentOf(call, _)
}
/** Holds if there is def-use or use-use flow from `pun` to `nodeTo`. */
predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
exists(Node preUpdate, Node nFrom, boolean uncertain, SsaDefOrUse defOrUse |
exists(UseOrPhi use, Node preUpdate |
adjustForPointerArith(pun, use) and
useToNode(use, nodeTo) and
preUpdate = pun.getPreUpdateNode() and
ssaFlowImpl(defOrUse, nFrom, nodeTo, uncertain)
|
if uncertain = true
then preUpdate = [nFrom, getAPriorDefinition(defOrUse)]
else preUpdate = nFrom
not exists(DataFlowCall call |
isArgumentOfCallable(call, preUpdate) and isArgumentOfCallable(call, nodeTo)
)
)
}

View File

@@ -117,6 +117,16 @@ private int countIndirections(Type t) {
else (
result = any(Indirection ind | ind.getType() = t).getNumberOfIndirections()
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
result = 0
)
@@ -144,6 +154,20 @@ class AllocationInstruction extends CallInstruction {
AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction }
}
private predicate isIndirectionType(Type t) { t instanceof Indirection }
private predicate hasUnspecifiedBaseType(Indirection t, Type base) {
base = t.getBaseType().getUnspecifiedType()
}
/**
* Holds if `t2` is the same type as `t1`, but after stripping away `result` number
* of indirections.
* Furthermore, specifies in `t2` been deeply stripped and typedefs has been resolved.
*/
private int getNumberOfIndirectionsImpl(Type t1, Type t2) =
shortestDistances(isIndirectionType/1, hasUnspecifiedBaseType/2)(t1, t2, result)
/**
* An abstract class for handling indirections.
*
@@ -162,7 +186,10 @@ abstract class Indirection extends Type {
* For example, the number of indirections of a variable `p` of type
* `int**` is `3` (i.e., `p`, `*p` and `**p`).
*/
abstract int getNumberOfIndirections();
final int getNumberOfIndirections() {
result =
getNumberOfIndirectionsImpl(this.getType(), any(Type end | not end instanceof Indirection))
}
/**
* Holds if `deref` is an instruction that behaves as a `LoadInstruction`
@@ -200,19 +227,11 @@ private class PointerOrArrayOrReferenceTypeIndirection extends Indirection insta
PointerOrArrayOrReferenceTypeIndirection() {
baseType = PointerOrArrayOrReferenceType.super.getBaseType()
}
override int getNumberOfIndirections() {
result = 1 + countIndirections(this.getBaseType().getUnspecifiedType())
}
}
private class PointerWrapperTypeIndirection extends Indirection instanceof PointerWrapper {
PointerWrapperTypeIndirection() { baseType = PointerWrapper.super.getBaseType() }
override int getNumberOfIndirections() {
result = 1 + countIndirections(this.getBaseType().getUnspecifiedType())
}
override predicate isAdditionalDereference(Instruction deref, Operand address) {
exists(CallInstruction call |
operandForFullyConvertedCall(getAUse(deref), call) and
@@ -233,10 +252,6 @@ private module IteratorIndirections {
baseType = super.getValueType()
}
override int getNumberOfIndirections() {
result = 1 + countIndirections(this.getBaseType().getUnspecifiedType())
}
override predicate isAdditionalDereference(Instruction deref, Operand address) {
exists(CallInstruction call |
operandForFullyConvertedCall(getAUse(deref), call) and
@@ -258,7 +273,7 @@ private module IteratorIndirections {
// Taint through `operator+=` and `operator-=` on iterators.
call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and
node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and
node1.(IndirectOperand).getOperand() = call.getArgumentOperand(0) and
node1.(IndirectOperand).hasOperandAndIndirectionIndex(call.getArgumentOperand(0), _) and
node1.getType().getUnspecifiedType() = this
)
}
@@ -573,7 +588,6 @@ private module Cached {
)
}
pragma[assume_small_delta]
private predicate convertsIntoArgumentRev(Instruction instr) {
convertsIntoArgumentFwd(instr) and
(
@@ -791,7 +805,7 @@ private module Cached {
address.getDef() = instr and
isDereference(load, address) and
isUseImpl(address, _, indirectionIndex - 1) and
result = instr
result = load
)
}

View File

@@ -160,7 +160,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
FunctionInput modelIn, FunctionOutput modelOut
|
indirectArgument = callInput(call, modelIn) and
indirectArgument.getAddressOperand() = nodeIn.asOperand() and
indirectArgument.hasAddressOperandAndIndirectionIndex(nodeIn.asOperand(), _) and
call.getStaticCallTarget() = func and
(
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)

View File

@@ -122,7 +122,46 @@ abstract private class OperandBasedUse extends UseImpl {
override string toString() { result = operand.toString() }
final override predicate hasIndexInBlock(IRBlock block, int index) {
operand.getUse() = block.getInstruction(index)
// Ideally, this would just be implemented as:
// ```
// operand.getUse() = block.getInstruction(index)
// ```
// but because the IR generated for a snippet such as
// ```
// int x = *p++;
// ```
// looks like
// ```
// r1(glval<int>) = VariableAddress[x] :
// r2(glval<int *>) = VariableAddress[p] :
// r3(int *) = Load[p] : &:r2, m1
// r4(int) = Constant[1] :
// r5(int *) = PointerAdd[4] : r3, r4
// m3(int *) = Store[p] : &:r2, r5
// r6(int *) = CopyValue : r3
// r7(int) = Load[?] : &:r6, ~m2
// m2(int) = Store[x] : &:r1, r7
// ```
// we need to ensure that the `r3` operand of the `CopyValue` instruction isn't seen as a fresh use
// of `p` that happens after the increment. So if the base instruction of this use comes from a
// post-fix crement operation we set the index of the SSA use that wraps the `r3` operand at the
// `CopyValue` instruction to be the same index as the `r3` operand at the `PointerAdd` instruction.
// This ensures that the SSA library doesn't create flow from the `PointerAdd` to `r6`.
exists(BaseSourceVariableInstruction base | base = this.getBase() |
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
then
exists(Operand op |
op =
min(Operand cand, int i |
isUse(_, cand, base, _, _) and
block.getInstruction(i) = cand.getUse()
|
cand order by i
) and
block.getInstruction(index) = op.getUse()
)
else operand.getUse() = block.getInstruction(index)
)
}
final override Cpp::Location getLocation() { result = operand.getLocation() }

View File

@@ -77,4 +77,16 @@ class IRPropertyProvider extends TIRPropertyProvider {
* Gets the value of the property named `key` for the specified operand.
*/
string getOperandProperty(Operand operand, string key) { none() }
/**
* Holds if the instruction `instr` should be included when printing
* the IR instructions.
*/
predicate shouldPrintInstruction(Instruction instr) { any() }
/**
* Holds if the operand `operand` should be included when printing the an
* instruction's operand list.
*/
predicate shouldPrintOperand(Operand operand) { any() }
}

View File

@@ -210,9 +210,6 @@ class Instruction extends Construction::TStageInstruction {
*/
final Language::AST getAst() { result = Construction::getInstructionAst(this) }
/** DEPRECATED: Alias for getAst */
deprecated Language::AST getAST() { result = this.getAst() }
/**
* Gets the location of the source code for this instruction.
*/
@@ -463,9 +460,6 @@ class VariableInstruction extends Instruction {
* Gets the AST variable that this instruction's IR variable refers to, if one exists.
*/
final Language::Variable getAstVariable() { result = var.(IRUserVariable).getVariable() }
/** DEPRECATED: Alias for getAstVariable */
deprecated Language::Variable getASTVariable() { result = this.getAstVariable() }
}
/**

View File

@@ -42,6 +42,14 @@ private predicate shouldPrintFunction(Language::Declaration decl) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
}
private predicate shouldPrintInstruction(Instruction i) {
exists(IRPropertyProvider provider | provider.shouldPrintInstruction(i))
}
private predicate shouldPrintOperand(Operand operand) {
exists(IRPropertyProvider provider | provider.shouldPrintOperand(operand))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
}
@@ -84,7 +92,9 @@ private string getOperandPropertyString(Operand operand) {
private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
TPrintableInstruction(Instruction instr) { shouldPrintFunction(instr.getEnclosingFunction()) }
TPrintableInstruction(Instruction instr) {
shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction())
}
/**
* A node to be emitted in the IR graph.
@@ -252,7 +262,8 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
private string getOperandsString() {
result =
concat(Operand operand |
operand = instr.getAnOperand()
operand = instr.getAnOperand() and
shouldPrintOperand(operand)
|
operand.getDumpString() + getOperandPropertyString(operand), ", "
order by

View File

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

View File

@@ -577,9 +577,6 @@ private Overlap getVariableMemoryLocationOverlap(
*/
predicate canReuseSsaForOldResult(Instruction instr) { OldSsa::canReuseSsaForMemoryResult(instr) }
/** DEPRECATED: Alias for canReuseSsaForOldResult */
deprecated predicate canReuseSSAForOldResult = canReuseSsaForOldResult/1;
bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }

View File

@@ -422,12 +422,6 @@ private module Cached {
)
}
/** DEPRECATED: Alias for getInstructionAst */
cached
deprecated Language::AST getInstructionAST(Instruction instr) {
result = getInstructionAst(instr)
}
cached
Language::LanguageType getInstructionResultType(Instruction instr) {
result = instr.(RawIR::Instruction).getResultLanguageType()
@@ -993,9 +987,6 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
// We don't support reusing SSA for any location that could create a `Chi` instruction.
}
/** DEPRECATED: Alias for canReuseSsaForMemoryResult */
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
/**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
* `DebugSsa` module, which is then imported by PrintSSA.
@@ -1005,9 +996,6 @@ module DebugSsa {
import DefUse
}
/** DEPRECATED: Alias for DebugSsa */
deprecated module DebugSSA = DebugSsa;
import CachedForDebugging
cached

View File

@@ -73,9 +73,6 @@ module UnaliasedSsaInstructions {
}
}
/** DEPRECATED: Alias for UnaliasedSsaInstructions */
deprecated module UnaliasedSSAInstructions = UnaliasedSsaInstructions;
/**
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
* aliased SSA stage.
@@ -107,6 +104,3 @@ module AliasedSsaInstructions {
result = TAliasedSsaUnreachedInstruction(irFunc)
}
}
/** DEPRECATED: Alias for AliasedSsaInstructions */
deprecated module AliasedSSAInstructions = AliasedSsaInstructions;

View File

@@ -74,20 +74,12 @@ private module Shared {
class TNonSsaMemoryOperand = Internal::TNonSsaMemoryOperand;
/** DEPRECATED: Alias for TNonSsaMemoryOperand */
deprecated class TNonSSAMemoryOperand = TNonSsaMemoryOperand;
/**
* Returns the non-Phi memory operand with the specified parameters.
*/
TNonSsaMemoryOperand nonSsaMemoryOperand(TRawInstruction useInstr, MemoryOperandTag tag) {
result = Internal::TNonSsaMemoryOperand(useInstr, tag)
}
/** DEPRECATED: Alias for nonSsaMemoryOperand */
deprecated TNonSSAMemoryOperand nonSSAMemoryOperand(TRawInstruction useInstr, MemoryOperandTag tag) {
result = nonSsaMemoryOperand(useInstr, tag)
}
}
/**
@@ -167,9 +159,6 @@ module UnaliasedSsaOperands {
TChiOperand chiOperand(Unaliased::Instruction useInstr, ChiOperandTag tag) { none() }
}
/** DEPRECATED: Alias for UnaliasedSsaOperands */
deprecated module UnaliasedSSAOperands = UnaliasedSsaOperands;
/**
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
* aliased SSA stage.
@@ -217,6 +206,3 @@ module AliasedSsaOperands {
result = Internal::TAliasedChiOperand(useInstr, tag)
}
}
/** DEPRECATED: Alias for AliasedSsaOperands */
deprecated module AliasedSSAOperands = AliasedSsaOperands;

View File

@@ -77,4 +77,16 @@ class IRPropertyProvider extends TIRPropertyProvider {
* Gets the value of the property named `key` for the specified operand.
*/
string getOperandProperty(Operand operand, string key) { none() }
/**
* Holds if the instruction `instr` should be included when printing
* the IR instructions.
*/
predicate shouldPrintInstruction(Instruction instr) { any() }
/**
* Holds if the operand `operand` should be included when printing the an
* instruction's operand list.
*/
predicate shouldPrintOperand(Operand operand) { any() }
}

View File

@@ -210,9 +210,6 @@ class Instruction extends Construction::TStageInstruction {
*/
final Language::AST getAst() { result = Construction::getInstructionAst(this) }
/** DEPRECATED: Alias for getAst */
deprecated Language::AST getAST() { result = this.getAst() }
/**
* Gets the location of the source code for this instruction.
*/
@@ -463,9 +460,6 @@ class VariableInstruction extends Instruction {
* Gets the AST variable that this instruction's IR variable refers to, if one exists.
*/
final Language::Variable getAstVariable() { result = var.(IRUserVariable).getVariable() }
/** DEPRECATED: Alias for getAstVariable */
deprecated Language::Variable getASTVariable() { result = this.getAstVariable() }
}
/**

View File

@@ -42,6 +42,14 @@ private predicate shouldPrintFunction(Language::Declaration decl) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
}
private predicate shouldPrintInstruction(Instruction i) {
exists(IRPropertyProvider provider | provider.shouldPrintInstruction(i))
}
private predicate shouldPrintOperand(Operand operand) {
exists(IRPropertyProvider provider | provider.shouldPrintOperand(operand))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
}
@@ -84,7 +92,9 @@ private string getOperandPropertyString(Operand operand) {
private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
TPrintableInstruction(Instruction instr) { shouldPrintFunction(instr.getEnclosingFunction()) }
TPrintableInstruction(Instruction instr) {
shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction())
}
/**
* A node to be emitted in the IR graph.
@@ -252,7 +262,8 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
private string getOperandsString() {
result =
concat(Operand operand |
operand = instr.getAnOperand()
operand = instr.getAnOperand() and
shouldPrintOperand(operand)
|
operand.getDumpString() + getOperandPropertyString(operand), ", "
order by

View File

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

View File

@@ -375,11 +375,6 @@ Locatable getInstructionAst(TStageInstruction instr) {
)
}
/** DEPRECATED: Alias for getInstructionAst */
deprecated Locatable getInstructionAST(TStageInstruction instr) {
result = getInstructionAst(instr)
}
CppType getInstructionResultType(TStageInstruction instr) {
getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result)
or

View File

@@ -76,9 +76,6 @@ abstract class TranslatedExpr extends TranslatedElement {
final override Locatable getAst() { result = expr }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = this.getAst() }
final override Declaration getFunction() { result = getEnclosingDeclaration(expr) }
/**

View File

@@ -77,4 +77,16 @@ class IRPropertyProvider extends TIRPropertyProvider {
* Gets the value of the property named `key` for the specified operand.
*/
string getOperandProperty(Operand operand, string key) { none() }
/**
* Holds if the instruction `instr` should be included when printing
* the IR instructions.
*/
predicate shouldPrintInstruction(Instruction instr) { any() }
/**
* Holds if the operand `operand` should be included when printing the an
* instruction's operand list.
*/
predicate shouldPrintOperand(Operand operand) { any() }
}

View File

@@ -210,9 +210,6 @@ class Instruction extends Construction::TStageInstruction {
*/
final Language::AST getAst() { result = Construction::getInstructionAst(this) }
/** DEPRECATED: Alias for getAst */
deprecated Language::AST getAST() { result = this.getAst() }
/**
* Gets the location of the source code for this instruction.
*/
@@ -463,9 +460,6 @@ class VariableInstruction extends Instruction {
* Gets the AST variable that this instruction's IR variable refers to, if one exists.
*/
final Language::Variable getAstVariable() { result = var.(IRUserVariable).getVariable() }
/** DEPRECATED: Alias for getAstVariable */
deprecated Language::Variable getASTVariable() { result = this.getAstVariable() }
}
/**

View File

@@ -42,6 +42,14 @@ private predicate shouldPrintFunction(Language::Declaration decl) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
}
private predicate shouldPrintInstruction(Instruction i) {
exists(IRPropertyProvider provider | provider.shouldPrintInstruction(i))
}
private predicate shouldPrintOperand(Operand operand) {
exists(IRPropertyProvider provider | provider.shouldPrintOperand(operand))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
}
@@ -84,7 +92,9 @@ private string getOperandPropertyString(Operand operand) {
private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
TPrintableInstruction(Instruction instr) { shouldPrintFunction(instr.getEnclosingFunction()) }
TPrintableInstruction(Instruction instr) {
shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction())
}
/**
* A node to be emitted in the IR graph.
@@ -252,7 +262,8 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
private string getOperandsString() {
result =
concat(Operand operand |
operand = instr.getAnOperand()
operand = instr.getAnOperand() and
shouldPrintOperand(operand)
|
operand.getDumpString() + getOperandPropertyString(operand), ", "
order by

View File

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

View File

@@ -422,12 +422,6 @@ private module Cached {
)
}
/** DEPRECATED: Alias for getInstructionAst */
cached
deprecated Language::AST getInstructionAST(Instruction instr) {
result = getInstructionAst(instr)
}
cached
Language::LanguageType getInstructionResultType(Instruction instr) {
result = instr.(RawIR::Instruction).getResultLanguageType()
@@ -993,9 +987,6 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
// We don't support reusing SSA for any location that could create a `Chi` instruction.
}
/** DEPRECATED: Alias for canReuseSsaForMemoryResult */
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
/**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
* `DebugSsa` module, which is then imported by PrintSSA.
@@ -1005,9 +996,6 @@ module DebugSsa {
import DefUse
}
/** DEPRECATED: Alias for DebugSsa */
deprecated module DebugSSA = DebugSsa;
import CachedForDebugging
cached

View File

@@ -46,9 +46,6 @@ predicate canReuseSsaForVariable(IRAutomaticVariable var) {
not allocationEscapes(var)
}
/** DEPRECATED: Alias for canReuseSsaForVariable */
deprecated predicate canReuseSSAForVariable = canReuseSsaForVariable/1;
private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) }
private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var }
@@ -80,9 +77,6 @@ class MemoryLocation extends TMemoryLocation {
predicate canReuseSsaForOldResult(Instruction instr) { none() }
/** DEPRECATED: Alias for canReuseSsaForOldResult */
deprecated predicate canReuseSSAForOldResult = canReuseSsaForOldResult/1;
/**
* Represents a set of `MemoryLocation`s that cannot overlap with
* `MemoryLocation`s outside of the set. The `VirtualVariable` will be

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
// buffer
exists(this.getParamSize()) and
input.isParameter(this.getParamSrc()) and
input.isParameterDeref(this.getParamSrc()) and
(
output.isParameterDeref(this.getParamDest()) or
output.isReturnValueDeref()

View File

@@ -40,9 +40,6 @@ library class RangeSsa extends SsaHelper {
}
}
/** DEPRECATED: Alias for RangeSsa */
deprecated class RangeSSA = RangeSsa;
private predicate guard_defn(VariableAccess v, Expr guard, BasicBlock b, boolean branch) {
guardCondition(guard, v, branch) and
guardSuccessor(guard, branch, b)

View File

@@ -70,6 +70,27 @@ predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionP
// Conservatively assume that every edge is a back edge if we don't have dominance information.
(
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
irreducibleSccEdge(edge.getOrigBlock(), phi.getBasicBlock()) or
not edge.getOrigBlock().hasDominanceInformation()
)
}
/**
* Holds if the edge from b1 to b2 is part of a multiple-entry cycle in an irreducible control flow
* graph.
*
* An ireducible control flow graph is one where the usual dominance-based back edge detection does
* not work, because there is a cycle with multiple entry points, meaning there are
* mutually-reachable basic blocks where neither dominates the other. For such a graph, we first
* remove all detectable back-edges using the normal condition that the predecessor block is
* dominated by the successor block, then mark all edges in a cycle in the resulting graph as back
* edges.
*/
private predicate irreducibleSccEdge(SemBasicBlock b1, SemBasicBlock b2) {
trimmedEdge(b1, b2) and trimmedEdge+(b2, b1)
}
private predicate trimmedEdge(SemBasicBlock pred, SemBasicBlock succ) {
pred.getASuccessor() = succ and
not succ.bbDominates(pred)
}

View File

@@ -729,7 +729,7 @@ module RangeStage<
) {
exists(SemExpr e, D::Delta d1, D::Delta d2 |
unequalFlowStepIntegralSsa(v, pos, e, d1, reason) and
boundedUpper(e, b, d1) and
boundedUpper(e, b, d2) and
boundedLower(e, b, d2) and
delta = D::fromFloat(D::toFloat(d1) + D::toFloat(d2))
)
@@ -877,7 +877,6 @@ module RangeStage<
)
}
pragma[assume_small_delta]
pragma[nomagic]
private predicate boundedPhiRankStep(
SemSsaPhiNode phi, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge,

View File

@@ -1,3 +1,23 @@
## 0.7.0
### Minor Analysis Improvements
* The `cpp/comparison-with-wider-type` query now correctly handles relational operations on signed operators. As a result the query may find more results.
## 0.6.4
No user-facing changes.
## 0.6.3
### New Queries
* Added a new query, `cpp/overrun-write`, to detect buffer overflows in C-style functions that manipulate buffers.
## 0.6.2
No user-facing changes.
## 0.6.1
### New Queries

View File

@@ -88,14 +88,6 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
e = any(StoreInstruction store).getDestinationAddress().getUnconvertedResultExpression()
)
}
predicate isBarrier(DataFlow::Node n, FlowState state) { none() }
predicate isAdditionalFlowStep(
DataFlow::Node n1, FlowState state1, DataFlow::Node n2, FlowState state2
) {
none()
}
}
import DataFlow::GlobalWithState<FlowFromFreeConfig>

View File

@@ -135,18 +135,24 @@ module ParameterSinks {
}
}
predicate isUse(DataFlow::Node n, Expr e) {
isUse0(n, e)
or
exists(CallInstruction call, int i, InitializeParameterInstruction init |
n.asOperand().getDef().getUnconvertedResultExpression() = e and
init = ParameterSinks::getAnAlwaysDereferencedParameter() and
call.getArgumentOperand(i) = n.asOperand() and
init.hasIndex(i) and
init.getEnclosingFunction() = call.getStaticCallTarget()
)
module IsUse {
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
predicate isUse(DataFlow::Node n, Expr e) {
isUse0(n, e)
or
exists(CallInstruction call, InitializeParameterInstruction init |
n.asOperand().getDef().getUnconvertedResultExpression() = e and
pragma[only_bind_into](init) = ParameterSinks::getAnAlwaysDereferencedParameter() and
viableParamArg(call, DataFlow::instructionNode(init), n) and
pragma[only_bind_out](init.getEnclosingFunction()) =
pragma[only_bind_out](call.getStaticCallTarget())
)
}
}
import IsUse
/**
* `dealloc1` is a deallocation expression, `e` is an expression that dereferences a
* pointer, and the `(dealloc1, e)` pair should be excluded by the `FlowFromFree` library.

View File

@@ -44,14 +44,6 @@ module CastToPointerArithFlowConfig implements DataFlow::StateConfigSig {
) and
getFullyConvertedType(node) = state
}
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
none()
}
}
/**

View File

@@ -72,6 +72,11 @@ VariableAccess commonException() {
or
result.getParent() instanceof BuiltInOperation
or
// Ignore any uninitialized use that is explicitly cast to void and
// is an expression statement.
result.getActualType() instanceof VoidType and
result.getParent() instanceof ExprStmt
or
// Finally, exclude functions that contain assembly blocks. It's
// anyone's guess what happens in those.
containsInlineAssembly(result.getEnclosingFunction())

View File

@@ -16,9 +16,6 @@ class UntrustedExternalApiDataNode extends ExternalApiDataNode {
DataFlow::Node getAnUntrustedSource() { UntrustedDataToExternalApiFlow::flow(result, this) }
}
/** DEPRECATED: Alias for UntrustedExternalApiDataNode */
deprecated class UntrustedExternalAPIDataNode = UntrustedExternalApiDataNode;
/** An external API which is used with untrusted data. */
private newtype TExternalApi =
/** An untrusted API method `m` where untrusted data is passed at `index`. */
@@ -51,6 +48,3 @@ class ExternalApiUsedWithUntrustedData extends TExternalApi {
)
}
}
/** DEPRECATED: Alias for ExternalApiUsedWithUntrustedData */
deprecated class ExternalAPIUsedWithUntrustedData = ExternalApiUsedWithUntrustedData;

View File

@@ -41,9 +41,6 @@ class ExternalApiDataNode extends DataFlow::Node {
string getFunctionDescription() { result = this.getExternalFunction().toString() }
}
/** DEPRECATED: Alias for ExternalApiDataNode */
deprecated class ExternalAPIDataNode = ExternalApiDataNode;
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalApiDataNode`s. */
deprecated class UntrustedDataToExternalApiConfig extends TaintTracking::Configuration {
UntrustedDataToExternalApiConfig() { this = "UntrustedDataToExternalAPIConfig" }
@@ -58,9 +55,6 @@ deprecated class UntrustedDataToExternalApiConfig extends TaintTracking::Configu
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalApiDataNode }
}
/** DEPRECATED: Alias for UntrustedDataToExternalApiConfig */
deprecated class UntrustedDataToExternalAPIConfig = UntrustedDataToExternalApiConfig;
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalApiDataNode`s. */
private module UntrustedDataToExternalApiConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {

View File

@@ -10,9 +10,6 @@ private import semmle.code.cpp.models.interfaces.SideEffect
*/
abstract class SafeExternalApiFunction extends Function { }
/** DEPRECATED: Alias for SafeExternalApiFunction */
deprecated class SafeExternalAPIFunction = SafeExternalApiFunction;
/** The default set of "safe" external APIs. */
private class DefaultSafeExternalApiFunction extends SafeExternalApiFunction {
DefaultSafeExternalApiFunction() {

View File

@@ -16,9 +16,6 @@ class UntrustedExternalApiDataNode extends ExternalApiDataNode {
DataFlow::Node getAnUntrustedSource() { UntrustedDataToExternalApiFlow::flow(result, this) }
}
/** DEPRECATED: Alias for UntrustedExternalApiDataNode */
deprecated class UntrustedExternalAPIDataNode = UntrustedExternalApiDataNode;
/** An external API which is used with untrusted data. */
private newtype TExternalApi =
/** An untrusted API method `m` where untrusted data is passed at `index`. */
@@ -51,6 +48,3 @@ class ExternalApiUsedWithUntrustedData extends TExternalApi {
)
}
}
/** DEPRECATED: Alias for ExternalApiUsedWithUntrustedData */
deprecated class ExternalAPIUsedWithUntrustedData = ExternalApiUsedWithUntrustedData;

View File

@@ -41,9 +41,6 @@ class ExternalApiDataNode extends DataFlow::Node {
string getFunctionDescription() { result = this.getExternalFunction().toString() }
}
/** DEPRECATED: Alias for ExternalApiDataNode */
deprecated class ExternalAPIDataNode = ExternalApiDataNode;
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalApiDataNode`s. */
deprecated class UntrustedDataToExternalApiConfig extends TaintTracking::Configuration {
UntrustedDataToExternalApiConfig() { this = "UntrustedDataToExternalAPIConfigIR" }
@@ -53,9 +50,6 @@ deprecated class UntrustedDataToExternalApiConfig extends TaintTracking::Configu
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalApiDataNode }
}
/** DEPRECATED: Alias for UntrustedDataToExternalApiConfig */
deprecated class UntrustedDataToExternalAPIConfig = UntrustedDataToExternalApiConfig;
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalApiDataNode`s. */
private module UntrustedDataToExternalApiConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }

View File

@@ -10,9 +10,6 @@ private import semmle.code.cpp.models.interfaces.SideEffect
*/
abstract class SafeExternalApiFunction extends Function { }
/** DEPRECATED: Alias for SafeExternalApiFunction */
deprecated class SafeExternalAPIFunction = SafeExternalApiFunction;
/** The default set of "safe" external APIs. */
private class DefaultSafeExternalApiFunction extends SafeExternalApiFunction {
DefaultSafeExternalApiFunction() {

View File

@@ -134,8 +134,6 @@ module ExecTaintConfig implements DataFlow::StateConfigSig {
predicate isBarrier(DataFlow::Node node) { isBarrierImpl(node) }
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
predicate isBarrierOut(DataFlow::Node node) {
isSink(node, _) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
}

View File

@@ -0,0 +1,306 @@
/**
* @name Overrunning write
* @description Exceeding the size of a static array during write or access operations
* may result in a buffer overflow.
* @kind path-problem
* @problem.severity error
* @security-severity 9.3
* @precision medium
* @id cpp/overrun-write
* @tags reliability
* security
* external/cwe/cwe-119
* external/cwe/cwe-131
*/
import cpp
import semmle.code.cpp.ir.dataflow.internal.ProductFlow
import semmle.code.cpp.ir.IR
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysis
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific
import StringSizeFlow::PathGraph1
import codeql.util.Unit
pragma[nomagic]
Instruction getABoundIn(SemBound b, IRFunction func) {
getSemanticExpr(result) = b.getExpr(0) and
result.getEnclosingIRFunction() = func
}
/**
* Holds if `i <= b + delta`.
*/
bindingset[i]
pragma[inline_late]
predicate bounded(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
)
}
VariableAccess getAVariableAccess(Expr e) { e.getAChild*() = result }
/**
* Holds if `(n, state)` pair represents the source of flow for the size
* expression associated with `alloc`.
*/
predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
exists(VariableAccess va, Expr size, int delta |
size = alloc.getSizeExpr() and
// Get the unique variable in a size expression like `x` in `malloc(x + 1)`.
va = unique( | | getAVariableAccess(size)) and
// Compute `delta` as the constant difference between `x` and `x + 1`.
bounded(any(Instruction instr | instr.getUnconvertedResultExpression() = size),
any(LoadInstruction load | load.getUnconvertedResultExpression() = va), delta) and
n.asConvertedExpr() = va.getFullyConverted() and
state = delta
)
}
predicate isSinkPairImpl(
CallInstruction c, DataFlow::Node bufSink, DataFlow::Node sizeSink, int delta, Expr eBuf
) {
exists(
int bufIndex, int sizeIndex, Instruction sizeInstr, Instruction bufInstr, ArrayFunction func
|
bufInstr = bufSink.asInstruction() and
c.getArgument(bufIndex) = bufInstr and
sizeInstr = sizeSink.asInstruction() and
c.getStaticCallTarget() = func and
pragma[only_bind_into](func)
.hasArrayWithVariableSize(pragma[only_bind_into](bufIndex),
pragma[only_bind_into](sizeIndex)) and
bounded(c.getArgument(sizeIndex), sizeInstr, delta) and
eBuf = bufInstr.getUnconvertedResultExpression()
)
}
module ValidState {
/**
* In the `StringSizeConfig` configuration we use an integer as the flow state for the second
* projection of the dataflow graph. The integer represents an offset that is added to the
* size of the allocation. For example, given:
* ```cpp
* char* p = new char[size + 1];
* size += 1;
* memset(p, 0, size);
* ```
* the initial flow state is `1`. This represents the fact that `size + 1` is a valid bound
* for the size of the allocation pointed to by `p`. After updating the size using `+=`, the
* flow state changes to `0`, which represents the fact that `size + 0` is a valid bound for
* the allocation.
*
* So we need to compute a set of valid integers that represent the offset applied to the
* size. We do this in two steps:
* 1. We first perform the dataflow traversal that the second projection of the product-flow
* library will perform, and visit all the places where the size argument is modified.
* 2. Once that dataflow traversal is done, we accumulate the offsets added at each places
* where the offset is modified (see `validStateImpl`).
*
* Because we want to guarantee that each place where we modify the offset has a `PathNode`
* we "flip" a boolean flow state in each `isAdditionalFlowStep`. This ensures that the node
* has a corresponding `PathNode`.
*/
private module ValidStateConfig implements DataFlow::StateConfigSig {
class FlowState = boolean;
predicate isSource(DataFlow::Node source, FlowState state) {
hasSize(_, source, _) and
state = false
}
predicate isSink(DataFlow::Node sink, FlowState state) {
isSinkPairImpl(_, _, sink, _, _) and
state = [false, true]
}
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
isAdditionalFlowStep2(node1, node2, _) and
state1 = [false, true] and
state2 = state1.booleanNot()
}
predicate includeHiddenNodes() { any() }
}
private import DataFlow::GlobalWithState<ValidStateConfig>
private predicate inLoop(PathNode n) { n.getASuccessor+() = n }
/**
* Holds if `value` is a possible offset for `n`.
*
* To ensure termination, we limit `value` to be in the
* range `[-2, 2]` if the node is part of a loop. Without
* this restriction we wouldn't terminate on an example like:
* ```cpp
* while(unknown()) { size++; }
* ```
*/
private predicate validStateImpl(PathNode n, int value) {
// If the dataflow node depends recursively on itself we restrict the range.
(inLoop(n) implies value = [-2 .. 2]) and
(
// For the dataflow source we have an allocation such as `malloc(size + k)`,
// and the value of the flow-state is then `k`.
hasSize(_, n.getNode(), value)
or
// For a dataflow sink any `value` that is strictly smaller than the delta
// needs to be a valid flow-state. That is, for a snippet like:
// ```
// p = b ? new char[size] : new char[size + 1];
// memset(p, 0, size + 2);
// ```
// the valid flow-states at the `memset` must include the set `{0, 1}` since the
// flow-state at `new char[size]` is `0`, and the flow-state at `new char[size + 1]`
// is `1`.
//
// So we find a valid flow-state at the sink's predecessor, and use the definition
// of our sink predicate to compute the valid flow-states at the sink.
exists(int delta, PathNode n0 |
n0.getASuccessor() = n and
validStateImpl(n0, value) and
isSinkPairImpl(_, _, n.getNode(), delta, _) and
delta > value
)
or
// For a non-source and non-sink node there is two cases to consider.
// 1. A node where we have to update the flow-state, or
// 2. A node that doesn't update the flow-state.
//
// For case 1, we compute the new flow-state by adding the constant operand of the
// `AddInstruction` to the flow-state of any predecessor node.
// For case 2 we simply propagate the valid flow-states from the predecessor node to
// the next one.
exists(PathNode n0, DataFlow::Node node0, DataFlow::Node node, int value0 |
n0.getASuccessor() = n and
validStateImpl(n0, value0) and
node = n.getNode() and
node0 = n0.getNode()
|
exists(int delta |
isAdditionalFlowStep2(node0, node, delta) and
value0 = value + delta
)
or
not isAdditionalFlowStep2(node0, node, _) and
value = value0
)
)
}
predicate validState(DataFlow::Node n, int value) {
validStateImpl(any(PathNode pn | pn.getNode() = n), value)
}
}
import ValidState
/**
* Holds if `node2` is a dataflow node that represents an addition of two operands `op1`
* and `op2` such that:
* 1. `node1` is the dataflow node that represents `op1`, and
* 2. the value of `op2` can be upper bounded by `delta.`
*/
predicate isAdditionalFlowStep2(DataFlow::Node node1, DataFlow::Node node2, int delta) {
exists(AddInstruction add, Operand op |
add.hasOperands(node1.asOperand(), op) and
semBounded(getSemanticExpr(op.getDef()), any(SemZeroBound zero), delta, true, _) and
node2.asInstruction() = add
)
}
module StringSizeConfig implements ProductFlow::StateConfigSig {
class FlowState1 = Unit;
class FlowState2 = int;
predicate isSourcePair(
DataFlow::Node bufSource, FlowState1 state1, DataFlow::Node sizeSource, FlowState2 state2
) {
// In the case of an allocation like
// ```cpp
// malloc(size + 1);
// ```
// we use `state2` to remember that there was an offset (in this case an offset of `1`) added
// to the size of the allocation. This state is then checked in `isSinkPair`.
exists(state1) and
hasSize(bufSource.asConvertedExpr(), sizeSource, state2) and
validState(sizeSource, state2)
}
predicate isSinkPair(
DataFlow::Node bufSink, FlowState1 state1, DataFlow::Node sizeSink, FlowState2 state2
) {
exists(state1) and
validState(sizeSink, state2) and
exists(int delta |
isSinkPairImpl(_, bufSink, sizeSink, delta, _) and
delta > state2
)
}
predicate isBarrierOut2(DataFlow::Node node) {
node = any(DataFlow::SsaPhiNode phi).getAnInput(true)
}
predicate isAdditionalFlowStep2(
DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2
) {
validState(node2, state2) and
exists(int delta |
isAdditionalFlowStep2(node1, node2, delta) and
state1 = state2 + delta
)
}
}
module StringSizeFlow = ProductFlow::GlobalWithState<StringSizeConfig>;
/**
* Gets the maximum number of elements accessed past the buffer `buffer` by the formatting
* function call `c` when an overflow is detected starting at the `(source1, source2)` pair
* and ending at the `(sink1, sink2)` pair.
*
* Implementation note: Since the number of elements accessed past the buffer is computed
* using a `FlowState` on the second component of the `DataFlow::PathNode` pair we project
* the columns down to the underlying `DataFlow::Node` in order to deduplicate the flow
* state.
*/
int getOverflow(
DataFlow::Node source1, DataFlow::Node source2, DataFlow::Node sink1, DataFlow::Node sink2,
CallInstruction c, Expr buffer
) {
result > 0 and
exists(
StringSizeFlow::PathNode1 pathSource1, StringSizeFlow::PathNode2 pathSource2,
StringSizeFlow::PathNode1 pathSink1, StringSizeFlow::PathNode2 pathSink2
|
StringSizeFlow::flowPath(pathSource1, pathSource2, pathSink1, pathSink2) and
source1 = pathSource1.getNode() and
source2 = pathSource2.getNode() and
sink1 = pathSink1.getNode() and
sink2 = pathSink2.getNode() and
isSinkPairImpl(c, sink1, sink2, result + pathSink2.getState(), buffer)
)
}
from
StringSizeFlow::PathNode1 source1, StringSizeFlow::PathNode2 source2,
StringSizeFlow::PathNode1 sink1, StringSizeFlow::PathNode2 sink2, int overflow, CallInstruction c,
Expr buffer, string element
where
StringSizeFlow::flowPath(source1, source2, sink1, sink2) and
overflow =
max(getOverflow(source1.getNode(), source2.getNode(), sink1.getNode(), sink2.getNode(), c,
buffer)
) and
if overflow = 1 then element = " element." else element = " elements."
select c.getUnconvertedResultExpression(), source1, sink1,
"This write may overflow $@ by " + overflow + element, buffer, buffer.toString()

View File

@@ -45,13 +45,20 @@ Element friendlyLoc(Expr e) {
not e instanceof Access and not e instanceof Call and result = e
}
int getComparisonSizeAdjustment(Expr e) {
if e.getType().(IntegralType).isSigned() then result = 1 else result = 0
}
from Loop l, RelationalOperation rel, VariableAccess small, Expr large
where
small = rel.getLesserOperand() and
large = rel.getGreaterOperand() and
rel = l.getCondition().getAChild*() and
forall(Expr conv | conv = large.getConversion*() |
upperBound(conv).log2() > getComparisonSize(small) * 8
// We adjust the comparison size in the case of a signed integer type.
// This is to exclude the sign bit from the comparison that determines if the small type's size is sufficient to hold
// the value of the larger type determined with range analysis.
upperBound(conv).log2() > (getComparisonSize(small) * 8 - getComparisonSizeAdjustment(small))
) and
// Ignore cases where the smaller type is int or larger
// These are still bugs, but you should need a very large string or array to

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