Compare commits

..

7 Commits

Author SHA1 Message Date
Nick Rolfe
73b34729e1 Update ruby/ql/src/queries/security/cwe-116/IncompleteSanitization.ql
Ruby: simplify localFlow call

Co-authored-by: Tom Hvitved <hvitved@github.com>
2022-05-18 09:20:25 +01:00
Tom Hvitved
d5667bd6e6 Ruby: Eliminate bad isLocalSourceNode antijoin
Gets rid of
```
Tuple counts for DataFlowPrivate::Cached::isLocalSourceNode#462ff392#f#antijoin_rhs@dd2f927s:
        20905019     ~3%    {2} r1 = JOIN DataFlowPrivate::Cached::TExprNode#462ff392#ff_1#higher_order_body WITH boundedFastTC(DataFlowPrivate::Cached::localFlowStepTypeTracker#462ff392#ff_10#higher_order_body,DataFlowPrivate::Cached::TExprNode#462ff392#ff_1#higher_order_body) ON FIRST 1 OUTPUT Rhs.1, Lhs.0

        10420128  ~1496%    {1} r2 = JOIN r1 WITH DataFlowPrivate::Cached::TExprNode#462ff392#ff_1#higher_order_body ON FIRST 1 OUTPUT Lhs.1

          480918     ~8%    {1} r3 = JOIN r1 WITH DataFlowPrivate::Cached::entrySsaDefinition#462ff392#f ON FIRST 1 OUTPUT Lhs.1

        10901046  ~1218%    {1} r4 = r2 UNION r3
                            return r4
```
2022-05-18 09:02:39 +02:00
Nick Rolfe
fa0a34975e Ruby: remove some unnecessary uses of PostUpdateNode 2022-05-17 16:30:25 +01:00
Nick Rolfe
e666df77b8 Ruby: address review comment 2022-05-17 12:37:49 +01:00
Nick Rolfe
47bcf69acd Ruby: accept test changes from change to isLocalSource 2022-05-16 13:53:20 +01:00
Nick Rolfe
bffe7f8ce4 Ruby: use localFlow instead of LocalSourceNode::flowsTo 2022-05-16 13:52:43 +01:00
Nick Rolfe
ecbdb661e9 Ruby: stop considering post-update nodes to be local source nodes 2022-05-16 11:55:15 +01:00
3013 changed files with 6085 additions and 252947 deletions

View File

@@ -6,8 +6,6 @@
"*/ql/examples/qlpack.yml",
"*/ql/consistency-queries/qlpack.yml",
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
"go/ql/config/legacy-support/qlpack.yml",
"go/build/codeql-extractor-go/codeql-extractor.yml",
"javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml",
"javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml",
"javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml",
@@ -17,7 +15,6 @@
"misc/legacy-support/*/qlpack.yml",
"misc/suite-helpers/qlpack.yml",
"ruby/extractor-pack/codeql-extractor.yml",
"swift/extractor-pack/codeql-extractor.yml",
"ql/extractor-pack/codeql-extractor.yml"
],
"versionPolicies": {

9
.gitattributes vendored
View File

@@ -39,7 +39,6 @@
*.py text
*.lua text
*.expected text
*.go text
# Explicitly set a bunch of known extensions to binary, because Git < 2.10 will treat
# `* text=auto eol=lf` as `* text eol=lf`
@@ -53,14 +52,6 @@
java/ql/test/stubs/**/*.java linguist-generated=true
java/ql/test/experimental/stubs/**/*.java linguist-generated=true
# Force git not to modify line endings for go or html files under the go/ql directory
go/ql/**/*.go -text
go/ql/**/*.html -text
# Force git not to modify line endings for go dbschemes
go/*.dbscheme -text
# Preserve unusual line ending from codeql-go merge
go/extractor/opencsv/CSVReader.java -text
# For some languages, upgrade script testing references really old dbscheme
# files from legacy upgrades that have CRLF line endings. Since upgrade
# resolution relies on object hashes, we must suppress line ending conversion

View File

@@ -1,14 +0,0 @@
{
"problemMatcher": [
{
"owner": "codeql-query-format",
"pattern": [
{
"regexp": "^((.*) would change by autoformatting\\.)$",
"file": 2,
"message": 1
}
]
}
]
}

View File

@@ -1,17 +0,0 @@
{
"problemMatcher": [
{
"owner": "codeql-syntax-check",
"pattern": [
{
"regexp": "^((ERROR|WARNING): .* \\((.*):(\\d+),(\\d+)-\\d+\\))$",
"message": 1,
"file": 3,
"line": 4,
"col": 5,
"severity": 2
}
]
}
]
}

View File

@@ -1,14 +0,0 @@
{
"problemMatcher": [
{
"owner": "codeql-test-run",
"pattern": [
{
"regexp": "(\\[.*\\] FAILED\\((RESULT|COMPILATION)\\) (.*))$",
"file": 3,
"message": 1
}
]
}
]
}

View File

@@ -1,13 +0,0 @@
{
"problemMatcher": [
{
"owner": "make",
"pattern": [
{
"regexp": "^(make: \\*\\*\\* .*)$",
"message": 1
}
]
}
]
}

View File

@@ -1,161 +0,0 @@
name: "Go: Run Tests"
on:
pull_request:
paths:
- "go/**"
- .github/workflows/go-tests.yml
jobs:
test-linux:
name: Test Linux (Ubuntu)
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.18.1
uses: actions/setup-go@v3
with:
go-version: 1.18.1
id: go
- name: Set up CodeQL CLI
run: |
echo "Removing old CodeQL Directory..."
rm -rf $HOME/codeql
echo "Done"
cd $HOME
echo "Downloading CodeQL CLI..."
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1)
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
echo "Done"
echo "Unpacking CodeQL CLI..."
unzip -q codeql-linux64.zip
rm -f codeql-linux64.zip
echo "Done"
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Check out code
uses: actions/checkout@v2
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
env PATH=$PATH:$HOME/codeql make
- name: Check that all QL and Go code is autoformatted
run: |
cd go
env PATH=$PATH:$HOME/codeql make check-formatting
- name: Compile qhelp files to markdown
run: |
cd go
env PATH=$PATH:$HOME/codeql QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
- name: Upload qhelp markdown
uses: actions/upload-artifact@v2
with:
name: qhelp-markdown
path: go/qhelp-out/**/*.md
- name: Test
run: |
cd go
env PATH=$PATH:$HOME/codeql make test
test-mac:
name: Test MacOS
runs-on: macOS-latest
steps:
- name: Set up Go 1.18.1
uses: actions/setup-go@v3
with:
go-version: 1.18.1
id: go
- name: Set up CodeQL CLI
run: |
echo "Removing old CodeQL Directory..."
rm -rf $HOME/codeql
echo "Done"
cd $HOME
echo "Downloading CodeQL CLI..."
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1)
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-osx64.zip "$LATEST"
echo "Done"
echo "Unpacking CodeQL CLI..."
unzip -q codeql-osx64.zip
rm -f codeql-osx64.zip
echo "Done"
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Check out code
uses: actions/checkout@v2
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
env PATH=$PATH:$HOME/codeql make
- name: Test
run: |
cd go
env PATH=$PATH:$HOME/codeql make test
test-win:
name: Test Windows
runs-on: windows-2019
steps:
- name: Set up Go 1.18.1
uses: actions/setup-go@v3
with:
go-version: 1.18.1
id: go
- name: Set up CodeQL CLI
run: |
echo "Removing old CodeQL Directory..."
rm -rf $HOME/codeql
echo "Done"
cd "$HOME"
echo "Downloading CodeQL CLI..."
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1)
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-win64.zip "$LATEST"
echo "Done"
echo "Unpacking CodeQL CLI..."
unzip -q -o codeql-win64.zip
unzip -q -o codeql-win64.zip codeql/codeql.exe
rm -f codeql-win64.zip
echo "Done"
env:
GITHUB_TOKEN: ${{ github.token }}
shell:
bash
- name: Check out code
uses: actions/checkout@v2
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
$Env:Path += ";$HOME\codeql"
cd go
make
- name: Test
run: |
$Env:Path += ";$HOME\codeql"
cd go
make test

View File

@@ -140,7 +140,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
folder: [cpp, csharp, java, javascript, python, ql, ruby, swift, go]
folder: [cpp, csharp, java, javascript, python, ql, ruby, swift]
needs:
- package

View File

@@ -19,6 +19,7 @@ jobs:
matrix:
repo:
- github/codeql
- github/codeql-go
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -76,7 +77,7 @@ jobs:
path: stats
- run: |
python -m pip install --user lxml
find stats -name 'stats.xml' -print0 | sort -z | xargs -0 python ruby/scripts/merge_stats.py --output ql/ql/src/ql.dbscheme.stats --normalise ql_tokeninfo
find stats -name 'stats.xml' -print0 | sort -z | xargs -0 python ql/scripts/merge_stats.py --output ql/ql/src/ql.dbscheme.stats --normalise ql_tokeninfo
- uses: actions/upload-artifact@v3
with:
name: ql.dbscheme.stats

View File

@@ -20,6 +20,11 @@ jobs:
uses: actions/checkout@v3
with:
path: codeql
- name: Clone github/codeql-go
uses: actions/checkout@v3
with:
repository: 'github/codeql-go'
path: codeql-go
- name: Set up Python 3.8
uses: actions/setup-python@v3
with:

View File

@@ -25,7 +25,8 @@ jobs:
git diff --exit-code --stat HEAD
- name: Generate C++ files
run: |
bazel run //swift/codegen:cppcodegen -- --cpp-output=$PWD/swift-generated-headers
bazel run //swift/codegen:trapgen -- --cpp-output=$PWD/swift-generated-headers
bazel run //swift/codegen:cppgen -- --cpp-output=$PWD/swift-generated-headers
- uses: actions/upload-artifact@v3
with:
name: swift-generated-headers

13
.gitignore vendored
View File

@@ -9,7 +9,6 @@
# qltest projects and artifacts
*/ql/test/**/*.testproj
*/ql/test/**/*.actual
*/ql/test/**/go.sum
# Visual studio temporaries, except a file used by QL4VS
.vs/*
@@ -43,15 +42,3 @@ csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
# CLion project files
/.clwb
# Go build artifacts
go/build/*
# Go binaries
go/tools/bin
go/tools/linux64
go/tools/osx64
go/tools/win64
go/tools/tokenizer.jar
go/main

View File

@@ -6,7 +6,6 @@ path_classifiers:
test:
- csharp/ql/src
- csharp/ql/test
- go/ql/test
- javascript/extractor/parser-tests
- javascript/extractor/tests
- javascript/ql/src
@@ -14,9 +13,6 @@ path_classifiers:
- python/ql/src
- python/ql/test
example:
- go/ql/src
queries:
- include: "*"

View File

@@ -1,6 +1,5 @@
/cpp/ @github/codeql-c-analysis
/csharp/ @github/codeql-csharp
/go/ @github/codeql-go
/java/ @github/codeql-java
/javascript/ @github/codeql-javascript
/python/ @github/codeql-python
@@ -38,7 +37,6 @@
# Workflows
/.github/workflows/ @github/codeql-ci-reviewers
/.github/workflows/go-* @github/codeql-go
/.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
/.github/workflows/ruby-* @github/codeql-ruby

View File

@@ -1,6 +1,6 @@
# CodeQL
This open source repository contains the standard CodeQL libraries and queries that power [GitHub Advanced Security](https://github.com/features/security/code) and the other application security products that [GitHub](https://github.com/features/security/) makes available to its customers worldwide.
This open source repository contains the standard CodeQL libraries and queries that power [GitHub Advanced Security](https://github.com/features/security/code) and the other application security products that [GitHub](https://github.com/features/security/) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
## How do I learn CodeQL and run queries?

View File

@@ -22,15 +22,13 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll"
],
"DataFlow Java/C++/C#/Python Common": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
@@ -38,8 +36,7 @@
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll"
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll"
],
"TaintTracking::Configuration Java/C++/C#/Python": [
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
@@ -60,8 +57,7 @@
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttrackingforlibraries/TaintTrackingImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttrackingforlibraries/TaintTrackingImpl.qll"
],
"DataFlow Java/C++/C#/Python Consistency checks": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
@@ -69,8 +65,7 @@
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll"
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll"
],
"DataFlow Java/C# Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
@@ -463,8 +458,7 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
"csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/SsaImplCommon.qll"
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll"
],
"CryptoAlgorithms Python/JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
@@ -506,8 +500,7 @@
],
"CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll",
"swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll"
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll"
],
"TypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
@@ -560,9 +553,5 @@
"HttpToFileAccessCustomizations JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll",
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll"
],
"Typo database": [
"javascript/ql/src/Expressions/TypoDatabase.qll",
"ql/ql/src/codeql_ql/style/TypoDatabase.qll"
]
}
}

View File

@@ -1,3 +0,0 @@
description: Add relation for tracking C++ braced initializers
compatibility: full
braced_initialisers.rel: delete

View File

@@ -1,21 +1,3 @@
## 0.2.3
### New Features
* An `isBraced` predicate was added to the `Initializer` class which holds when a C++ braced initializer was used in the initialization.
## 0.2.2
### Deprecated APIs
* The `AnalysedString` class in the `StringAnalysis` module has been replaced with `AnalyzedString`, to follow our style guide. The old name still exists as a deprecated alias.
### New Features
* A `getInitialization` predicate was added to the `ConstexprIfStmt`, `IfStmt`, and `SwitchStmt` classes that yields the C++17-style initializer of the `if` or `switch` statement when it exists.
## 0.2.1
## 0.2.0
### Breaking Changes

View File

@@ -0,0 +1,4 @@
---
category: feature
---
* A `getInitialization` predicate was added to the `ConstexprIfStmt`, `IfStmt`, and `SwitchStmt` classes that yields the C++17-style initializer of the `if` or `switch` statement when it exists.

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* The `AnalysedString` class in the `StringAnalysis` module has been replaced with `AnalyzedString`, to follow our style guide. The old name still exists as a deprecated alias.

View File

@@ -1 +0,0 @@
## 0.2.1

View File

@@ -1,9 +0,0 @@
## 0.2.2
### Deprecated APIs
* The `AnalysedString` class in the `StringAnalysis` module has been replaced with `AnalyzedString`, to follow our style guide. The old name still exists as a deprecated alias.
### New Features
* A `getInitialization` predicate was added to the `ConstexprIfStmt`, `IfStmt`, and `SwitchStmt` classes that yields the C++17-style initializer of the `if` or `switch` statement when it exists.

View File

@@ -1,5 +0,0 @@
## 0.2.3
### New Features
* An `isBraced` predicate was added to the `Initializer` class which holds when a C++ braced initializer was used in the initialization.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.3
lastReleaseVersion: 0.2.0

View File

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

View File

@@ -31,7 +31,7 @@ class Field extends MemberVariable {
int getByteOffset() { fieldoffsets(underlyingElement(this), result, _) }
/**
* Gets the byte offset within `mostDerivedClass` of each occurrence of this
* Gets the byte offset within `mostDerivedClass` of each occurence of this
* field within `mostDerivedClass` itself or a base class subobject of
* `mostDerivedClass`.
* Note that for fields of virtual base classes, and non-virtual base classes

View File

@@ -51,7 +51,4 @@ class Initializer extends ControlFlowNode, @initialiser {
override Function getControlFlowScope() { result = this.getExpr().getEnclosingFunction() }
override Stmt getEnclosingStmt() { result = this.getExpr().getEnclosingStmt() }
/** Holds if the initializer used the C++ braced initializer notation. */
predicate isBraced() { braced_initialisers(underlyingElement(this)) }
}

View File

@@ -872,7 +872,7 @@ class FormatLiteral extends Literal {
private Type getConversionType1(int n) {
exists(string cnv | cnv = this.getConversionChar(n) |
cnv = ["d", "i"] and
cnv.regexpMatch("d|i") and
result = this.getIntegralConversion(n) and
not result.getUnderlyingType().(IntegralType).isExplicitlySigned() and
not result.getUnderlyingType().(IntegralType).isExplicitlyUnsigned()
@@ -912,7 +912,7 @@ class FormatLiteral extends Literal {
private Type getConversionType2(int n) {
exists(string cnv | cnv = this.getConversionChar(n) |
cnv = ["o", "u", "x", "X"] and
cnv.regexpMatch("o|u|x|X") and
result = this.getIntegralConversion(n) and
result.getUnderlyingType().(IntegralType).isUnsigned()
)
@@ -920,7 +920,7 @@ class FormatLiteral extends Literal {
private Type getConversionType3(int n) {
exists(string cnv | cnv = this.getConversionChar(n) |
cnv = ["a", "A", "e", "E", "f", "F", "g", "G"] and result = this.getFloatingPointConversion(n)
cnv.regexpMatch("a|A|e|E|f|F|g|G") and result = this.getFloatingPointConversion(n)
)
}

View File

@@ -970,7 +970,7 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2) {
subEdge(p1, n1, n2, p2)
or
// If `n1` has sub-nodes to accommodate destructors, but there are none to be
// If `n1` has sub-nodes to accomodate destructors, but there are none to be
// called, connect the "before destructors" node directly to the "after
// destructors" node. For performance, only do this when the nodes exist.
exists(Pos afterDtors | afterDtors.isAfterDestructors() | subEdge(afterDtors, n1, _, _)) and

View File

@@ -216,9 +216,10 @@ private module LambdaFlow {
or
// jump step
exists(Node mid, DataFlowType t0 |
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
toReturn = false and
toJump = true
toJump = true and
lastCall = TDataFlowCallNone()
|
jumpStepCached(node, mid) and
t = t0
@@ -304,7 +305,7 @@ cached
private module Cached {
/**
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
* collapsing the two stages.
*/
cached
@@ -788,31 +789,24 @@ private module Cached {
cached
predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) }
cached
predicate storeSet(
Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
storeStep(node1, c, node2) and
contentType = getNodeDataFlowType(node1) and
containerType = getNodeDataFlowType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
or
readSet(n2, c, n1) and
contentType = getNodeDataFlowType(n1) and
containerType = getNodeDataFlowType(n2)
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
exists(ContentSet cs | c = cs.getAStoreContent() |
storeStep(node1, cs, node2) and
contentType = getNodeDataFlowType(node1) and
containerType = getNodeDataFlowType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, cs, contentType), n1)
or
readSet(n2, cs, n1) and
contentType = getNodeDataFlowType(n1) and
containerType = getNodeDataFlowType(n2)
)
)
}

View File

@@ -549,7 +549,7 @@ module FlowVar_internal {
bb = this.(Loop).getStmt() and
v = this.getARelevantVariable()
or
this.reachesWithoutAssignment(pragma[only_bind_out](bb.getAPredecessor()), v) and
this.reachesWithoutAssignment(bb.getAPredecessor(), v) and
this.bbInLoop(bb)
) and
not assignsToVar(bb, v)

View File

@@ -216,9 +216,10 @@ private module LambdaFlow {
or
// jump step
exists(Node mid, DataFlowType t0 |
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
toReturn = false and
toJump = true
toJump = true and
lastCall = TDataFlowCallNone()
|
jumpStepCached(node, mid) and
t = t0
@@ -304,7 +305,7 @@ cached
private module Cached {
/**
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
* collapsing the two stages.
*/
cached
@@ -788,31 +789,24 @@ private module Cached {
cached
predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) }
cached
predicate storeSet(
Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
storeStep(node1, c, node2) and
contentType = getNodeDataFlowType(node1) and
containerType = getNodeDataFlowType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
or
readSet(n2, c, n1) and
contentType = getNodeDataFlowType(n1) and
containerType = getNodeDataFlowType(n2)
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
exists(ContentSet cs | c = cs.getAStoreContent() |
storeStep(node1, cs, node2) and
contentType = getNodeDataFlowType(node1) and
containerType = getNodeDataFlowType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, cs, contentType), n1)
or
readSet(n2, cs, n1) and
contentType = getNodeDataFlowType(n1) and
containerType = getNodeDataFlowType(n2)
)
)
}

View File

@@ -37,7 +37,7 @@ private module Cached {
* along the chain of addresses computed by `StoreNodeInstr.getInner` to identify field writes
* and call `storeStep` accordingly (i.e., for an expression like `a.b.c = x`, we visit `c`, then
* `b`, then `a`).
* 2. Flow is transferred from a `WriteSideEffectInstruction` to a `StoreNodeOperand` after flow
* 2. Flow is transfered from a `WriteSideEffectInstruction` to a `StoreNodeOperand` after flow
* returns to a caller. Flow will then proceed to the defining instruction of the operand (because
* the `StoreNodeInstr` computed by `StoreNodeOperand.getInner()` is the `StoreNode` containing
* the defining instruction), and then along the chain computed by `StoreNodeInstr.getInner` like

View File

@@ -67,7 +67,7 @@ class DefaultEdge extends EdgeKind, TDefaultEdge {
/**
* A "case" edge, representing the successor of a `Switch` instruction when the
* the condition value matches a corresponding `case` label.
* the condition value matches a correponding `case` label.
*/
class CaseEdge extends EdgeKind, TCaseEdge {
string minValue;

View File

@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
final Instruction getAnInstruction() { this = valueNumber(result) }
/**
* Gets one of the instructions that was assigned this value number. The chosen instruction is
* Gets one of the instructions that was assigned this value number. The chosen instuction is
* deterministic but arbitrary. Intended for use only in debugging.
*/
final Instruction getExampleInstruction() {

View File

@@ -1005,7 +1005,7 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
/**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
* `DebugSSA` module, which is then imported by PrintSSA.
*/
module DebugSsa {

View File

@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
final Instruction getAnInstruction() { this = valueNumber(result) }
/**
* Gets one of the instructions that was assigned this value number. The chosen instruction is
* Gets one of the instructions that was assigned this value number. The chosen instuction is
* deterministic but arbitrary. Intended for use only in debugging.
*/
final Instruction getExampleInstruction() {

View File

@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
final Instruction getAnInstruction() { this = valueNumber(result) }
/**
* Gets one of the instructions that was assigned this value number. The chosen instruction is
* Gets one of the instructions that was assigned this value number. The chosen instuction is
* deterministic but arbitrary. Intended for use only in debugging.
*/
final Instruction getExampleInstruction() {

View File

@@ -1005,7 +1005,7 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
/**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
* `DebugSSA` module, which is then imported by PrintSSA.
*/
module DebugSsa {

View File

@@ -59,7 +59,7 @@ predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgori
/**
* Holds if there is additional evidence that `name` looks like it might be
* related to operations with an encyption algorithm, besides the name of a
* specific algorithm. This can be used in conjunction with
* specific algorithm. This can be used in conjuction with
* `isInsecureEncryption` to produce a stronger heuristic.
*/
bindingset[name]

View File

@@ -1436,10 +1436,6 @@ initialisers(
int location: @location_expr ref
);
braced_initialisers(
int init: @initialiser ref
);
/**
* An ancestor for the expression, for cases in which we cannot
* otherwise find the expression's parent.

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
description: Add relation for tracking C++ braced initializers
compatibility: backwards

View File

@@ -1,18 +1,3 @@
## 0.1.4
## 0.1.3
### Minor Analysis Improvements
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query precision has been increased to `high`.
* The `cpp/unused-local-variable` no longer ignores functions that include `if` and `switch` statements with C++17-style initializers.
## 0.1.2
### Minor Analysis Improvements
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query has been extended to support a broader selection of XML libraries and interfaces.
## 0.1.1
### New Queries

View File

@@ -57,10 +57,10 @@ class ExtractionProblem extends TExtractionProblem {
/** Gets the problem message for this problem. */
string getProblemMessage() { none() }
/** Gets the file this problem occurred in. */
/** Gets the file this problem occured in. */
File getFile() { none() }
/** Gets the location this problem occurred in. */
/** Gets the location this problem occured in. */
Location getLocation() { none() }
/** Gets the SARIF severity of this problem. */

View File

@@ -57,10 +57,10 @@ class ExtractionError extends TExtractionError {
/** Gets the error message for this error. */
string getErrorMessage() { none() }
/** Gets the file this error occurred in. */
/** Gets the file this error occured in. */
File getFile() { none() }
/** Gets the location this error occurred in. */
/** Gets the location this error occured in. */
Location getLocation() { none() }
/** Gets the SARIF severity of this error. */

View File

@@ -19,7 +19,7 @@ predicate whitelist(Function f) {
"nearbyintl", "rint", "rintf", "rintl", "round", "roundf", "roundl", "trunc", "truncf",
"truncl"
] or
f.getName().matches("\\_\\_builtin\\_%")
f.getName().matches("__builtin_%")
}
predicate whitelistPow(FunctionCall fc) {

View File

@@ -13,7 +13,7 @@
* @deprecated This query is deprecated, use
* Potentially overrunning write (`cpp/overrunning-write`) and
* Potentially overrunning write with float to string conversion
* (`cpp/overrunning-write-with-float`) instead.
* (`cpp/overrunning-write-with-float) instead.
*/
import cpp

View File

@@ -1,78 +0,0 @@
/**
* Models the libxml2 XML library.
*/
import cpp
import XML
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
/**
* A call to a `libxml2` function that parses XML.
*/
class Libxml2ParseCall extends FunctionCall {
int optionsArg;
Libxml2ParseCall() {
exists(string fname | this.getTarget().getName() = fname |
fname = "xmlCtxtUseOptions" and optionsArg = 1
or
fname = "xmlReadFile" and optionsArg = 2
or
fname = ["xmlCtxtReadFile", "xmlParseInNodeContext", "xmlReadDoc", "xmlReadFd"] and
optionsArg = 3
or
fname = ["xmlCtxtReadDoc", "xmlCtxtReadFd", "xmlReadMemory"] and optionsArg = 4
or
fname = ["xmlCtxtReadMemory", "xmlReadIO"] and optionsArg = 5
or
fname = "xmlCtxtReadIO" and optionsArg = 6
)
}
/**
* Gets the argument that specifies `xmlParserOption`s.
*/
Expr getOptions() { result = this.getArgument(optionsArg) }
}
/**
* An `xmlParserOption` for `libxml2` that is considered unsafe.
*/
class Libxml2BadOption extends EnumConstant {
Libxml2BadOption() { this.getName() = ["XML_PARSE_NOENT", "XML_PARSE_DTDLOAD"] }
}
/**
* The libxml2 XML library.
*/
class LibXml2Library extends XmlLibrary {
LibXml2Library() { this = "LibXml2Library" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is an `options` argument on a libxml2 parse call that specifies
// at least one unsafe option.
//
// note: we don't need to track an XML object for libxml2, so we don't
// really need data flow. Nevertheless we jam it into this configuration,
// with matching sources and sinks. This allows results to be presented by
// the same query, in a consistent way as other results with flow paths.
exists(Libxml2ParseCall call, Expr options |
options = call.getOptions() and
node.asExpr() = options and
flowstate = "libxml2" and
exists(Libxml2BadOption opt |
globalValueNumber(options).getAnExpr().getValue().toInt().bitAnd(opt.getValue().toInt()) !=
0
)
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the `options` argument on a `libxml2` parse call.
exists(Libxml2ParseCall call, Expr options |
options = call.getOptions() and
node.asExpr() = options and
flowstate = "libxml2"
)
}
}

View File

@@ -1,55 +0,0 @@
/**
* Provides a abstract classes for modeling XML libraries. This design is
* currently specialized for the purposes of the XXE query.
*/
import cpp
import semmle.code.cpp.ir.dataflow.DataFlow
import Xerces
import Libxml2
/**
* A flow state representing a possible configuration of an XML object.
*/
abstract class XxeFlowState extends DataFlow::FlowState {
bindingset[this]
XxeFlowState() { any() } // required characteristic predicate
}
/**
* An XML library or interface.
*/
abstract class XmlLibrary extends string {
bindingset[this]
XmlLibrary() { any() } // required characteristic predicate
/**
* Holds if `node` is the source node for a potentially unsafe configuration
* object for this XML library, along with `flowstate` representing its
* initial state.
*/
abstract predicate configurationSource(DataFlow::Node node, string flowstate);
/**
* Holds if `node` is the sink node where an unsafe configuration object is
* used to interpret XML.
*/
abstract predicate configurationSink(DataFlow::Node node, string flowstate);
}
/**
* An `Expr` that changes the configuration of an XML object, transforming the
* `XxeFlowState` that flows through it.
*/
abstract class XxeFlowStateTransformer extends Expr {
/**
* Gets the flow state that `flowstate` is transformed into.
*
* Due to limitations of the implementation the transformation defined by this
* predicate must be idempotent, that is, for any input `x` it must be that:
* ```
* transform(transform(x)) = transform(x)
* ```
*/
abstract XxeFlowState transform(XxeFlowState flowstate);
}

View File

@@ -13,8 +13,344 @@
*/
import cpp
import XML
import semmle.code.cpp.ir.dataflow.DataFlow
import DataFlow::PathGraph
import semmle.code.cpp.ir.IR
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
/**
* A flow state representing a possible configuration of an XML object.
*/
abstract class XXEFlowState extends DataFlow::FlowState {
bindingset[this]
XXEFlowState() { any() } // required characteristic predicate
}
/**
* An `Expr` that changes the configuration of an XML object, transforming the
* `XXEFlowState` that flows through it.
*/
abstract class XXEFlowStateTransformer extends Expr {
/**
* Gets the flow state that `flowstate` is transformed into.
*
* Due to limitations of the implementation the transformation defined by this
* predicate must be idempotent, that is, for any input `x` it must be that:
* ```
* transform(tranform(x)) = tranform(x)
* ```
*/
abstract XXEFlowState transform(XXEFlowState flowstate);
}
/**
* The `AbstractDOMParser` class.
*/
class AbstractDOMParserClass extends Class {
AbstractDOMParserClass() { this.hasName("AbstractDOMParser") }
}
/**
* The `XercesDOMParser` class.
*/
class XercesDOMParserClass extends Class {
XercesDOMParserClass() { this.hasName("XercesDOMParser") }
}
/**
* The `DOMLSParser` class.
*/
class DomLSParserClass extends Class {
DomLSParserClass() { this.hasName("DOMLSParser") }
}
/**
* The `SAXParser` class.
*/
class SaxParserClass extends Class {
SaxParserClass() { this.hasName("SAXParser") }
}
/**
* The `SAX2XMLReader` class.
*/
class Sax2XmlReader extends Class {
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
}
/**
* Gets a valid flow state for `AbstractDOMParser` or `SAXParser` flow.
*
* These flow states take the form `Xerces-A-B`, where:
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
*/
predicate encodeXercesFlowState(
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
) {
flowstate = "Xerces-0-0" and
disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 0
or
flowstate = "Xerces-0-1" and
disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 1
or
flowstate = "Xerces-1-0" and
disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 0
or
flowstate = "Xerces-1-1" and
disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 1
}
/**
* A flow state representing the configuration of an `AbstractDOMParser` or
* `SAXParser` object.
*/
class XercesFlowState extends XXEFlowState {
XercesFlowState() { encodeXercesFlowState(this, _, _) }
}
/**
* A flow state transformer for a call to
* `AbstractDOMParser.setDisableDefaultEntityResolution` or
* `SAXParser.setDisableDefaultEntityResolution`. Transforms the flow
* state through the qualifier according to the setting in the parameter.
*/
class DisableDefaultEntityResolutionTransformer extends XXEFlowStateTransformer {
Expr newValue;
DisableDefaultEntityResolutionTransformer() {
exists(Call call, Function f |
call.getTarget() = f and
(
f.getDeclaringType() instanceof AbstractDOMParserClass or
f.getDeclaringType() instanceof SaxParserClass
) and
f.hasName("setDisableDefaultEntityResolution") and
this = call.getQualifier() and
newValue = call.getArgument(0)
)
}
final override XXEFlowState transform(XXEFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}
/**
* A flow state transformer for a call to
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
* state through the qualifier according to the setting in the parameter.
*/
class CreateEntityReferenceNodesTransformer extends XXEFlowStateTransformer {
Expr newValue;
CreateEntityReferenceNodesTransformer() {
exists(Call call, Function f |
call.getTarget() = f and
f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDOMParserClass and
this = call.getQualifier() and
newValue = call.getArgument(0)
)
}
final override XXEFlowState transform(XXEFlowState flowstate) {
exists(int disabledDefaultEntityResolution |
encodeXercesFlowState(flowstate, disabledDefaultEntityResolution, _) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, disabledDefaultEntityResolution, 1)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, disabledDefaultEntityResolution, 0)
)
)
}
}
/**
* The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
*/
class FeatureDisableDefaultEntityResolution extends Variable {
FeatureDisableDefaultEntityResolution() {
this.getName() = "fgXercesDisableDefaultEntityResolution" and
this.getDeclaringType().getName() = "XMLUni"
}
}
/**
* A flow state transformer for a call to `SAX2XMLReader.setFeature`
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
* Transforms the flow state through the qualifier according to this setting.
*/
class SetFeatureTransformer extends XXEFlowStateTransformer {
Expr newValue;
SetFeatureTransformer() {
exists(Call call, Function f |
call.getTarget() = f and
f.getClassAndName("setFeature") instanceof Sax2XmlReader and
this = call.getQualifier() and
globalValueNumber(call.getArgument(0)).getAnExpr().(VariableAccess).getTarget() instanceof
FeatureDisableDefaultEntityResolution and
newValue = call.getArgument(1)
)
}
final override XXEFlowState transform(XXEFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}
/**
* The `DOMLSParser.getDomConfig` function.
*/
class GetDomConfig extends Function {
GetDomConfig() { this.getClassAndName("getDomConfig") instanceof DomLSParserClass }
}
/**
* The `DOMConfiguration.setParameter` function.
*/
class DomConfigurationSetParameter extends Function {
DomConfigurationSetParameter() {
this.getClassAndName("setParameter").getName() = "DOMConfiguration"
}
}
/**
* A flow state transformer for a call to `DOMConfiguration.setParameter`
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
* This is a slightly more complex transformer because the qualifier is a
* `DOMConfiguration` pointer returned by `DOMLSParser.getDomConfig` - and it
* is *that* qualifier we want to transform the flow state of.
*/
class DomConfigurationSetParameterTransformer extends XXEFlowStateTransformer {
Expr newValue;
DomConfigurationSetParameterTransformer() {
exists(FunctionCall getDomConfigCall, FunctionCall setParameterCall |
// this is the qualifier of a call to `DOMLSParser.getDomConfig`.
getDomConfigCall.getTarget() instanceof GetDomConfig and
this = getDomConfigCall.getQualifier() and
// `setParameterCall` is a call to `setParameter` on the return value of
// the same call to `DOMLSParser.getDomConfig`.
setParameterCall.getTarget() instanceof DomConfigurationSetParameter and
globalValueNumber(setParameterCall.getQualifier()).getAnExpr() = getDomConfigCall and
// the parameter being set is
// `XMLUni::fgXercesDisableDefaultEntityResolution`.
globalValueNumber(setParameterCall.getArgument(0)).getAnExpr().(VariableAccess).getTarget()
instanceof FeatureDisableDefaultEntityResolution and
// the value being set is `newValue`.
newValue = setParameterCall.getArgument(1)
)
}
final override XXEFlowState transform(XXEFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}
/**
* The `AbstractDOMParser.parse`, `DOMLSParserClass.parse`, `SAXParser.parse`
* or `SAX2XMLReader.parse` method.
*/
class ParseFunction extends Function {
ParseFunction() {
this.getClassAndName("parse") instanceof AbstractDOMParserClass or
this.getClassAndName("parse") instanceof DomLSParserClass or
this.getClassAndName("parse") instanceof SaxParserClass or
this.getClassAndName("parse") instanceof Sax2XmlReader
}
}
/**
* The `createLSParser` function that returns a newly created `DOMLSParser`
* object.
*/
class CreateLSParser extends Function {
CreateLSParser() {
this.hasName("createLSParser") and
this.getUnspecifiedType().(PointerType).getBaseType() instanceof DomLSParserClass // returns a `DOMLSParser *`.
}
}
/**
* The `createXMLReader` function that returns a newly created `SAX2XMLReader`
* object.
*/
class CreateXmlReader extends Function {
CreateXmlReader() {
this.hasName("createXMLReader") and
this.getUnspecifiedType().(PointerType).getBaseType() instanceof Sax2XmlReader // returns a `SAX2XMLReader *`.
}
}
/**
* A call to a `libxml2` function that parses XML.
*/
class Libxml2ParseCall extends FunctionCall {
int optionsArg;
Libxml2ParseCall() {
exists(string fname | this.getTarget().getName() = fname |
fname = "xmlCtxtUseOptions" and optionsArg = 1
or
fname = "xmlReadFile" and optionsArg = 2
or
fname = ["xmlCtxtReadFile", "xmlParseInNodeContext", "xmlReadDoc", "xmlReadFd"] and
optionsArg = 3
or
fname = ["xmlCtxtReadDoc", "xmlCtxtReadFd", "xmlReadMemory"] and optionsArg = 4
or
fname = ["xmlCtxtReadMemory", "xmlReadIO"] and optionsArg = 5
or
fname = "xmlCtxtReadIO" and optionsArg = 6
)
}
/**
* Gets the argument that specifies `xmlParserOption`s.
*/
Expr getOptions() { result = this.getArgument(optionsArg) }
}
/**
* An `xmlParserOption` for `libxml2` that is considered unsafe.
*/
class Libxml2BadOption extends EnumConstant {
Libxml2BadOption() { this.getName() = ["XML_PARSE_NOENT", "XML_PARSE_DTDLOAD"] }
}
/**
* A configuration for tracking XML objects and their states.
@@ -23,25 +359,85 @@ class XXEConfiguration extends DataFlow::Configuration {
XXEConfiguration() { this = "XXEConfiguration" }
override predicate isSource(DataFlow::Node node, string flowstate) {
any(XmlLibrary l).configurationSource(node, flowstate)
// source is the write on `this` of a call to the `XercesDOMParser`
// constructor.
exists(CallInstruction call |
call.getStaticCallTarget() = any(XercesDOMParserClass c).getAConstructor() and
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
call.getThisArgument() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
or
// source is the result of a call to `createLSParser`.
exists(Call call |
call.getTarget() instanceof CreateLSParser and
call = node.asExpr() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
or
// source is the write on `this` of a call to the `SAXParser`
// constructor.
exists(CallInstruction call |
call.getStaticCallTarget() = any(SaxParserClass c).getAConstructor() and
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
call.getThisArgument() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
or
// source is the result of a call to `createXMLReader`.
exists(Call call |
call.getTarget() instanceof CreateXmlReader and
call = node.asExpr() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
or
// source is an `options` argument on a `libxml2` parse call that specifies
// at least one unsafe option.
//
// note: we don't need to track an XML object for `libxml2`, so we don't
// really need data flow. Nevertheless we jam it into this configuration,
// with matching sources and sinks. This allows results to be presented by
// the same query, in a consistent way as other results with flow paths.
exists(Libxml2ParseCall call, Expr options |
options = call.getOptions() and
node.asExpr() = options and
flowstate = "libxml2" and
exists(Libxml2BadOption opt |
globalValueNumber(options).getAnExpr().getValue().toInt().bitAnd(opt.getValue().toInt()) !=
0
)
)
}
override predicate isSink(DataFlow::Node node, string flowstate) {
any(XmlLibrary l).configurationSink(node, flowstate)
// sink is the read of the qualifier of a call to `parse`.
exists(Call call |
call.getTarget() instanceof ParseFunction and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesFlowState and
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
or
// sink is the `options` argument on a `libxml2` parse call.
exists(Libxml2ParseCall call, Expr options |
options = call.getOptions() and
node.asExpr() = options and
flowstate = "libxml2"
)
}
override predicate isAdditionalFlowStep(
DataFlow::Node node1, string state1, DataFlow::Node node2, string state2
) {
// create additional flow steps for `XxeFlowStateTransformer`s
state2 = node2.asConvertedExpr().(XxeFlowStateTransformer).transform(state1) and
// create additional flow steps for `XXEFlowStateTransformer`s
state2 = node2.asConvertedExpr().(XXEFlowStateTransformer).transform(state1) and
DataFlow::simpleLocalFlowStep(node1, node2)
}
override predicate isBarrier(DataFlow::Node node, string flowstate) {
// when the flowstate is transformed at a call node, block the original
// flowstate value.
node.asConvertedExpr().(XxeFlowStateTransformer).transform(flowstate) != flowstate
node.asConvertedExpr().(XXEFlowStateTransformer).transform(flowstate) != flowstate
}
}

View File

@@ -1,376 +0,0 @@
/**
* Models the Xerces XML library.
*/
import cpp
import XML
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.ir.IR
/**
* Gets a valid flow state for `AbstractDOMParser` or `SAXParser` flow.
*
* These flow states take the form `Xerces-A-B`, where:
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
*/
predicate encodeXercesFlowState(
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
) {
flowstate = "Xerces-0-0" and
disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 0
or
flowstate = "Xerces-0-1" and
disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 1
or
flowstate = "Xerces-1-0" and
disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 0
or
flowstate = "Xerces-1-1" and
disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 1
}
/**
* A flow state representing the configuration of an `AbstractDOMParser` or
* `SAXParser` object.
*/
class XercesFlowState extends XxeFlowState {
XercesFlowState() { encodeXercesFlowState(this, _, _) }
}
/**
* The `AbstractDOMParser` class.
*/
class AbstractDomParserClass extends Class {
AbstractDomParserClass() { this.hasName("AbstractDOMParser") }
}
/**
* The `XercesDOMParser` class.
*/
class XercesDomParserClass extends Class {
XercesDomParserClass() { this.hasName("XercesDOMParser") }
}
/**
* The `XercesDOMParser` interface for the Xerces XML library.
*/
class XercesDomParserLibrary extends XmlLibrary {
XercesDomParserLibrary() { this = "XercesDomParserLibrary" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is the write on `this` of a call to the `XercesDOMParser`
// constructor.
exists(CallInstruction call |
call.getStaticCallTarget() = any(XercesDomParserClass c).getAConstructor() and
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
call.getThisArgument() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the read of the qualifier of a call to `AbstractDOMParser.parse`.
exists(Call call |
call.getTarget().getClassAndName("parse") instanceof AbstractDomParserClass and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesFlowState and
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
}
}
/**
* The `DOMLSParser` class.
*/
class DomLSParserClass extends Class {
DomLSParserClass() { this.hasName("DOMLSParser") }
}
/**
* The `createLSParser` function that returns a newly created `DOMLSParser`
* object.
*/
class CreateLSParser extends Function {
CreateLSParser() {
this.hasName("createLSParser") and
this.getUnspecifiedType().(PointerType).getBaseType() instanceof DomLSParserClass // returns a `DOMLSParser *`.
}
}
/**
* The createLSParser interface for the Xerces XML library.
*/
class CreateLSParserLibrary extends XmlLibrary {
CreateLSParserLibrary() { this = "CreateLSParserLibrary" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is the result of a call to `createLSParser`.
exists(Call call |
call.getTarget() instanceof CreateLSParser and
call = node.asExpr() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the read of the qualifier of a call to `DOMLSParserClass.parse`.
exists(Call call |
call.getTarget().getClassAndName("parse") instanceof DomLSParserClass and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesFlowState and
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
}
}
/**
* The `SAXParser` class.
*/
class SaxParserClass extends Class {
SaxParserClass() { this.hasName("SAXParser") }
}
/**
* The `SAX2XMLReader` class.
*/
class Sax2XmlReader extends Class {
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
}
/**
* The SAXParser interface for the Xerces XML library.
*/
class SaxParserLibrary extends XmlLibrary {
SaxParserLibrary() { this = "SaxParserLibrary" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is the write on `this` of a call to the `SAXParser`
// constructor.
exists(CallInstruction call |
call.getStaticCallTarget() = any(SaxParserClass c).getAConstructor() and
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
call.getThisArgument() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the read of the qualifier of a call to `SAXParser.parse`.
exists(Call call |
call.getTarget().getClassAndName("parse") instanceof SaxParserClass and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesFlowState and
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
}
}
/**
* The `createXMLReader` function that returns a newly created `SAX2XMLReader`
* object.
*/
class CreateXmlReader extends Function {
CreateXmlReader() {
this.hasName("createXMLReader") and
this.getUnspecifiedType().(PointerType).getBaseType() instanceof Sax2XmlReader // returns a `SAX2XMLReader *`.
}
}
/**
* The SAX2XMLReader interface for the Xerces XML library.
*/
class Sax2XmlReaderLibrary extends XmlLibrary {
Sax2XmlReaderLibrary() { this = "Sax2XmlReaderLibrary" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is the result of a call to `createXMLReader`.
exists(Call call |
call.getTarget() instanceof CreateXmlReader and
call = node.asExpr() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the read of the qualifier of a call to `SAX2XMLReader.parse`.
exists(Call call |
call.getTarget().getClassAndName("parse") instanceof Sax2XmlReader and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesFlowState and
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
}
}
/**
* A flow state transformer for a call to
* `AbstractDOMParser.setDisableDefaultEntityResolution` or
* `SAXParser.setDisableDefaultEntityResolution`. Transforms the flow
* state through the qualifier according to the setting in the parameter.
*/
class DisableDefaultEntityResolutionTransformer extends XxeFlowStateTransformer {
Expr newValue;
DisableDefaultEntityResolutionTransformer() {
exists(Call call, Function f |
call.getTarget() = f and
(
f.getDeclaringType() instanceof AbstractDomParserClass or
f.getDeclaringType() instanceof SaxParserClass
) and
f.hasName("setDisableDefaultEntityResolution") and
this = call.getQualifier() and
newValue = call.getArgument(0)
)
}
final override XxeFlowState transform(XxeFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}
/**
* A flow state transformer for a call to
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
* state through the qualifier according to the setting in the parameter.
*/
class CreateEntityReferenceNodesTransformer extends XxeFlowStateTransformer {
Expr newValue;
CreateEntityReferenceNodesTransformer() {
exists(Call call, Function f |
call.getTarget() = f and
f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDomParserClass and
this = call.getQualifier() and
newValue = call.getArgument(0)
)
}
final override XxeFlowState transform(XxeFlowState flowstate) {
exists(int disabledDefaultEntityResolution |
encodeXercesFlowState(flowstate, disabledDefaultEntityResolution, _) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, disabledDefaultEntityResolution, 1)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, disabledDefaultEntityResolution, 0)
)
)
}
}
/**
* The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
*/
class FeatureDisableDefaultEntityResolution extends Variable {
FeatureDisableDefaultEntityResolution() {
this.getName() = "fgXercesDisableDefaultEntityResolution" and
this.getDeclaringType().getName() = "XMLUni"
}
}
/**
* A flow state transformer for a call to `SAX2XMLReader.setFeature`
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
* Transforms the flow state through the qualifier according to this setting.
*/
class SetFeatureTransformer extends XxeFlowStateTransformer {
Expr newValue;
SetFeatureTransformer() {
exists(Call call, Function f |
call.getTarget() = f and
f.getClassAndName("setFeature") instanceof Sax2XmlReader and
this = call.getQualifier() and
globalValueNumber(call.getArgument(0)).getAnExpr().(VariableAccess).getTarget() instanceof
FeatureDisableDefaultEntityResolution and
newValue = call.getArgument(1)
)
}
final override XxeFlowState transform(XxeFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}
/**
* The `DOMLSParser.getDomConfig` function.
*/
class GetDomConfig extends Function {
GetDomConfig() { this.getClassAndName("getDomConfig") instanceof DomLSParserClass }
}
/**
* The `DOMConfiguration.setParameter` function.
*/
class DomConfigurationSetParameter extends Function {
DomConfigurationSetParameter() {
this.getClassAndName("setParameter").getName() = "DOMConfiguration"
}
}
/**
* A flow state transformer for a call to `DOMConfiguration.setParameter`
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
* This is a slightly more complex transformer because the qualifier is a
* `DOMConfiguration` pointer returned by `DOMLSParser.getDomConfig` - and it
* is *that* qualifier we want to transform the flow state of.
*/
class DomConfigurationSetParameterTransformer extends XxeFlowStateTransformer {
Expr newValue;
DomConfigurationSetParameterTransformer() {
exists(FunctionCall getDomConfigCall, FunctionCall setParameterCall |
// this is the qualifier of a call to `DOMLSParser.getDomConfig`.
getDomConfigCall.getTarget() instanceof GetDomConfig and
this = getDomConfigCall.getQualifier() and
// `setParameterCall` is a call to `setParameter` on the return value of
// the same call to `DOMLSParser.getDomConfig`.
setParameterCall.getTarget() instanceof DomConfigurationSetParameter and
globalValueNumber(setParameterCall.getQualifier()).getAnExpr() = getDomConfigCall and
// the parameter being set is
// `XMLUni::fgXercesDisableDefaultEntityResolution`.
globalValueNumber(setParameterCall.getArgument(0)).getAnExpr().(VariableAccess).getTarget()
instanceof FeatureDisableDefaultEntityResolution and
// the value being set is `newValue`.
newValue = setParameterCall.getArgument(1)
)
}
final override XxeFlowState transform(XxeFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/unused-local-variable` no longer ignores functions that include `if` and `switch` statements with C++17-style initializers.

View File

@@ -1,5 +1,4 @@
## 0.1.2
### Minor Analysis Improvements
---
category: minorAnalysis
---
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query has been extended to support a broader selection of XML libraries and interfaces.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query precision has been increased to `high`.

View File

@@ -1,6 +0,0 @@
## 0.1.3
### Minor Analysis Improvements
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query precision has been increased to `high`.
* The `cpp/unused-local-variable` no longer ignores functions that include `if` and `switch` statements with C++17-style initializers.

View File

@@ -1 +0,0 @@
## 0.1.4

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.1.4
lastReleaseVersion: 0.1.1

View File

@@ -1,7 +0,0 @@
void test()
{
uint16_t j = 256;
char testSubject[122];
testSubject[j] = 12; // You can use a uint8 here
}

View File

@@ -1,18 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Find access to an array with a Uint16 when the array has a size lower than 256.</p>
</overview>
<recommendation>
<p>Use a int with a lower bit size instead. For instance in this example use a 8 bit int.</p>
</recommendation>
<example>
<sample src="WrongUintAccess.cpp" />
</example>
</qhelp>

View File

@@ -1,25 +0,0 @@
/**
* @id cpp/wrong-uint-access
* @name Wrong Uint
* @descripion Acess an array of size lower than 256 with a uint16.
* @kind problem
* @problem.severity recommendation
* @tags efficiency
*/
import cpp
from Variable var, ArrayExpr useExpr, ArrayType defLine, VariableAccess use
where
var.getUnspecifiedType() = defLine and
use = useExpr.getArrayBase() and
var = use.getTarget() and
(
useExpr.getArrayOffset().getType() instanceof UInt16_t or
useExpr.getArrayOffset().getType() instanceof UInt32_t or
useExpr.getArrayOffset().getType() instanceof UInt64_t
) and
defLine.getArraySize() <= 256
select useExpr,
"Using a " + useExpr.getArrayOffset().getType() + " to acess the array $@ of size " +
defLine.getArraySize() + ".", var, var.getName()

View File

@@ -58,7 +58,7 @@ where
// unfortunately cannot use numeric value here because // O_CREAT is defined differently on different OSes:
// https://github.com/red/red/blob/92feb0c0d5f91e087ab35fface6906afbf99b603/runtime/definitions.reds#L477-L491
// this may introduce false negatives
fctmp.getArgument(1).(BitwiseOrExpr).getAChild*().getValueText() = "O_CREAT" or
fctmp.getArgument(1).(BitwiseOrExpr).getAChild*().getValueText().matches("O_CREAT") or
fctmp.getArgument(1).getValueText().matches("%O_CREAT%")
) and
fctmp.getNumberOfArguments() = 2 and

View File

@@ -13,7 +13,7 @@ import cpp
from Function f
where
f.getName() = ["atof", "atoi", "atol"] and
f.getName().regexpMatch("atof|atoi|atol") and
f.getFile().getAbsolutePath().matches("%stdlib.h")
select f.getACallToThisFunction(),
"AV Rule 23: The library functions atof, atoi and atol from library <stdlib.h> shall not be used."

View File

@@ -13,7 +13,7 @@ import cpp
from Function f
where
f.getName() = ["abort", "exit", "getenv", "system"] and
f.getName().regexpMatch("abort|exit|getenv|system") and
f.getFile().getAbsolutePath().matches("%stdlib.h")
select f.getACallToThisFunction(),
"The library functions abort, exit, getenv and system from library <stdlib.h> should not be used."

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.1.4
version: 0.1.2-dev
groups:
- cpp
- queries

View File

@@ -181,7 +181,7 @@ private string expectationCommentPattern() { result = "\\s*\\$((?:[^/]|/[^/])*)(
/**
* The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first
* column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a
* column containing expected results preceded by the string `name:`.
* column containing expected results preceeded by the string `name:`.
*/
private newtype TColumn =
TDefaultColumn() or

View File

@@ -1,7 +1,6 @@
package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value
Dapper,55,,,,,,55,,,,
Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,,
Microsoft.EntityFrameworkCore,6,,,,,,6,,,,
Microsoft.Extensions.Primitives,,,54,,,,,,,54,
Microsoft.VisualBasic,,,4,,,,,,,,4
MySql.Data.MySqlClient,48,,,,,,48,,,,
1 package sink source summary sink:code sink:html sink:remote sink:sql sink:xss source:local summary:taint summary:value
2 Dapper 55 55
3 Microsoft.ApplicationBlocks.Data 28 28
Microsoft.EntityFrameworkCore 6 6
4 Microsoft.Extensions.Primitives 54 54
5 Microsoft.VisualBasic 4 4
6 MySql.Data.MySqlClient 48 48

View File

@@ -9,6 +9,6 @@ C# framework & library support
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
`ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
System,"``System.*``, ``System``",3,2336,28,5
Others,"``Dapper``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Primitives``, ``Microsoft.VisualBasic``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,149,137,
Totals,,3,2492,359,5
Others,"``Dapper``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.Extensions.Primitives``, ``Microsoft.VisualBasic``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,149,131,
Totals,,3,2492,353,5

View File

@@ -1,9 +1,3 @@
## 1.1.4
## 1.1.3
## 1.1.2
## 1.1.1
## 1.1.0

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.1.4
lastReleaseVersion: 1.1.1

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
version: 1.1.4
version: 1.1.2-dev
groups:
- csharp
- solorigate

View File

@@ -1,9 +1,3 @@
## 1.1.4
## 1.1.3
## 1.1.2
## 1.1.1
## 1.1.0

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.1.4
lastReleaseVersion: 1.1.1

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
version: 1.1.4
version: 1.1.2-dev
groups:
- csharp
- solorigate

View File

@@ -1,9 +1,3 @@
## 0.2.3
## 0.2.2
## 0.2.1
## 0.2.0
### Breaking Changes

View File

@@ -1 +0,0 @@
## 0.2.1

View File

@@ -1 +0,0 @@
## 0.2.2

View File

@@ -1 +0,0 @@
## 0.2.3

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.3
lastReleaseVersion: 0.2.0

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all
version: 0.2.3
version: 0.2.1-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp

View File

@@ -491,7 +491,7 @@ module Opcodes {
override Callable getTarget() { none() }
/** Gets the function pointer type targeted by this instruction. */
/** Gets the function pointer type targetted by this instruction. */
FunctionPointerType getTargetType() { cil_access(this, result) }
// The number of items popped/pushed from the stack depends on the target of

View File

@@ -117,7 +117,7 @@ class Modifiable extends Declaration, @modifiable {
* Note that explicit interface implementations are also considered effectively
* `private` if the implemented interface is itself effectively `private`. Finally,
* `private protected` members are not considered effectively `private`, because
* they can be overridden within the declaring assembly.
* they can be overriden within the declaring assembly.
*/
predicate isEffectivelyPrivate() {
this.isReallyPrivate() or
@@ -143,7 +143,7 @@ class Modifiable extends Declaration, @modifiable {
* considered. Explicit interface implementations are also considered effectively
* `internal` if the implemented interface is itself effectively `internal`. Finally,
* `internal protected` members are not considered effectively `internal`, because
* they can be overridden outside the declaring assembly.
* they can be overriden outside the declaring assembly.
*/
predicate isEffectivelyInternal() {
this.isReallyInternal() or

View File

@@ -265,7 +265,7 @@ class TypeMentionNode extends PrintAstNode, TTypeMentionNode {
final TypeMention getTypeMention() { result = typeMention }
/**
* Gets the `Element` targeted by the `TypeMention`.
* Gets the `Element` targetted by the `TypeMention`.
*/
final Element getTarget() { result = typeMention.getTarget() }

View File

@@ -672,7 +672,7 @@ module Unification {
* `ConstrainedTypeParameter::unifiable()` can be used.
*
*
* For performance reasons, type parameter constraints inside `t1` and `t2` are
* For performance reasons, type paramater constraints inside `t1` and `t2` are
* *not* taken into account, and there is also no guarantee that the same type
* parameter can be substituted with two different terms. For example, in
*

View File

@@ -744,7 +744,7 @@ cached
private module Cached {
/**
* If needed, call this predicate from `ControlFlowGraphImplSpecific.qll` in order to
* force a stage-dependency on the `ControlFlowGraphImplShared.qll` stage and thereby
* force a stage-dependency on the `ControlFlowGraphImplShared.qll` stage and therby
* collapsing the two stages.
*/
cached

View File

@@ -515,7 +515,9 @@ Element interpretElement(
/**
* Holds if `c` has a `generated` summary.
*/
predicate hasSummary(Callable c, boolean generated) { summaryElement(c, _, _, _, generated) }
predicate hasSummary(DataFlowCallable c, boolean generated) {
summaryElement(c, _, _, _, generated)
}
cached
private module Cached {

View File

@@ -1,7 +1,6 @@
/** Provides classes and predicates for defining flow summaries. */
import csharp
private import dotnet
private import internal.FlowSummaryImpl as Impl
private import internal.DataFlowDispatch as DataFlowDispatch
@@ -139,6 +138,28 @@ private class RecordConstructorFlow extends SummarizedCallable {
}
}
private class SummarizedCallableDefaultClearsContent extends Impl::Public::SummarizedCallable {
SummarizedCallableDefaultClearsContent() {
this instanceof Impl::Public::SummarizedCallable or none()
}
// By default, we assume that all stores into arguments are definite
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
exists(SummaryComponentStack output, SummaryComponent target |
this.propagatesFlow(_, output, _) and
output.drop(_) =
SummaryComponentStack::push(SummaryComponent::content(content),
SummaryComponentStack::singleton(target)) and
not content instanceof DataFlow::ElementContent
|
target = SummaryComponent::argument(pos.getPosition())
or
target = SummaryComponent::qualifier() and
pos.isThisParameter()
)
}
}
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
private class RecordConstructorFlowRequiredSummaryComponentStack extends RequiredSummaryComponentStack {

View File

@@ -1,529 +0,0 @@
/**
* Provides classes for performing global (inter-procedural)
* content-sensitive data flow analyses.
*/
private import DataFlowImplCommon
module ContentDataFlow {
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Private as DataFlowPrivate
private import DataFlowImplForContentDataFlow as DF
class Node = DF::Node;
class FlowFeature = DF::FlowFeature;
class ContentSet = DF::ContentSet;
predicate stageStats = DF::stageStats/8;
/**
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
* of this abstract class. To create a configuration, extend this class with
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
* ```ql
* class MyAnalysisConfiguration extends ContentDataFlowConfiguration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isBarrier`.
* // Optionally override `isAdditionalFlowStep`.
* // Optionally override `getAFeature`.
* // Optionally override `accessPathLimit`.
* // Optionally override `isRelevantContent`.
* }
* ```
*
* Unlike `DataFlow::Configuration` (on which this class is based), we allow
* for data to be stored (possibly nested) inside contents of sources and sinks.
* We track flow paths of the form
*
* ```
* source --value-->* node
* (--read--> node --value-->* node)*
* --(non-value|value)-->* node
* (--store--> node --value-->* node)*
* --value-->* sink
* ```
*
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
* step, `--store-->` is a store step, and `--(non-value)-->` is a
* non-value-preserving flow step.
*
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
* steps, followed by 0 or more stores, with value-preserving steps allowed in
* between all other steps.
*/
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant data flow source.
*/
abstract predicate isSource(Node source);
/**
* Holds if `sink` is a relevant data flow sink.
*/
abstract predicate isSink(Node sink);
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrier(Node node) { none() }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*/
FlowFeature getAFeature() { none() }
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
predicate isRelevantContent(ContentSet c) { any() }
/**
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
* for this configuration. `preservesValue` indicates whether any of the additional
* flow steps defined by `isAdditionalFlowStep` are needed.
*
* For the source access path, `sourceAp`, the top of the stack represents the content
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow from `source.Field2.Field1`.
*
* For the sink access path, `sinkAp`, the top of the stack represents the content
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
*/
final predicate hasFlow(
Node source, AccessPath sourceAp, Node sink, AccessPath sinkAp, boolean preservesValue
) {
exists(DF::PathNode pathSource, DF::PathNode pathSink |
this.(ConfigurationAdapter).hasFlowPath(pathSource, pathSink) and
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
source = pathSource.getNode() and
sink = pathSink.getNode()
|
pathSink.getState().(InitState).decode(preservesValue)
or
pathSink.getState().(ReadState).decode(_, preservesValue)
or
pathSink.getState().(StoreState).decode(_, preservesValue)
)
}
}
/** A flow state representing no reads or stores. */
private class InitState extends DF::FlowState {
private boolean preservesValue_;
InitState() { this = "Init(" + preservesValue_ + ")" and preservesValue_ in [false, true] }
predicate decode(boolean preservesValue) { preservesValue = preservesValue_ }
}
/** A flow state representing that content has been stored into. */
private class StoreState extends DF::FlowState {
private boolean preservesValue_;
private int size_;
StoreState() {
preservesValue_ in [false, true] and
size_ in [1 .. any(Configuration c).accessPathLimit()] and
this = "StoreState(" + size_ + "," + preservesValue_ + ")"
}
predicate decode(int size, boolean preservesValue) {
size = size_ and preservesValue = preservesValue_
}
}
/** A flow state representing that content has been read from. */
private class ReadState extends DF::FlowState {
private boolean preservesValue_;
private int size_;
ReadState() {
preservesValue_ in [false, true] and
size_ in [1 .. any(Configuration c).accessPathLimit()] and
this = "ReadState(" + size_ + "," + preservesValue_ + ")"
}
predicate decode(int size, boolean preservesValue) {
size = size_ and preservesValue = preservesValue_
}
}
private predicate storeStep(
Node node1, DF::FlowState state1, ContentSet c, Node node2, StoreState state2,
Configuration config
) {
exists(boolean preservesValue, int size |
storeSet(node1, c, node2, _, _) and
config.isRelevantContent(c) and
state2.decode(size + 1, preservesValue)
|
state1.(InitState).decode(preservesValue) and size = 0
or
state1.(ReadState).decode(_, preservesValue) and size = 0
or
state1.(StoreState).decode(size, preservesValue)
)
}
private predicate readStep(
Node node1, DF::FlowState state1, ContentSet c, Node node2, ReadState state2,
Configuration config
) {
exists(int size |
readSet(node1, c, node2) and
config.isRelevantContent(c) and
state2.decode(size + 1, true)
|
state1.(InitState).decode(true) and
size = 0
or
state1.(ReadState).decode(size, true)
)
}
private predicate additionalStep(
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2, Configuration config
) {
config.isAdditionalFlowStep(node1, node2) and
(
state1 instanceof InitState and
state2.(InitState).decode(false)
or
exists(int size |
state1.(ReadState).decode(size, _) and
state2.(ReadState).decode(size, false)
)
)
}
private class ConfigurationAdapter extends DF::Configuration {
private Configuration c;
ConfigurationAdapter() { this = c }
final override predicate isSource(Node source, DF::FlowState state) {
c.isSource(source) and
state.(InitState).decode(true)
}
final override predicate isSink(Node sink, DF::FlowState state) {
c.isSink(sink) and
(
state instanceof InitState or
state instanceof StoreState or
state instanceof ReadState
)
}
final override predicate isAdditionalFlowStep(
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2
) {
storeStep(node1, state1, _, node2, state2, this) or
readStep(node1, state1, _, node2, state2, this) or
additionalStep(node1, state1, node2, state2, this)
}
final override predicate isBarrier(Node node) { c.isBarrier(node) }
final override FlowFeature getAFeature() { result = c.getAFeature() }
// needed to record reads/stores inside summarized callables
final override predicate includeHiddenNodes() { any() }
}
private newtype TAccessPath =
TAccessPathNil() or
TAccessPathCons(ContentSet head, AccessPath tail) {
nodeReachesStore(_, _, _, _, head, _, tail)
or
nodeReachesRead(_, _, _, _, head, tail, _)
}
/** An access path. */
class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */
ContentSet getHead() { this = TAccessPathCons(result, _) }
/** Gets the tail of this access path, if any. */
AccessPath getTail() { this = TAccessPathCons(_, result) }
/**
* Gets a textual representation of this access path.
*
* Elements are dot-separated, and the head of the stack is
* rendered first.
*/
string toString() {
this = TAccessPathNil() and
result = ""
or
exists(ContentSet head, AccessPath tail |
this = TAccessPathCons(head, tail) and
result = head + "." + tail
)
}
}
// important to use `edges` and not `PathNode::getASuccessor()`, as the latter
// is not pruned for reachability
private predicate pathSucc = DF::PathGraph::edges/2;
/**
* Provides a big-step flow relation, where flow stops at read/store steps that
* must be recorded, and flow via `subpaths` such that reads/stores inside
* summarized callables can be recorded as well.
*/
private module BigStepFlow {
private predicate reachesSink(DF::PathNode node) {
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
or
exists(DF::PathNode mid |
pathSucc(node, mid) and
reachesSink(mid)
)
}
/**
* Holds if the flow step `pred -> succ` should not be allowed to be included
* in the big-step relation.
*/
pragma[nomagic]
private predicate excludeStep(DF::PathNode pred, DF::PathNode succ) {
pathSucc(pred, succ) and
(
// we need to record reads/stores inside summarized callables
DF::PathGraph::subpaths(pred, _, _, succ)
or
// only allow flow into a summarized callable, as part of the big-step
// relation, when flow can reach a sink without going back out
DF::PathGraph::subpaths(pred, succ, _, _) and
not reachesSink(succ)
or
// needed to record store steps
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
pred.getConfiguration())
or
// needed to record read steps
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
pred.getConfiguration())
)
}
pragma[nomagic]
private DataFlowCallable getEnclosingCallableImpl(DF::PathNode node) {
result = getNodeEnclosingCallable(node.getNode())
}
pragma[inline]
private DataFlowCallable getEnclosingCallable(DF::PathNode node) {
pragma[only_bind_into](result) = getEnclosingCallableImpl(pragma[only_bind_out](node))
}
pragma[nomagic]
private predicate bigStepEntry(DF::PathNode node) {
node.getConfiguration() instanceof Configuration and
(
any(ConfigurationAdapter config).isSource(node.getNode(), node.getState())
or
excludeStep(_, node)
or
DF::PathGraph::subpaths(_, node, _, _)
)
}
pragma[nomagic]
private predicate bigStepExit(DF::PathNode node) {
node.getConfiguration() instanceof Configuration and
(
bigStepEntry(node)
or
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
or
excludeStep(node, _)
or
DF::PathGraph::subpaths(_, _, node, _)
)
}
pragma[nomagic]
private predicate step(DF::PathNode pred, DF::PathNode succ) {
pathSucc(pred, succ) and
not excludeStep(pred, succ)
}
pragma[nomagic]
private predicate stepRec(DF::PathNode pred, DF::PathNode succ) {
step(pred, succ) and
not bigStepEntry(pred)
}
private predicate stepRecPlus(DF::PathNode n1, DF::PathNode n2) = fastTC(stepRec/2)(n1, n2)
/**
* Holds if there is flow `pathSucc+(pred) = succ`, and such a flow path does
* not go through any reads/stores that need to be recorded, or summarized
* steps.
*/
pragma[nomagic]
private predicate bigStep(DF::PathNode pred, DF::PathNode succ) {
exists(DF::PathNode mid |
bigStepEntry(pred) and
step(pred, mid)
|
succ = mid
or
stepRecPlus(mid, succ)
) and
bigStepExit(succ)
}
pragma[nomagic]
predicate bigStepNotLocal(DF::PathNode pred, DF::PathNode succ) {
bigStep(pred, succ) and
not getEnclosingCallable(pred) = getEnclosingCallable(succ)
}
pragma[nomagic]
predicate bigStepMaybeLocal(DF::PathNode pred, DF::PathNode succ) {
bigStep(pred, succ) and
getEnclosingCallable(pred) = getEnclosingCallable(succ)
}
}
/**
* Holds if `source` can reach `node`, having read `reads` from the source and
* written `stores` into `node`.
*
* `source` is either a source from a configuration, in which case `scReads` and
* `scStores` are always empty, or it is the parameter of a summarized callable,
* in which case `scReads` and `scStores` record the reads/stores for a summary
* context, that is, the reads/stores for an argument that can reach the parameter.
*/
pragma[nomagic]
private predicate nodeReaches(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node,
AccessPath reads, AccessPath stores
) {
exists(ConfigurationAdapter config |
node = source and
reads = scReads and
stores = scStores
|
config.hasFlowPath(source, _) and
scReads = TAccessPathNil() and
scStores = TAccessPathNil()
or
// the argument in a sub path can be reached, so we start flow from the sub path
// parameter, while recording the read/store summary context
exists(DF::PathNode arg |
nodeReachesSubpathArg(_, _, _, arg, scReads, scStores) and
DF::PathGraph::subpaths(arg, source, _, _)
)
)
or
exists(DF::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
BigStepFlow::bigStepMaybeLocal(mid, node)
)
or
exists(DF::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
BigStepFlow::bigStepNotLocal(mid, node) and
// when flow is not local, we cannot flow back out, so we may stop
// flow early when computing summary flow
any(ConfigurationAdapter config).hasFlowPath(source, _) and
scReads = TAccessPathNil() and
scStores = TAccessPathNil()
)
or
// store step
exists(AccessPath storesMid, ContentSet c |
nodeReachesStore(source, scReads, scStores, node, c, reads, storesMid) and
stores = TAccessPathCons(c, storesMid)
)
or
// read step
exists(AccessPath readsMid, ContentSet c |
nodeReachesRead(source, scReads, scStores, node, c, readsMid, stores) and
reads = TAccessPathCons(c, readsMid)
)
or
// flow-through step; match outer stores/reads with inner store/read summary contexts
exists(DF::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
nodeReachesSubpathArg(source, scReads, scStores, mid, innerScReads, innerScStores) and
subpathArgReachesOut(mid, innerScReads, innerScStores, node, reads, stores)
)
}
pragma[nomagic]
private predicate nodeReachesStore(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
AccessPath reads, AccessPath stores
) {
exists(DF::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
node.getConfiguration()) and
pathSucc(mid, node)
)
}
pragma[nomagic]
private predicate nodeReachesRead(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
AccessPath reads, AccessPath stores
) {
exists(DF::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
node.getConfiguration()) and
pathSucc(mid, node)
)
}
pragma[nomagic]
private predicate nodeReachesSubpathArg(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode arg,
AccessPath reads, AccessPath stores
) {
nodeReaches(source, scReads, scStores, arg, reads, stores) and
DF::PathGraph::subpaths(arg, _, _, _)
}
pragma[nomagic]
private predicate subpathArgReachesOut(
DF::PathNode arg, AccessPath scReads, AccessPath scStores, DF::PathNode out, AccessPath reads,
AccessPath stores
) {
exists(DF::PathNode source, DF::PathNode ret |
nodeReaches(source, scReads, scStores, ret, reads, stores) and
DF::PathGraph::subpaths(arg, source, ret, out)
)
}
}

View File

@@ -12,6 +12,14 @@ private import semmle.code.csharp.dispatch.RuntimeCallable
private import semmle.code.csharp.frameworks.system.Collections
private import semmle.code.csharp.frameworks.system.collections.Generic
private predicate summarizedCallable(DataFlowCallable c) {
c instanceof FlowSummary::SummarizedCallable
or
FlowSummaryImpl::Private::summaryReturnNode(_, TJumpReturnKind(c, _))
or
c = interpretElement(_, _, _, _, _, _)
}
/**
* Gets a source declaration of callable `c` that has a body or has
* a flow summary.
@@ -21,6 +29,9 @@ private import semmle.code.csharp.frameworks.system.collections.Generic
*/
DotNet::Callable getCallableForDataFlow(DotNet::Callable c) {
exists(DotNet::Callable unboundDecl | unboundDecl = c.getUnboundDeclaration() |
summarizedCallable(unboundDecl) and
result = unboundDecl
or
result.hasBody() and
if unboundDecl.getFile().fromSource()
then
@@ -70,46 +81,17 @@ newtype TReturnKind =
v = def.getSourceVariable().getAssignable()
)
} or
TJumpReturnKind(Callable target, ReturnKind rk) {
target.isUnboundDeclaration() and
TJumpReturnKind(DataFlowCallable target, ReturnKind rk) {
rk instanceof NormalReturnKind and
(
rk instanceof NormalReturnKind and
(
target instanceof Constructor or
not target.getReturnType() instanceof VoidType
)
or
exists(target.getParameter(rk.(OutRefReturnKind).getPosition()))
target instanceof Constructor or
not target.getReturnType() instanceof VoidType
)
}
/**
* A summarized callable where the summary should be used for dataflow analysis.
*/
class DataFlowSummarizedCallable instanceof FlowSummary::SummarizedCallable {
DataFlowSummarizedCallable() {
not this.fromSource()
or
this.fromSource() and not this.isAutoGenerated()
exists(target.getParameter(rk.(OutRefReturnKind).getPosition()))
}
string toString() { result = super.toString() }
}
private module Cached {
/**
* The following heuristic is used to rank when to use source code or when to use summaries for DataFlowCallables.
* 1. Use hand written summaries.
* 2. Use source code.
* 3. Use auto generated summaries.
*/
cached
newtype TDataFlowCallable =
TDotNetCallable(DotNet::Callable c) {
c.isUnboundDeclaration() and not c instanceof DataFlowSummarizedCallable
} or
TSummarizedCallable(DataFlowSummarizedCallable sc)
cached
newtype TDataFlowCall =
TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) {
@@ -126,7 +108,7 @@ private module Cached {
// No need to include calls that are compiled from source
not call.getImplementation().getMethod().compiledFromSource()
} or
TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) {
TSummaryCall(FlowSummary::SummarizedCallable c, Node receiver) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
@@ -162,7 +144,7 @@ private module DispatchImpl {
* call is a delegate call, or if the qualifier accesses a parameter of
* the enclosing callable `c` (including the implicit `this` parameter).
*/
predicate mayBenefitFromCallContext(NonDelegateDataFlowCall call, DataFlowCallable c) {
predicate mayBenefitFromCallContext(NonDelegateDataFlowCall call, Callable c) {
c = call.getEnclosingCallable() and
call.getDispatchCall().mayBenefitFromCallContext()
}
@@ -172,7 +154,7 @@ private module DispatchImpl {
* restricted to those `call`s for which a context might make a difference.
*/
DataFlowCallable viableImplInCallContext(NonDelegateDataFlowCall call, DataFlowCall ctx) {
result.getUnderlyingCallable() =
result =
call.getDispatchCall()
.getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall())
.getUnboundDeclaration()
@@ -251,13 +233,13 @@ class ImplicitCapturedReturnKind extends ReturnKind, TImplicitCapturedReturnKind
* one API entry point and out of another.
*/
class JumpReturnKind extends ReturnKind, TJumpReturnKind {
private Callable target;
private DataFlowCallable target;
private ReturnKind rk;
JumpReturnKind() { this = TJumpReturnKind(target, rk) }
/** Gets the target of the jump. */
Callable getTarget() { result = target }
DataFlowCallable getTarget() { result = target }
/** Gets the return kind of the target. */
ReturnKind getTargetReturnKind() { result = rk }
@@ -265,24 +247,8 @@ class JumpReturnKind extends ReturnKind, TJumpReturnKind {
override string toString() { result = "jump to " + target }
}
/** A callable used for data flow. */
class DataFlowCallable extends TDataFlowCallable {
/** Get the underlying source code callable, if any. */
DotNet::Callable asCallable() { this = TDotNetCallable(result) }
/** Get the underlying summarized callable, if any. */
FlowSummary::SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }
/** Get the underlying callable. */
DotNet::Callable getUnderlyingCallable() {
result = this.asCallable() or result = this.asSummarizedCallable()
}
/** Gets a textual representation of this dataflow callable. */
string toString() { result = this.getUnderlyingCallable().toString() }
/** Get the location of this dataflow callable. */
Location getLocation() { result = this.getUnderlyingCallable().getLocation() }
class DataFlowCallable extends DotNet::Callable {
DataFlowCallable() { this.isUnboundDeclaration() }
}
/** A call relevant for data flow. */
@@ -340,32 +306,18 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
DispatchCall getDispatchCall() { result = dc }
override DataFlowCallable getARuntimeTarget() {
result.asCallable() = getCallableForDataFlow(dc.getADynamicTarget())
result = getCallableForDataFlow(dc.getADynamicTarget())
or
exists(Callable c, boolean static |
result.asSummarizedCallable() = c and
c = this.getATarget(static)
|
static = false
or
static = true and not c instanceof RuntimeCallable
)
}
/** Gets a static or dynamic target of this call. */
Callable getATarget(boolean static) {
result = dc.getADynamicTarget().getUnboundDeclaration() and static = false
or
result = dc.getAStaticTarget().getUnboundDeclaration() and static = true
result = dc.getAStaticTarget().getUnboundDeclaration() and
summarizedCallable(result) and
not result instanceof RuntimeCallable
}
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
override DataFlowCallable getEnclosingCallable() {
result.getUnderlyingCallable() = cfn.getEnclosingCallable()
}
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
override string toString() { result = cfn.toString() }
@@ -393,9 +345,7 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
override DataFlowCallable getEnclosingCallable() {
result.getUnderlyingCallable() = cfn.getEnclosingCallable()
}
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
override string toString() { result = cfn.toString() }
@@ -413,15 +363,13 @@ class TransitiveCapturedDataFlowCall extends DataFlowCall, TTransitiveCapturedCa
TransitiveCapturedDataFlowCall() { this = TTransitiveCapturedCall(cfn, target) }
override DataFlowCallable getARuntimeTarget() { result.getUnderlyingCallable() = target }
override DataFlowCallable getARuntimeTarget() { result = target }
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
override DataFlow::ExprNode getNode() { none() }
override DataFlowCallable getEnclosingCallable() {
result.getUnderlyingCallable() = cfn.getEnclosingCallable()
}
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
override string toString() { result = "[transitive] " + cfn.toString() }
@@ -436,16 +384,14 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
override DataFlowCallable getARuntimeTarget() {
// There is no dispatch library for CIL, so do not consider overrides for now
result.getUnderlyingCallable() = getCallableForDataFlow(call.getTarget())
result = getCallableForDataFlow(call.getTarget())
}
override ControlFlow::Nodes::ElementNode getControlFlowNode() { none() }
override DataFlow::ExprNode getNode() { result.getExpr() = call }
override DataFlowCallable getEnclosingCallable() {
result.getUnderlyingCallable() = call.getEnclosingCallable()
}
override DataFlowCallable getEnclosingCallable() { result = call.getEnclosingCallable() }
override string toString() { result = call.toString() }
@@ -460,7 +406,7 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
* the method `Select`.
*/
class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
private FlowSummaryImpl::Public::SummarizedCallable c;
private FlowSummary::SummarizedCallable c;
private Node receiver;
SummaryCall() { this = TSummaryCall(c, receiver) }
@@ -476,7 +422,7 @@ class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
override DataFlow::Node getNode() { none() }
override DataFlowCallable getEnclosingCallable() { result.asSummarizedCallable() = c }
override DataFlowCallable getEnclosingCallable() { result = c }
override string toString() { result = "[summary] call to " + receiver + " in " + c }

View File

@@ -216,9 +216,10 @@ private module LambdaFlow {
or
// jump step
exists(Node mid, DataFlowType t0 |
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
toReturn = false and
toJump = true
toJump = true and
lastCall = TDataFlowCallNone()
|
jumpStepCached(node, mid) and
t = t0
@@ -304,7 +305,7 @@ cached
private module Cached {
/**
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
* collapsing the two stages.
*/
cached
@@ -788,31 +789,24 @@ private module Cached {
cached
predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) }
cached
predicate storeSet(
Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
storeStep(node1, c, node2) and
contentType = getNodeDataFlowType(node1) and
containerType = getNodeDataFlowType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
or
readSet(n2, c, n1) and
contentType = getNodeDataFlowType(n1) and
containerType = getNodeDataFlowType(n2)
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
exists(ContentSet cs | c = cs.getAStoreContent() |
storeStep(node1, cs, node2) and
contentType = getNodeDataFlowType(node1) and
containerType = getNodeDataFlowType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, cs, contentType), n1)
or
readSet(n2, cs, n1) and
contentType = getNodeDataFlowType(n1) and
containerType = getNodeDataFlowType(n2)
)
)
}

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