mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
Merge branch 'main' into py-restframework
This commit is contained in:
23
.github/dependabot.yml
vendored
23
.github/dependabot.yml
vendored
@@ -17,3 +17,26 @@ updates:
|
||||
ignore:
|
||||
- dependency-name: '*'
|
||||
update-types: ['version-update:semver-patch', 'version-update:semver-minor']
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "go/extractor"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
allow:
|
||||
- dependency-name: "golang.org/x/mod"
|
||||
- dependency-name: "golang.org/x/tools"
|
||||
groups:
|
||||
extractor-dependencies:
|
||||
patterns:
|
||||
- "golang.org/x/*"
|
||||
reviewers:
|
||||
- "github/codeql-go"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "go/ql/test"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
reviewers:
|
||||
- "github/codeql-go"
|
||||
|
||||
30
.github/workflows/check-change-note.yml
vendored
30
.github/workflows/check-change-note.yml
vendored
@@ -15,20 +15,34 @@ on:
|
||||
|
||||
jobs:
|
||||
check-change-note:
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
PULL_REQUEST_NUMBER: ${{ github.event.number }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Fail if no change note found. To fix, either add one, or add the `no-change-note-required` label.
|
||||
if: |
|
||||
github.event.pull_request.draft == false &&
|
||||
!contains(github.event.pull_request.labels.*.name, 'no-change-note-required')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' |
|
||||
grep true -c
|
||||
change_note_files=$(gh api "repos/$REPO/pulls/$PULL_REQUEST_NUMBER/files" --paginate --jq '.[].filename | select(test("/change-notes/.*[.]md$"))')
|
||||
|
||||
if [ -z "$change_note_files" ]; then
|
||||
echo "No change note found. Either add one, or add the 'no-change-note-required' label."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Change notes found:"
|
||||
echo "$change_note_files"
|
||||
|
||||
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md', 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text, or released/x.y.z.md for released change-notes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$"))' |
|
||||
grep true -c
|
||||
bad_change_note_file_names=$(gh api "repos/$REPO/pulls/$PULL_REQUEST_NUMBER/files" --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))][] | select((test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$")) | not)')
|
||||
|
||||
if [ -n "$bad_change_note_file_names" ]; then
|
||||
echo "The following change note file names are invalid:"
|
||||
echo "$bad_change_note_file_names"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
4
.github/workflows/compile-queries.yml
vendored
4
.github/workflows/compile-queries.yml
vendored
@@ -29,9 +29,9 @@ jobs:
|
||||
# run with --check-only if running in a PR (github.sha != main)
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500
|
||||
- name: compile queries - full
|
||||
# do full compile if running on main - this populates the cache
|
||||
if : ${{ github.event_name != 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500
|
||||
|
||||
2
.github/workflows/csharp-qltest.yml
vendored
2
.github/workflows/csharp-qltest.yml
vendored
@@ -91,7 +91,7 @@ jobs:
|
||||
run: |
|
||||
# Generate (Asp)NetCore stubs
|
||||
STUBS_PATH=stubs_output
|
||||
python3 ql/src/Stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger latest "$STUBS_PATH"
|
||||
python3 ql/src/Stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger 6.5.0 "$STUBS_PATH"
|
||||
rm -rf ql/test/resources/stubs/_frameworks
|
||||
# Update existing stubs in the repo with the freshly generated ones
|
||||
mv "$STUBS_PATH/output/stubs/_frameworks" ql/test/resources/stubs/
|
||||
|
||||
@@ -498,22 +498,6 @@
|
||||
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll"
|
||||
],
|
||||
"TaintedFormatStringQuery Ruby/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringQuery.qll"
|
||||
],
|
||||
"TaintedFormatStringCustomizations Ruby/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringCustomizations.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringCustomizations.qll"
|
||||
],
|
||||
"HttpToFileAccessQuery JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessQuery.qll"
|
||||
],
|
||||
"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"
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_filegroup")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
alias(
|
||||
name = "dbscheme",
|
||||
actual = "//cpp/ql/lib:dbscheme",
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "dbscheme-stats",
|
||||
actual = "//cpp/ql/lib:dbscheme-stats",
|
||||
)
|
||||
|
||||
pkg_filegroup(
|
||||
name = "db-files",
|
||||
srcs = [
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
class BuiltinType extends @builtintype {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from BuiltinType type, string name, int kind, int kind_new, int size, int sign, int alignment
|
||||
where
|
||||
builtintypes(type, name, kind, size, sign, alignment) and
|
||||
if
|
||||
type instanceof @fp16 or
|
||||
type instanceof @std_bfloat16 or
|
||||
type instanceof @std_float16 or
|
||||
type instanceof @complex_std_float32 or
|
||||
type instanceof @complex_float32x or
|
||||
type instanceof @complex_std_float64 or
|
||||
type instanceof @complex_float64x or
|
||||
type instanceof @complex_std_float128
|
||||
then kind_new = 2
|
||||
else kind_new = kind
|
||||
select type, name, kind_new, size, sign, alignment
|
||||
2221
cpp/downgrades/5b388693c66db1e7dc2e76a90aa67a2b6eb74f0f/old.dbscheme
Normal file
2221
cpp/downgrades/5b388693c66db1e7dc2e76a90aa67a2b6eb74f0f/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Introduce new floating-point types from C23 and C++23
|
||||
compatibility: backwards
|
||||
builtintypes.rel: run builtintypes.qlo
|
||||
@@ -1,7 +1,7 @@
|
||||
package(default_visibility = ["//cpp:__pkg__"])
|
||||
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_files")
|
||||
|
||||
package(default_visibility = ["//cpp:__pkg__"])
|
||||
|
||||
pkg_files(
|
||||
name = "dbscheme",
|
||||
srcs = ["semmlecode.cpp.dbscheme"],
|
||||
|
||||
@@ -1,3 +1,39 @@
|
||||
## 0.10.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `AnalysedString` class, use the new name `AnalyzedString`.
|
||||
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.
|
||||
|
||||
## 0.10.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
|
||||
non-returning in the IR and dataflow.
|
||||
* Treat functions that reach the end of the function as returning in the IR.
|
||||
They used to be treated as unreachable but it is allowed in C.
|
||||
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
|
||||
|
||||
## 0.9.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.9.2
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
|
||||
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
|
||||
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added support for new floating-point types in C23 and C++23.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Treat functions that reach the end of the function as returning in the IR.
|
||||
They used to be treated as unreachable but it is allowed in C.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
|
||||
non-returning in the IR and dataflow.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Added a new class `AdditionalCallTarget` for specifying additional call targets.
|
||||
9
cpp/ql/lib/change-notes/released/0.10.0.md
Normal file
9
cpp/ql/lib/change-notes/released/0.10.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 0.10.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
|
||||
non-returning in the IR and dataflow.
|
||||
* Treat functions that reach the end of the function as returning in the IR.
|
||||
They used to be treated as unreachable but it is allowed in C.
|
||||
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
|
||||
6
cpp/ql/lib/change-notes/released/0.10.1.md
Normal file
6
cpp/ql/lib/change-notes/released/0.10.1.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## 0.10.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `AnalysedString` class, use the new name `AnalyzedString`.
|
||||
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.
|
||||
14
cpp/ql/lib/change-notes/released/0.9.2.md
Normal file
14
cpp/ql/lib/change-notes/released/0.9.2.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 0.9.2
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
|
||||
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
|
||||
3
cpp/ql/lib/change-notes/released/0.9.3.md
Normal file
3
cpp/ql/lib/change-notes/released/0.9.3.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.9.3
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.1
|
||||
lastReleaseVersion: 0.10.1
|
||||
|
||||
3
cpp/ql/lib/experimental/cryptography/Concepts.qll
Normal file
3
cpp/ql/lib/experimental/cryptography/Concepts.qll
Normal file
@@ -0,0 +1,3 @@
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
import experimental.cryptography.modules.OpenSSL as OpenSSL
|
||||
239
cpp/ql/lib/experimental/cryptography/CryptoAlgorithmNames.qll
Normal file
239
cpp/ql/lib/experimental/cryptography/CryptoAlgorithmNames.qll
Normal file
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Names of known cryptographic algorithms.
|
||||
* The names are standardized into upper-case, no spaces, dashes or underscores.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a string to represent generally unknown algorithms.
|
||||
* Predicate is to be used to get a consistent string representation
|
||||
* for unknown algorithms.
|
||||
*/
|
||||
string unknownAlgorithm() { result = "UNKNOWN" }
|
||||
|
||||
string getHashType() { result = "HASH" }
|
||||
|
||||
string getSymmetricEncryptionType() { result = "SYMMETRIC_ENCRYPTION" }
|
||||
|
||||
string getAsymmetricEncryptionType() { result = "ASYMMETRIC_ENCRYPTION" }
|
||||
|
||||
string getKeyDerivationType() { result = "KEY_DERIVATION" }
|
||||
|
||||
string getCipherBlockModeType() { result = "BLOCK_MODE" }
|
||||
|
||||
string getSymmetricPaddingType() { result = "SYMMETRIC_PADDING" }
|
||||
|
||||
string getAsymmetricPaddingType() { result = "ASYMMETRIC_PADDING" }
|
||||
|
||||
string getEllipticCurveType() { result = "ELLIPTIC_CURVE" }
|
||||
|
||||
string getSignatureType() { result = "SIGNATURE" }
|
||||
|
||||
string getKeyExchangeType() { result = "KEY_EXCHANGE" }
|
||||
|
||||
string getAsymmetricType() {
|
||||
result in [
|
||||
getAsymmetricEncryptionType(), getSignatureType(), getKeyExchangeType(),
|
||||
getEllipticCurveType()
|
||||
]
|
||||
}
|
||||
|
||||
predicate isKnownType(string algType) {
|
||||
algType in [
|
||||
getHashType(), getSymmetricEncryptionType(), getAsymmetricEncryptionType(),
|
||||
getKeyDerivationType(), getCipherBlockModeType(), getSymmetricPaddingType(),
|
||||
getAsymmetricPaddingType(), getEllipticCurveType(), getSignatureType(), getKeyExchangeType()
|
||||
]
|
||||
}
|
||||
|
||||
predicate isKnownAlgorithm(string name) { isKnownAlgorithm(name, _) }
|
||||
|
||||
predicate isKnownAlgorithm(string name, string algType) {
|
||||
isHashingAlgorithm(name) and algType = "HASH"
|
||||
or
|
||||
isEncryptionAlgorithm(name, algType) and
|
||||
algType in ["SYMMETRIC_ENCRYPTION", "ASYMMETRIC_ENCRYPTION"]
|
||||
or
|
||||
isKeyDerivationAlgorithm(name) and algType = "KEY_DERIVATION"
|
||||
or
|
||||
isCipherBlockModeAlgorithm(name) and algType = "BLOCK_MODE"
|
||||
or
|
||||
isPaddingAlgorithm(name, algType) and algType in ["SYMMETRIC_PADDING", "ASYMMETRIC_PADDING"]
|
||||
or
|
||||
isEllipticCurveAlgorithm(name) and algType = "ELLIPTIC_CURVE"
|
||||
or
|
||||
isSignatureAlgorithm(name) and algType = "SIGNATURE"
|
||||
or
|
||||
isKeyExchangeAlgorithm(name) and algType = "KEY_EXCHANGE"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a known hashing algorithm in the model/library.
|
||||
*/
|
||||
predicate isHashingAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"BLAKE2", "BLAKE2B", "BLAKE2S", "SHA2", "SHA224", "SHA256", "SHA384", "SHA512", "SHA512224",
|
||||
"SHA512256", "SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512", "SHAKE128", "SHAKE256",
|
||||
"SM3", "WHIRLPOOL", "POLY1305", "HAVEL128", "MD2", "MD4", "MD5", "PANAMA", "RIPEMD",
|
||||
"RIPEMD128", "RIPEMD256", "RIPEMD160", "RIPEMD320", "SHA0", "SHA1", "SHA", "MGF1", "MGF1SHA1",
|
||||
"MDC2", "SIPHASH"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isEncryptionAlgorithm(string name, string algType) {
|
||||
isAsymmetricEncryptionAlgorithm(name) and algType = "ASYMMETRIC_ENCRYPTION"
|
||||
or
|
||||
isSymmetricEncryptionAlgorithm(name) and algType = "SYMMETRIC_ENCRYPTION"
|
||||
}
|
||||
|
||||
predicate isEncryptionAlgorithm(string name) { isEncryptionAlgorithm(name, _) }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known symmetric encryption algorithm.
|
||||
*/
|
||||
predicate isSymmetricEncryptionAlgorithm(string name) {
|
||||
// NOTE: AES is meant to caputure all possible key lengths
|
||||
name =
|
||||
[
|
||||
"AES", "AES128", "AES192", "AES256", "ARIA", "BLOWFISH", "BF", "ECIES", "CAST", "CAST5",
|
||||
"CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "CHACHA", "CHACHA20",
|
||||
"CHACHA20POLY1305", "GOST", "GOSTR34102001", "GOSTR341094", "GOSTR341194", "GOST2814789",
|
||||
"GOSTR341194", "GOST2814789", "GOST28147", "GOSTR341094", "GOST89", "GOST94", "GOST34102012",
|
||||
"GOST34112012", "IDEA", "RABBIT", "SEED", "SM4", "DES", "DESX", "3DES", "TDES", "2DES",
|
||||
"DES3", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4", "RC4", "ARCFOUR", "ARC5",
|
||||
"RC5", "MAGMA", "KUZNYECHIK"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known key derivation algorithm.
|
||||
*/
|
||||
predicate isKeyDerivationAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"ARGON2", "CONCATKDF", "CONCATKDFHASH", "CONCATKDFHMAC", "KBKDFCMAC", "BCRYPT", "HKDF",
|
||||
"HKDFEXPAND", "KBKDF", "KBKDFHMAC", "PBKDF1", "PBKDF2", "PBKDF2HMAC", "PKCS5", "SCRYPT",
|
||||
"X963KDF", "EVPKDF"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known cipher block mode
|
||||
*/
|
||||
predicate isCipherBlockModeAlgorithm(string name) {
|
||||
name = ["CBC", "GCM", "CCM", "CFB", "OFB", "CFB8", "CTR", "OPENPGP", "XTS", "EAX", "SIV", "ECB"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known padding algorithm
|
||||
*/
|
||||
predicate isPaddingAlgorithm(string name, string algType) {
|
||||
isSymmetricPaddingAlgorithm(name) and algType = "SYMMETRIC_PADDING"
|
||||
or
|
||||
isAsymmetricPaddingAlgorithm(name) and algType = "ASYMMETRIC_PADDING"
|
||||
}
|
||||
|
||||
/**
|
||||
* holds if `name` corresponds to a known symmetric padding algorithm
|
||||
*/
|
||||
predicate isSymmetricPaddingAlgorithm(string name) { name = ["PKCS7", "ANSIX923"] }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known asymmetric padding algorithm
|
||||
*/
|
||||
predicate isAsymmetricPaddingAlgorithm(string name) { name = ["OAEP", "PKCS1V15", "PSS", "KEM"] }
|
||||
|
||||
predicate isBrainpoolCurve(string curveName, int keySize) {
|
||||
// ALL BRAINPOOL CURVES
|
||||
keySize in [160, 192, 224, 256, 320, 384, 512] and
|
||||
(
|
||||
curveName = "BRAINPOOLP" + keySize.toString() + "R1"
|
||||
or
|
||||
curveName = "BRAINPOOLP" + keySize.toString() + "T1"
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSecCurve(string curveName, int keySize) {
|
||||
// ALL SEC CURVES
|
||||
keySize in [112, 113, 128, 131, 160, 163, 192, 193, 224, 233, 239, 256, 283, 384, 409, 521, 571] and
|
||||
exists(string suff | suff in ["R1", "R2", "K1"] |
|
||||
curveName = "SECT" + keySize.toString() + suff or
|
||||
curveName = "SECP" + keySize.toString() + suff
|
||||
)
|
||||
}
|
||||
|
||||
predicate isC2Curve(string curveName, int keySize) {
|
||||
// ALL C2 CURVES
|
||||
keySize in [163, 176, 191, 208, 239, 272, 304, 359, 368, 431] and
|
||||
exists(string pre, string suff |
|
||||
pre in ["PNB", "ONB", "TNB"] and suff in ["V1", "V2", "V3", "V4", "V5", "W1", "R1"]
|
||||
|
|
||||
curveName = "C2" + pre + keySize.toString() + suff
|
||||
)
|
||||
}
|
||||
|
||||
predicate isPrimeCurve(string curveName, int keySize) {
|
||||
// ALL PRIME CURVES
|
||||
keySize in [192, 239, 256] and
|
||||
exists(string suff | suff in ["V1", "V2", "V3"] | curveName = "PRIME" + keySize.toString() + suff)
|
||||
}
|
||||
|
||||
predicate isEllipticCurveAlgorithm(string curveName) { isEllipticCurveAlgorithm(curveName, _) }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known elliptic curve.
|
||||
*/
|
||||
predicate isEllipticCurveAlgorithm(string curveName, int keySize) {
|
||||
isSecCurve(curveName, keySize)
|
||||
or
|
||||
isBrainpoolCurve(curveName, keySize)
|
||||
or
|
||||
isC2Curve(curveName, keySize)
|
||||
or
|
||||
isPrimeCurve(curveName, keySize)
|
||||
or
|
||||
curveName = "ES256" and keySize = 256
|
||||
or
|
||||
curveName = "CURVE25519" and keySize = 255
|
||||
or
|
||||
curveName = "X25519" and keySize = 255
|
||||
or
|
||||
curveName = "ED25519" and keySize = 255
|
||||
or
|
||||
curveName = "CURVE448" and keySize = 448 // TODO: need to check the key size
|
||||
or
|
||||
curveName = "ED448" and keySize = 448
|
||||
or
|
||||
curveName = "X448" and keySize = 448
|
||||
or
|
||||
curveName = "NUMSP256T1" and keySize = 256
|
||||
or
|
||||
curveName = "NUMSP384T1" and keySize = 384
|
||||
or
|
||||
curveName = "NUMSP512T1" and keySize = 512
|
||||
or
|
||||
curveName = "SM2" and keySize in [256, 512]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known signature algorithm.
|
||||
*/
|
||||
predicate isSignatureAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"DSA", "ECDSA", "EDDSA", "ES256", "ES256K", "ES384", "ES512", "ED25519", "ED448", "ECDSA256",
|
||||
"ECDSA384", "ECDSA512"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a key exchange algorithm.
|
||||
*/
|
||||
predicate isKeyExchangeAlgorithm(string name) {
|
||||
name = ["ECDH", "DH", "DIFFIEHELLMAN", "X25519", "X448"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known asymmetric encryption.
|
||||
*/
|
||||
predicate isAsymmetricEncryptionAlgorithm(string name) { name = ["RSA"] }
|
||||
316
cpp/ql/lib/experimental/cryptography/CryptoArtifact.qll
Normal file
316
cpp/ql/lib/experimental/cryptography/CryptoArtifact.qll
Normal file
@@ -0,0 +1,316 @@
|
||||
import cpp
|
||||
private import experimental.cryptography.CryptoAlgorithmNames
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
|
||||
/*
|
||||
* A cryptographic artifact is a DataFlow::Node associated with some
|
||||
* operation, algorithm, or any other aspect of cryptography.
|
||||
*/
|
||||
|
||||
abstract class CryptographicArtifact extends Expr { }
|
||||
|
||||
// /**
|
||||
// * Associates a symmetric encryption algorithm with a block mode.
|
||||
// * The DataFlow::Node representing this association should be the
|
||||
// * point where the algorithm and block mode are combined.
|
||||
// * This may be at the call to encryption or in the construction
|
||||
// * of an object prior to encryption.
|
||||
// */
|
||||
// abstract class SymmetricCipher extends CryptographicArtifact{
|
||||
// abstract SymmetricEncryptionAlgorithm getEncryptionAlgorithm();
|
||||
// abstract BlockMode getBlockMode();
|
||||
// final predicate hasBlockMode(){
|
||||
// exists(this.getBlockMode())
|
||||
// }
|
||||
// }
|
||||
// /**
|
||||
// * A cryptographic operation is a method call that invokes a cryptographic
|
||||
// * algorithm (encrypt/decrypt) or a function in support of a cryptographic algorithm
|
||||
// * (key generation).
|
||||
// *
|
||||
// * Since operations are related to or in support of algorithms, operations must
|
||||
// * provide a reference to their associated algorithm. Often operataions themselves
|
||||
// * encapsulate algorithms, so operations can also extend CryptographicAlgorithm
|
||||
// * and refer to themselves as the target algorithm.
|
||||
// */
|
||||
// abstract class CryptographicOperation extends CryptographicArtifact, Call{
|
||||
// // bindingset[paramName, ind]
|
||||
// // final DataFlow::Node getParameterSource(int ind, string paramName){
|
||||
// // result = Utils::getUltimateSrcFromApiNode(this.(API::CallNode).getParameter(ind, paramName))
|
||||
// // }
|
||||
// final string getAlgorithmName(){
|
||||
// if exists(this.getAlgorithm().getName())
|
||||
// then result = this.getAlgorithm().getName()
|
||||
// else result = unknownAlgorithm()
|
||||
// }
|
||||
// final predicate hasAlgorithm(){
|
||||
// exists(this.getAlgorithm())
|
||||
// }
|
||||
// final predicate isUnknownAlgorithm(){
|
||||
// this.getAlgorithmName() = unknownAlgorithm()
|
||||
// or
|
||||
// not this.hasAlgorithm()
|
||||
// }
|
||||
// // TODO: this might have to be parameterized by a configuration source for
|
||||
// // situations where an operation is passed an algorithm
|
||||
// abstract CryptographicAlgorithm getAlgorithm();
|
||||
// }
|
||||
// /** A key generation operation for asymmetric keys */
|
||||
// abstract class KeyGen extends CryptographicOperation{
|
||||
// int getAKeySizeInBits(){
|
||||
// result = getKeySizeInBits(_)
|
||||
// }
|
||||
// final predicate hasKeySize(Expr configSrc){
|
||||
// exists(this.getKeySizeInBits(configSrc))
|
||||
// }
|
||||
// final predicate hasKeySize(){
|
||||
// exists(this.getAKeySizeInBits())
|
||||
// }
|
||||
// abstract Expr getKeyConfigSrc();
|
||||
// abstract int getKeySizeInBits(Expr configSrc);
|
||||
// }
|
||||
abstract class CryptographicOperation extends CryptographicArtifact, Call { }
|
||||
|
||||
abstract class KeyGeneration extends CryptographicOperation {
|
||||
// TODO: what if the algorithm is UNKNOWN?
|
||||
abstract Expr getKeyConfigurationSource(CryptographicAlgorithm alg);
|
||||
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
int getKeySizeInBits(CryptographicAlgorithm alg) {
|
||||
result = this.getKeyConfigurationSource(alg).(Literal).getValue().toInt()
|
||||
}
|
||||
|
||||
predicate hasConstantKeySize(CryptographicAlgorithm alg) { exists(this.getKeySizeInBits(alg)) }
|
||||
|
||||
predicate hasKeyConfigurationSource(CryptographicAlgorithm alg) {
|
||||
exists(this.getKeyConfigurationSource(alg))
|
||||
}
|
||||
|
||||
Expr getAKeyConfigurationSource() { result = this.getKeyConfigurationSource(_) }
|
||||
}
|
||||
|
||||
abstract class AsymmetricKeyGeneration extends KeyGeneration { }
|
||||
|
||||
abstract class SymmetricKeyGeneration extends KeyGeneration { }
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm is a `CryptographicArtifact`
|
||||
* representing a cryptographic algorithm (see `CryptoAlgorithmNames.qll`).
|
||||
* Cryptographic algorithms can be functions referencing common crypto algorithms (e.g., hashlib.md5)
|
||||
* or strings that are used in cryptographic operation configurations (e.g., hashlib.new("md5")).
|
||||
* Cryptogrpahic algorithms may also be operations that wrap or abstract one or
|
||||
* more algorithms (e.g., cyrptography.fernet.Fernet and AES, CBC and PKCS7).
|
||||
*
|
||||
* In principle, this class should model the location where an algorithm enters the program, not
|
||||
* necessarily where it is used.
|
||||
*/
|
||||
abstract class CryptographicAlgorithm extends CryptographicArtifact {
|
||||
abstract string getName();
|
||||
|
||||
abstract string getAlgType();
|
||||
|
||||
// string getAlgType(){
|
||||
// if this instanceof HashAlgorithm then result = getHashType()
|
||||
// else if this instanceof KeyDerivationAlgorithm then result = getKeyDerivationType()
|
||||
// else if this instanceof SymmetricEncryptionAlgorithm then result = getSymmetricEncryptionType()
|
||||
// else if this instanceof AsymmetricEncryptionAlgorithm then result = getAsymmetricEncryptionType()
|
||||
// else if this instanceof SymmetricEncryptionAlgorithm then result = getSymmetricPaddingType()
|
||||
// else if this instanceof AsymmetricEncryptionAlgorithm then result = getAsymmetricPaddingType()
|
||||
// else if this instanceof EllipticCurveAlgorithm then result = getEllipticCurveType()
|
||||
// else if this instanceof BlockMode then result = getCipherBlockModeType()
|
||||
// else if this instanceof KeyExchangeAlgorithm then result = getKeyExchangeType()
|
||||
// else if this instanceof SigningAlgorithm then result = getSignatureType()
|
||||
// else result = unknownAlgorithm()
|
||||
// }
|
||||
// TODO: handle case where name isn't known, not just unknown?
|
||||
/**
|
||||
* Normalizes a raw name into a normalized name as found in `CryptoAlgorithmNames.qll`.
|
||||
* Subclassess should override for more api-specific normalization.
|
||||
* By deafult, converts a raw name to upper-case with no hyphen, underscore, hash, or space.
|
||||
*/
|
||||
bindingset[s]
|
||||
string normalizeName(string s) {
|
||||
exists(string normStr | normStr = s.toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
|
||||
result = normStr and isKnownAlgorithm(result)
|
||||
or
|
||||
result = unknownAlgorithm() and not isKnownAlgorithm(normStr)
|
||||
)
|
||||
}
|
||||
|
||||
abstract Expr configurationSink();
|
||||
|
||||
predicate hasConfigurationSink() { exists(this.configurationSink()) }
|
||||
}
|
||||
|
||||
abstract class HashAlgorithm extends CryptographicAlgorithm {
|
||||
final string getHashName() {
|
||||
if exists(string n | n = this.getName() and isHashingAlgorithm(n))
|
||||
then isHashingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getHashType() }
|
||||
}
|
||||
|
||||
abstract class KeyDerivationAlgorithm extends CryptographicAlgorithm {
|
||||
final string getKDFName() {
|
||||
if exists(string n | n = this.getName() and isKeyDerivationAlgorithm(n))
|
||||
then isKeyDerivationAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getKeyDerivationType() }
|
||||
}
|
||||
|
||||
// abstract class KeyDerivationOperation extends CryptographicOperation{
|
||||
// DataFlow::Node getIterationSizeSrc(){
|
||||
// none()
|
||||
// }
|
||||
// DataFlow::Node getSaltConfigSrc(){
|
||||
// none()
|
||||
// }
|
||||
// DataFlow::Node getHashConfigSrc(){
|
||||
// none()
|
||||
// }
|
||||
// // TODO: get encryption algorithm for CBC-based KDF?
|
||||
// DataFlow::Node getDerivedKeySizeSrc(){
|
||||
// none()
|
||||
// }
|
||||
// DataFlow::Node getModeSrc(){
|
||||
// none()
|
||||
// }
|
||||
// // TODO: add more to cover all the parameters of most KDF operations? Perhaps subclass for each type?
|
||||
// abstract predicate requiresIteration();
|
||||
// abstract predicate requiresSalt();
|
||||
// abstract predicate requiresHash();
|
||||
// //abstract predicate requiresKeySize(); // Going to assume all requires a size
|
||||
// abstract predicate requiresMode();
|
||||
// }
|
||||
abstract class EncryptionAlgorithm extends CryptographicAlgorithm {
|
||||
final predicate isAsymmetric() { this instanceof AsymmetricEncryptionAlgorithm }
|
||||
|
||||
final predicate isSymmetric() { not this.isAsymmetric() }
|
||||
// NOTE: DO_NOT add getEncryptionName here, we rely on the fact the parent
|
||||
// class does not have this common predicate.
|
||||
}
|
||||
|
||||
/**
|
||||
* A parent class to represent any algorithm for which
|
||||
* asymmetric cryptography is involved.
|
||||
* Intended to be distinct from AsymmetricEncryptionAlgorithm
|
||||
* which is intended only for asymmetric algorithms that specifically encrypt.
|
||||
*/
|
||||
abstract class AsymmetricAlgorithm extends CryptographicAlgorithm { }
|
||||
|
||||
/**
|
||||
* Algorithms directly or indirectly related to asymmetric encryption,
|
||||
* e.g., RSA, DSA, but also RSA padding algorithms
|
||||
*/
|
||||
abstract class AsymmetricEncryptionAlgorithm extends AsymmetricAlgorithm, EncryptionAlgorithm {
|
||||
final string getEncryptionName() {
|
||||
if exists(string n | n = this.getName() and isAsymmetricEncryptionAlgorithm(n))
|
||||
then isAsymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getAsymmetricEncryptionType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithms directly or indirectly related to symmetric encryption,
|
||||
* e.g., AES, DES, but also block modes and padding
|
||||
*/
|
||||
abstract class SymmetricEncryptionAlgorithm extends EncryptionAlgorithm {
|
||||
final string getEncryptionName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
|
||||
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
// TODO: add a stream cipher predicate?
|
||||
override string getAlgType() { result = getSymmetricEncryptionType() }
|
||||
}
|
||||
|
||||
// Used only to categorize all padding into a single object,
|
||||
// DO_NOT add predicates here. Only for categorization purposes.
|
||||
abstract class PaddingAlgorithm extends CryptographicAlgorithm { }
|
||||
|
||||
abstract class SymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricPaddingAlgorithm(n))
|
||||
then isSymmetricPaddingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getSymmetricPaddingType() }
|
||||
}
|
||||
|
||||
abstract class AsymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName() {
|
||||
if exists(string n | n = this.getName() and isAsymmetricPaddingAlgorithm(n))
|
||||
then isAsymmetricPaddingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getAsymmetricPaddingType() }
|
||||
}
|
||||
|
||||
abstract class EllipticCurveAlgorithm extends AsymmetricAlgorithm {
|
||||
final string getCurveName() {
|
||||
if exists(string n | n = this.getName() and isEllipticCurveAlgorithm(n))
|
||||
then isEllipticCurveAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
final int getCurveBitSize() { isEllipticCurveAlgorithm(this.getCurveName(), result) }
|
||||
|
||||
override string getAlgType() { result = getEllipticCurveType() }
|
||||
}
|
||||
|
||||
abstract class BlockModeAlgorithm extends CryptographicAlgorithm {
|
||||
final string getBlockModeName() {
|
||||
if exists(string n | n = this.getName() and isCipherBlockModeAlgorithm(n))
|
||||
then isCipherBlockModeAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source of the IV configuration.
|
||||
*/
|
||||
abstract Expr getIVorNonce();
|
||||
|
||||
final predicate hasIVorNonce() { exists(this.getIVorNonce()) }
|
||||
|
||||
override string getAlgType() { result = getCipherBlockModeType() }
|
||||
}
|
||||
|
||||
// abstract class KeyWrapOperation extends CryptographicOperation{
|
||||
// }
|
||||
abstract class AuthenticatedEncryptionAlgorithm extends SymmetricEncryptionAlgorithm {
|
||||
final string getAuthticatedEncryptionName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
|
||||
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KeyExchangeAlgorithm extends AsymmetricAlgorithm {
|
||||
final string getKeyExchangeName() {
|
||||
if exists(string n | n = this.getName() and isKeyExchangeAlgorithm(n))
|
||||
then isKeyExchangeAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getKeyExchangeType() }
|
||||
}
|
||||
|
||||
abstract class SigningAlgorithm extends AsymmetricAlgorithm {
|
||||
final string getSigningName() {
|
||||
if exists(string n | n = this.getName() and isSignatureAlgorithm(n))
|
||||
then isSignatureAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType() { result = getSignatureType() }
|
||||
}
|
||||
718
cpp/ql/lib/experimental/cryptography/modules/OpenSSL.qll
Normal file
718
cpp/ql/lib/experimental/cryptography/modules/OpenSSL.qll
Normal file
@@ -0,0 +1,718 @@
|
||||
import cpp
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
import experimental.cryptography.utils.OpenSSL.CryptoFunction
|
||||
import experimental.cryptography.utils.OpenSSL.AlgorithmSink
|
||||
import experimental.cryptography.utils.OpenSSL.PassthroughFunction
|
||||
import experimental.cryptography.utils.OpenSSL.CryptoAlgorithm
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
// import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Problematic case in OpenSSL speed.c
|
||||
* static const char *names[ALGOR_NUM] = {
|
||||
* "md2", "mdc2", "md4", "md5", "sha1", "rmd160",
|
||||
* "sha256", "sha512", "whirlpool", "hmac(md5)",
|
||||
* "des-cbc", "des-ede3", "rc4", "idea-cbc", "seed-cbc",
|
||||
* "rc2-cbc", "rc5-cbc", "blowfish", "cast-cbc",
|
||||
* "aes-128-cbc", "aes-192-cbc", "aes-256-cbc",
|
||||
* "camellia-128-cbc", "camellia-192-cbc", "camellia-256-cbc",
|
||||
* "evp", "ghash", "rand", "cmac"
|
||||
* };
|
||||
*
|
||||
* Every entry is considered a block mode, hash, and symmetric encryption algorithm
|
||||
* getEncryptionName for example, will return unknown
|
||||
*/
|
||||
predicate nodeToExpr(DataFlow::Node node, Expr e) {
|
||||
e = node.asExpr() or e = node.asIndirectArgument()
|
||||
}
|
||||
|
||||
Expr getExprFromNode(DataFlow::Node node) { nodeToExpr(node, result) }
|
||||
|
||||
DataFlow::Node getNodeFromExpr(Expr e) { nodeToExpr(result, e) }
|
||||
|
||||
predicate isEVP_PKEY_CTX(Type t) { t.getUnderlyingType().stripType().getName() = "evp_pkey_ctx_st" }
|
||||
|
||||
/**
|
||||
* An expression representing an EVP_PKEY_CTX* at the location of a
|
||||
* known AlgorithmSinkArgument.
|
||||
* The EVP_PKEY_CTX* represents the location where the CTX is tied to the algorithm,
|
||||
* and can be used as a source for tracing EVP_PKEY_CTX to other operations.
|
||||
*/
|
||||
class Known_EVP_PKEY_CTX_Ptr_Source extends Expr {
|
||||
Known_EVP_PKEY_CTX_Ptr_Source() {
|
||||
isEVP_PKEY_CTX(this.getUnderlyingType()) and
|
||||
this.getUnderlyingType() instanceof PointerType and
|
||||
exists(AlgorithmSinkArgument arg, Call sinkCall |
|
||||
arg.getSinkCall() = sinkCall and
|
||||
sinkCall.getAnArgument() = this
|
||||
or
|
||||
this = sinkCall
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// module CTXFlow implements DataFlow::ConfigSig{
|
||||
// predicate isSource(DataFlow::Node source) {
|
||||
// // ASSUMPTION: at a sink, an algorithm is converted into a CTX through a return of the call only
|
||||
// // and is the primary source of interest for CTX tracing
|
||||
// source.asExpr() instanceof AlgorithmSinkArgument
|
||||
// }
|
||||
// predicate isSink(DataFlow::Node sink){
|
||||
// sink.asExpr() instanceof CTXSink
|
||||
// }
|
||||
// predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// // cls.getName() = "asn1_object_st" flow out on any EVP_PKEY_CTX which is "evp_pkey_ctx_st"
|
||||
// exists(Call c |
|
||||
// isEVP_PKEY_CTX(c.getUnderlyingType()) and
|
||||
// node1.asExpr() = c.getAnArgument() and c = node2.asExpr())
|
||||
// }
|
||||
// }
|
||||
// module CTXFlowConfig = DataFlow::Global<CTXFlow>;
|
||||
// TODO: currently only handles tracing from literals to sinks
|
||||
module LiteralAlgorithmTracerConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() instanceof Literal and
|
||||
// Optimization to reduce literal tracing on integers to only those that are known/relevant NIDs.
|
||||
(
|
||||
exists(source.asExpr().getValue().toInt())
|
||||
implies
|
||||
source.asExpr().getValue().toInt() < getNIDMax()
|
||||
) and
|
||||
// False positives observed inside OBJ_nid2* and OBJ_sn2* functions where NULL is a possible assignment.
|
||||
// While this is a concern, it only occurs if the object being referenced is NULL to begin with
|
||||
// Perhaps a different query should be used to find these caes if they represent a threat.
|
||||
// Filter out any open ssl function source in a function namae Obj_*
|
||||
// False positives in OpenSSL also observed for CRYPTO_strndup (filtering any CRYPTO_* function)
|
||||
// due to setting a null byte in the string
|
||||
(
|
||||
isPossibleOpenSSLFunction(source.getEnclosingCallable())
|
||||
implies
|
||||
(
|
||||
not source.getEnclosingCallable().getName().matches("OBJ_%") and
|
||||
not source.getEnclosingCallable().getName().matches("CRYPTO_%")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// A sink is a call to a function that takes an algorithm as an argument
|
||||
// must include checks for asIndirectArgument since the input may be a pointer to an object
|
||||
// and the member of the object holds the algorithm on the trace.
|
||||
getExprFromNode(sink) instanceof AlgorithmSinkArgument
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
knownPassThroughStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// If the node is the 'next' argument of a isCallPassThrough, it is only allowed if it is an out parameter
|
||||
// i.e., a defining argument. This barrier says that if the node is an expression not an out parameter, it is filtered.
|
||||
// Out arguments will not be filtered.
|
||||
exists(Call c | knownPassthoughCall(c, _, node.asExpr()) and c.getAnArgument() = node.asExpr())
|
||||
or
|
||||
// False positive reducer, don't flow out through argv
|
||||
node.asVariable().hasName("argv")
|
||||
or
|
||||
node.asIndirectVariable().hasName("argv")
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
// Assume a read on crypto identifying field for any object of type asn1_object_st (i.e., ASN1_OBJECT)
|
||||
exists(Class cls | cls.getName() = "asn1_object_st" |
|
||||
node.getType().getUnspecifiedType().stripType() = cls and
|
||||
c.(DataFlow::FieldContent).getField() = cls.getAMember() and
|
||||
c.(DataFlow::FieldContent).getField().getName() in ["nid", "sn", "ln"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module LiteralAlgorithmTracer = DataFlow::Global<LiteralAlgorithmTracerConfig>;
|
||||
|
||||
/**
|
||||
* `source` is an expression that is a source of an algorithm of type `algType`.
|
||||
* `algType` may be `UNKONWN`.
|
||||
* See CryptoAlgorithmNames for other possible values of `algType`.
|
||||
*/
|
||||
bindingset[sinkAlgType]
|
||||
predicate hasLiteralPathToAlgSink(DataFlow::Node source, DataFlow::Node sink, string sinkAlgType) {
|
||||
LiteralAlgorithmTracer::flow(source, sink) and
|
||||
getExprFromNode(sink).(AlgorithmSinkArgument).algType() = sinkAlgType
|
||||
}
|
||||
|
||||
private predicate knownTracedAlgorithm(Literal e, string srcSinkType) {
|
||||
knownTracedAlgorithm(e, srcSinkType, srcSinkType)
|
||||
}
|
||||
|
||||
private predicate knownTracedAlgorithm(Literal e, string srcType, string sinkType) {
|
||||
resolveAlgorithmFromLiteral(e, _, srcType) and
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(e), _, sinkType) and
|
||||
isKnownType(sinkType) and
|
||||
isKnownType(srcType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedLiteralAlgorithm(Literal e, string srcSinkType) {
|
||||
// Asymmetric special case:
|
||||
// Since asymmetric algorithm sinks are used for various categories of asymmetric algorithms
|
||||
// an asymmetric algorithm is only unknown if there is no trace from any asymmetric type to the given srcSinkType sink
|
||||
if getAsymmetricType() = srcSinkType
|
||||
then forall(string t | t = getAsymmetricType() | unknownTracedLiteralAlgorithm(e, t, srcSinkType))
|
||||
else unknownTracedLiteralAlgorithm(e, srcSinkType, srcSinkType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedLiteralAlgorithm(Literal e, string srcType, string sinkType) {
|
||||
// the literal resolves to an algorithm, but not to the sinktype
|
||||
// or generally doesn't resolve to any algorithm type
|
||||
// this case covers 'nonsense' cases e.g., use RSA for symmetric encryption
|
||||
not resolveAlgorithmFromLiteral(e, _, srcType) and
|
||||
isValidAlgorithmLiteral(e) and
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(e), _, sinkType) and
|
||||
isKnownType(sinkType) and
|
||||
isKnownType(srcType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedNonLiteralAlgorithm(AlgorithmSinkArgument e, string srcSinkType) {
|
||||
// Asymmetric special case:
|
||||
// Since asymmetric algorithm sinks are used for various categories of asymmetric algorithms
|
||||
// an asymmetric algorithm is only unknown if there is no trace from any asymmetric type to the given srcSinkType sink
|
||||
if getAsymmetricType() = srcSinkType
|
||||
then
|
||||
forall(string t | t = getAsymmetricType() | unknownTracedNonLiteralAlgorithm(e, t, srcSinkType))
|
||||
else unknownTracedNonLiteralAlgorithm(e, srcSinkType, srcSinkType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedNonLiteralAlgorithm(
|
||||
AlgorithmSinkArgument e, string srcType, string sinkType
|
||||
) {
|
||||
not hasLiteralPathToAlgSink(_, getNodeFromExpr(e), srcType) and
|
||||
LiteralAlgorithmTracerConfig::isSink(getNodeFromExpr(e)) and
|
||||
e.algType() = sinkType and
|
||||
isKnownType(srcType) and
|
||||
isKnownType(sinkType)
|
||||
}
|
||||
|
||||
private predicate functionAlgorithm(Call c, string algType) {
|
||||
isOpenSSLCryptoFunctionCall(c, _, algType)
|
||||
}
|
||||
|
||||
abstract class OpenSSLTracedAlgorithm extends CryptographicAlgorithm {
|
||||
override string getName() { resolveAlgorithmFromLiteral(this, result, this.getAlgType()) }
|
||||
|
||||
override Expr configurationSink() {
|
||||
exists(DataFlow::Node sink |
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(this), sink, this.getAlgType())
|
||||
|
|
||||
result = getExprFromNode(sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OpenSSLFunctionAlgorithm extends CryptographicAlgorithm {
|
||||
override string getName() { isOpenSSLCryptoFunctionCall(this, result, this.getAlgType()) }
|
||||
|
||||
override Expr configurationSink() { result = this }
|
||||
}
|
||||
|
||||
abstract class OpenSSLUnknownTracedLiteralAlgorithm extends CryptographicAlgorithm {
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() {
|
||||
exists(DataFlow::Node sink |
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(this), sink, this.getAlgType())
|
||||
|
|
||||
result = getExprFromNode(sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OpenSSLUnknownTracedNonLiteralAlgorithm extends CryptographicAlgorithm {
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() { result = this }
|
||||
}
|
||||
|
||||
module SymmetricEncryption {
|
||||
abstract class OpenSSLSymmetricEncryptionAlgorithm extends SymmetricEncryptionAlgorithm { }
|
||||
|
||||
class OpenSSLSymmetricEncryptionTracedAlgorithm extends OpenSSLTracedAlgorithm,
|
||||
OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLSymmetricEncryptionTracedAlgorithm() {
|
||||
knownTracedAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSymmetricEncryptionFunctionAlgorithm extends OpenSSLFunctionAlgorithm,
|
||||
OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLSymmetricEncryptionFunctionAlgorithm() {
|
||||
functionAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSymmetricEncryptionTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLSymmetricEncryptionTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSymmetricEncryptionUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLSymmetricEncryptionUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module BlockModes {
|
||||
/**
|
||||
* In OpenSSL, block modes are associated directly with symmetric encryption algorithms.
|
||||
* As such, OpenSSLBLockModes are modeled as extensions of any openssl symmetric encryption algorithm
|
||||
*/
|
||||
class OpenSSLBlockModeAlgorithm extends BlockModeAlgorithm, Expr instanceof SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLBlockModeAlgorithm() {
|
||||
//two cases, either the block mode is a literal or it is a function call
|
||||
resolveAlgorithmFromLiteral(this, _, "BLOCK_MODE")
|
||||
or
|
||||
isOpenSSLCryptoFunctionCall(this, _, "BLOCK_MODE")
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
resolveAlgorithmFromLiteral(this, result, "BLOCK_MODE")
|
||||
or
|
||||
isOpenSSLCryptoFunctionCall(this, result, "BLOCK_MODE")
|
||||
}
|
||||
|
||||
override Expr configurationSink() {
|
||||
result = this.(SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm).configurationSink()
|
||||
}
|
||||
|
||||
override Expr getIVorNonce() {
|
||||
// TODO
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
class UnknownOpenSSLBlockModeAlgorithm extends BlockModeAlgorithm, Expr instanceof SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm
|
||||
{
|
||||
UnknownOpenSSLBlockModeAlgorithm() {
|
||||
//two cases, either the block mode is a literal or it is a function call
|
||||
not resolveAlgorithmFromLiteral(this, _, "BLOCK_MODE") and
|
||||
not isOpenSSLCryptoFunctionCall(this, _, "BLOCK_MODE")
|
||||
}
|
||||
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() {
|
||||
result = this.(SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm).configurationSink()
|
||||
}
|
||||
|
||||
override Expr getIVorNonce() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
module Hashes {
|
||||
abstract class OpenSSLHashAlgorithm extends HashAlgorithm { }
|
||||
|
||||
class OpenSSLHashTracedAlgorithm extends OpenSSLTracedAlgorithm, OpenSSLHashAlgorithm {
|
||||
OpenSSLHashTracedAlgorithm() { knownTracedAlgorithm(this, getHashType()) }
|
||||
}
|
||||
|
||||
class OpenSSLHashFunctionAlgorithm extends OpenSSLFunctionAlgorithm, OpenSSLHashAlgorithm {
|
||||
OpenSSLHashFunctionAlgorithm() { functionAlgorithm(this, getHashType()) }
|
||||
}
|
||||
|
||||
class OpenSSLHashTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
OpenSSLHashAlgorithm
|
||||
{
|
||||
OpenSSLHashTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getHashType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLHashUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
OpenSSLHashAlgorithm
|
||||
{
|
||||
OpenSSLHashUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getHashType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLNullHash extends HashAlgorithm {
|
||||
OpenSSLNullHash() {
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
c.getTarget().getName() in ["EVP_md_null"]
|
||||
)
|
||||
}
|
||||
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() { result = this }
|
||||
}
|
||||
}
|
||||
|
||||
module EllipticCurves {
|
||||
// TODO: need to address EVP_PKEY_Q_keygen where the type is "EC" but the curve is UNKNOWN?
|
||||
class OpenSSLEllipticCurveTracedAlgorithm extends OpenSSLTracedAlgorithm, EllipticCurveAlgorithm {
|
||||
OpenSSLEllipticCurveTracedAlgorithm() { knownTracedAlgorithm(this, getEllipticCurveType()) }
|
||||
}
|
||||
|
||||
class OpenSSLEllipticCurveFunctionAlgorithm extends OpenSSLFunctionAlgorithm,
|
||||
EllipticCurveAlgorithm
|
||||
{
|
||||
OpenSSLEllipticCurveFunctionAlgorithm() { functionAlgorithm(this, getEllipticCurveType()) }
|
||||
}
|
||||
|
||||
class OpenSSLEllipticCurveTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
EllipticCurveAlgorithm
|
||||
{
|
||||
OpenSSLEllipticCurveTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getEllipticCurveType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLEllipticCurvehUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
EllipticCurveAlgorithm
|
||||
{
|
||||
OpenSSLEllipticCurvehUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getEllipticCurveType())
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_ex.html
|
||||
class OpenSSLNullEllipticCurve extends EllipticCurveAlgorithm {
|
||||
OpenSSLNullEllipticCurve() {
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
c.getTarget().getName() in ["EC_KEY_new", "EC_KEY_new_ex"]
|
||||
)
|
||||
}
|
||||
|
||||
override string getName() { result = unknownAlgorithm() }
|
||||
|
||||
override Expr configurationSink() { result = this }
|
||||
}
|
||||
}
|
||||
|
||||
module AsymmetricEncryption {
|
||||
class OpenSSLAsymmetricEncryptionTracedAlgorithm extends OpenSSLTracedAlgorithm,
|
||||
AsymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLAsymmetricEncryptionTracedAlgorithm() {
|
||||
knownTracedAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLAsymmetricEncryptionFunctionAlgorithm extends OpenSSLFunctionAlgorithm,
|
||||
AsymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLAsymmetricEncryptionFunctionAlgorithm() {
|
||||
functionAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLAsymmetricEncryptionTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
AsymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLAsymmetricEncryptionTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLAsymmetricEncryptionUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
AsymmetricEncryptionAlgorithm
|
||||
{
|
||||
OpenSSLAsymmetricEncryptionUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module SigningAlgorithms {
|
||||
class OpenSSLSignatureTracedAlgorithm extends OpenSSLTracedAlgorithm, SigningAlgorithm {
|
||||
OpenSSLSignatureTracedAlgorithm() { knownTracedAlgorithm(this, getSignatureType()) }
|
||||
}
|
||||
|
||||
class OpenSSLSignatureFunctionAlgorithm extends OpenSSLFunctionAlgorithm, SigningAlgorithm {
|
||||
OpenSSLSignatureFunctionAlgorithm() { functionAlgorithm(this, getSignatureType()) }
|
||||
}
|
||||
|
||||
class OpenSSLSignatureTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
SigningAlgorithm
|
||||
{
|
||||
OpenSSLSignatureTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getSignatureType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSignatureUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
SigningAlgorithm
|
||||
{
|
||||
OpenSSLSignatureUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getSignatureType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module KeyExchange {
|
||||
class OpenSSLKeyExchangeTracedAlgorithm extends OpenSSLTracedAlgorithm, KeyExchangeAlgorithm {
|
||||
OpenSSLKeyExchangeTracedAlgorithm() { knownTracedAlgorithm(this, getKeyExchangeType()) }
|
||||
}
|
||||
|
||||
class OpenSSLKeyExchangeFunctionAlgorithm extends OpenSSLFunctionAlgorithm, KeyExchangeAlgorithm {
|
||||
OpenSSLKeyExchangeFunctionAlgorithm() { functionAlgorithm(this, getKeyExchangeType()) }
|
||||
}
|
||||
|
||||
class OpenSSLKeyExchangeTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
|
||||
KeyExchangeAlgorithm
|
||||
{
|
||||
OpenSSLKeyExchangeTracedUnknownLiteralAlgorithm() {
|
||||
unknownTracedLiteralAlgorithm(this, getKeyExchangeType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLKeyExchangeUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
|
||||
KeyExchangeAlgorithm
|
||||
{
|
||||
OpenSSLKeyExchangeUnknownNonLiteralTracedAlgorithm() {
|
||||
unknownTracedNonLiteralAlgorithm(this, getKeyExchangeType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module KeyGeneration {
|
||||
/**
|
||||
* Functions that explicitly set key generation parameters.
|
||||
* `sizeInd` is the parameter specifying the size of the key.
|
||||
* `outInd` is the parameter or return value that the key is written to.
|
||||
* `outInd` is -1 if the key is written to the return value.
|
||||
*/
|
||||
predicate isAsymmetricKeyGenExplicitAlgorithm(Function func, int sizeInd, int outInd) {
|
||||
isPossibleOpenSSLFunction(func) and
|
||||
exists(string name | func.hasGlobalName(name) |
|
||||
name in [
|
||||
"EVP_PKEY_CTX_set_dsa_paramgen_bits", "DSA_generate_parameters_ex",
|
||||
"EVP_PKEY_CTX_set_rsa_keygen_bits", "RSA_generate_key_ex", "RSA_generate_key_fips",
|
||||
"EVP_PKEY_CTX_set_dh_paramgen_prime_len", "DH_generate_parameters_ex"
|
||||
] and
|
||||
sizeInd = 1 and
|
||||
outInd = 0
|
||||
or
|
||||
name in ["DSA_generate_parameters", "RSA_generate_key", "DH_generate_parameters"] and
|
||||
sizeInd = 0 and
|
||||
outInd = -1
|
||||
) and
|
||||
exists(Type t |
|
||||
(
|
||||
if sizeInd = -1
|
||||
then t = func.getType().getUnderlyingType()
|
||||
else t = func.getParameter(sizeInd).getUnderlyingType()
|
||||
) and
|
||||
t instanceof IntegralType and
|
||||
not t instanceof CharType
|
||||
)
|
||||
}
|
||||
|
||||
module AsymExplicitAlgKeyLengthFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// Optimizations to avoid tracing all integers
|
||||
node.asExpr().(Literal).getValue().toInt() > 0 and // exclude sentinel values
|
||||
node.asExpr().(Literal).getValue().toInt() < 8500
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(FunctionCall c, int sizeInd |
|
||||
isAsymmetricKeyGenExplicitAlgorithm(c.getTarget(), sizeInd, _) and
|
||||
c.getArgument(sizeInd) = node.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module AsymExplicitAlgKeyLengthFlow = DataFlow::Global<AsymExplicitAlgKeyLengthFlowConfig>;
|
||||
|
||||
class OpenSSLAsymmetricKeyGenTiedToAlgorithm extends AsymmetricKeyGeneration {
|
||||
OpenSSLAsymmetricKeyGenTiedToAlgorithm() {
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
isAsymmetricKeyGenExplicitAlgorithm(c.getTarget(), _, _)
|
||||
)
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() { result = this }
|
||||
|
||||
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg) {
|
||||
alg = this and
|
||||
exists(int sizeInd |
|
||||
isAsymmetricKeyGenExplicitAlgorithm(this.getTarget(), sizeInd, _) and
|
||||
AsymExplicitAlgKeyLengthFlow::flow(DataFlow::exprNode(result),
|
||||
DataFlow::exprNode(this.getArgument(sizeInd)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Length_to_RSA_EVP_PKEY_Q_keygen_Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// Optimizations to avoid tracing all integers
|
||||
node.asExpr().(Literal).getValue().toInt() > 0 and // exclude sentinel values
|
||||
node.asExpr().(Literal).getValue().toInt() < 5000
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(FunctionCall c |
|
||||
c.getTarget().getName() = "EVP_PKEY_Q_keygen" and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
c.getArgument(3) = node.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Length_to_RSA_EVP_PKEY_Q_keygen_Flow =
|
||||
DataFlow::Global<Length_to_RSA_EVP_PKEY_Q_keygen_Config>;
|
||||
|
||||
class OpenSSL_RSA_EVP_PKEY_Q_keygen extends AsymmetricKeyGeneration {
|
||||
OpenSSL_RSA_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
this.getTarget().getName() = "EVP_PKEY_Q_keygen" and
|
||||
this.getArgument(3).getUnderlyingType() instanceof IntegralType
|
||||
)
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.configurationSink().(AlgorithmSinkArgument).getSinkCall() = this
|
||||
}
|
||||
|
||||
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg) {
|
||||
alg = this.getAlgorithm() and
|
||||
Length_to_RSA_EVP_PKEY_Q_keygen_Flow::flow(DataFlow::exprNode(result),
|
||||
DataFlow::exprNode(this.getArgument(3)))
|
||||
}
|
||||
}
|
||||
|
||||
predicate isKeyGenOperationWithNoSize(Function func) {
|
||||
isPossibleOpenSSLFunction(func) and
|
||||
exists(string name | func.hasGlobalName(name) |
|
||||
name in ["EVP_PKEY_keygen", "DSA_generate_key", "DH_generate_key", "EVP_PKEY_generate"]
|
||||
)
|
||||
}
|
||||
|
||||
module KeyGenKeySizeInitToKeyGenConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
exists(Call c, Function func, int outInd |
|
||||
isAsymmetricKeyGenExplicitAlgorithm(func, _, outInd) and
|
||||
c.getTarget() = func
|
||||
|
|
||||
if outInd = -1 then node.asExpr() = c else node.asExpr() = c.getArgument(outInd)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(Call c |
|
||||
isKeyGenOperationWithNoSize(c.getTarget()) and c.getAnArgument() = node.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module KeyGenKeySizeInitToKeyGenFlow = DataFlow::Global<KeyGenKeySizeInitToKeyGenConfig>;
|
||||
|
||||
predicate isEVP_PKEY_CTX_Source(DataFlow::Node node, CryptographicAlgorithm alg) {
|
||||
exists(Call c |
|
||||
alg.configurationSink().(AlgorithmSinkArgument).getSinkCall() = c and
|
||||
(
|
||||
node.asExpr() = c
|
||||
or
|
||||
node.asExpr() = c.getAnArgument()
|
||||
or
|
||||
node.asDefiningArgument() = c.getAnArgument()
|
||||
)
|
||||
) and
|
||||
(
|
||||
node.asExpr() instanceof Known_EVP_PKEY_CTX_Ptr_Source
|
||||
or
|
||||
node.asDefiningArgument() instanceof Known_EVP_PKEY_CTX_Ptr_Source
|
||||
)
|
||||
}
|
||||
|
||||
predicate isKeyGen_EVP_PKEY_CTX_Sink(DataFlow::Node node, Call c) {
|
||||
isKeyGenOperationWithNoSize(c.getTarget()) and nodeToExpr(node, c.getAnArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace from EVP_PKEY_CTX* at algorithm sink to keygen,
|
||||
* users can then extrapolatae the matching algorithm from the alg sink to the keygen
|
||||
*/
|
||||
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isEVP_PKEY_CTX_Source(source, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isKeyGen_EVP_PKEY_CTX_Sink(sink, _) }
|
||||
}
|
||||
|
||||
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize_Flow =
|
||||
DataFlow::Global<EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize>;
|
||||
|
||||
/**
|
||||
* UNKNOWN key sizes to general purpose key generation functions (i.e., that take in no key size and assume
|
||||
* is it set on context prior to the call). No path from a key configuration to these operations
|
||||
* means the key size is UNKNOWN, or more precisely the key size is DEFAULT but
|
||||
* the defaults can change with each version of OpenSSL, we simply assume the size is generally UNKNOWN.
|
||||
* ASSUMPTION/TODO: we currently model all known locations where a key size is set explicitly.
|
||||
* When a key is set implicitly, this usually means a key generation operation
|
||||
* is called where the operation takes in no key size, and no flow to this operation
|
||||
* initializes the context with a key size.
|
||||
* Currently, without a definitive source (set of sources) to start tracing from, we cannot determine
|
||||
* determine if a single path exists that initializes the context with a key size and another that doesn't.
|
||||
* Rather than attempt to model all possible sources, we assume that if no path
|
||||
* from a key config location reaches a generic key generation operation, then the key size is not set.
|
||||
* NOTE: while this is true, it is possible a key size is set in one path, but not in another
|
||||
* meaning this approach (and other similar approaches used in this model for UNKNOWN)
|
||||
* can produce false negatives.
|
||||
*/
|
||||
class OpenSSLDefaultKeyGeneration extends AsymmetricKeyGeneration {
|
||||
OpenSSLDefaultKeyGeneration() {
|
||||
// this is a call to a function matching isKeyGenOperationWithNoSize
|
||||
// and there is no flow from a key configuration source to this call
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isKeyGenOperationWithNoSize(this.getTarget()) and
|
||||
not exists(DataFlow::Node src, DataFlow::Node sink |
|
||||
KeyGenKeySizeInitToKeyGenFlow::flow(src, sink) and
|
||||
nodeToExpr(sink, this.getAnArgument())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
if this.getTarget().getName() in ["DSA_generate_key", "DH_generate_key"]
|
||||
then result = this
|
||||
else
|
||||
// NOTE/ASSUMPTION: EVP_PKEY_keygen, EVP_PKEY_generate assume only other possibilities,
|
||||
// each take in a CTX as the first arg, need to trace from an alg sink from this CTX param
|
||||
// get every alg sink, get the corresponding call, trace out on any CTX type variable
|
||||
// to the key gen
|
||||
// NOTE: looking for any cryptographic algorithm tracing to the keygen to handle
|
||||
// any odd cases we aren't awaare of where keygen can be used for other algorithm types
|
||||
exists(DataFlow::Node src, DataFlow::Node sink |
|
||||
EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize_Flow::flow(src, sink) and
|
||||
isEVP_PKEY_CTX_Source(src, result) and
|
||||
isKeyGen_EVP_PKEY_CTX_Sink(sink, this)
|
||||
// TODO: what if there is no CTX source? then the keygen becomes an UNKNOWN sink
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* For this class, there is no known configuration source for any algorithm
|
||||
*/
|
||||
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg) { none() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
* Predicates/classes for identifying algorithm sinks.
|
||||
* An Algorithm Sink is a function that takes an algorithm as an argument.
|
||||
* In particular, any function that takes in an algorithm that until the call
|
||||
* the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm)
|
||||
*/
|
||||
|
||||
//TODO: enforce a hierarchy of AlgorithmSinkArgument, e.g., so I can get all Asymmetric SinkArguments that includes all the strictly RSA etc.
|
||||
import cpp
|
||||
import experimental.cryptography.utils.OpenSSL.LibraryFunction
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
|
||||
predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType) { arg.algType() = algType }
|
||||
|
||||
abstract class AlgorithmSinkArgument extends Expr {
|
||||
AlgorithmSinkArgument() {
|
||||
exists(Call c | c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function call in which the argument exists
|
||||
*/
|
||||
Call getSinkCall() { result.getAnArgument() = this }
|
||||
|
||||
abstract string algType();
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html
|
||||
predicate cipherAlgorithmSink(string funcName, int argInd) {
|
||||
funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0
|
||||
or
|
||||
funcName = "EVP_CIPHER_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class CipherAlgorithmSink extends AlgorithmSinkArgument {
|
||||
CipherAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
cipherAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getSymmetricEncryptionType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch
|
||||
predicate macAlgorithmSink(string funcName, int argInd) {
|
||||
(funcName = "EVP_MAC_fetch" and argInd = 1)
|
||||
}
|
||||
|
||||
class MACAlgorithmSink extends AlgorithmSinkArgument {
|
||||
MACAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
macAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "TBD" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch
|
||||
predicate messageDigestAlgorithmSink(string funcName, int argInd) {
|
||||
funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0
|
||||
or
|
||||
funcName = "EVP_MD_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class MessageDigestAlgorithmSink extends AlgorithmSinkArgument {
|
||||
MessageDigestAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
messageDigestAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getHashType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch
|
||||
predicate keyExchangeAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_KEYEXCH_fetch" and argInd = 1
|
||||
or
|
||||
funcName = "EVP_KEM_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument {
|
||||
KeyExchangeAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
keyExchangeAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getKeyExchangeType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch
|
||||
predicate keyManagementAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_KEYMGMT_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class KeyManagementAlgorithmSink extends AlgorithmSinkArgument {
|
||||
KeyManagementAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
keyManagementAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "TBD" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KDF
|
||||
predicate keyDerivationAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_KDF_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument {
|
||||
KeyDerivationAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
keyDerivationAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getKeyDerivationType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html
|
||||
predicate asymmetricCipherAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1
|
||||
or
|
||||
funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3
|
||||
// NOTE: other cases are handled by AsymmetricAlgorithmSink
|
||||
}
|
||||
|
||||
class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument {
|
||||
AsymmetricCipherAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
asymmetricCipherAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
|
||||
}
|
||||
|
||||
class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
|
||||
AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c, string funcName |
|
||||
funcName = c.getTarget().getName() and
|
||||
this = c.getArgument(3)
|
||||
|
|
||||
funcName = "EVP_PKEY_Q_keygen" and
|
||||
c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch
|
||||
predicate randomAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_RAND_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class RandomAlgorithmSink extends AlgorithmSinkArgument {
|
||||
RandomAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
randomAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "TBD" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch
|
||||
predicate signatureAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_SIGNATURE_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class SignatureAlgorithmSink extends AlgorithmSinkArgument {
|
||||
SignatureAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
signatureAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getSignatureType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html
|
||||
predicate ellipticCurveAlgorithmSink(string funcName, int argInd) {
|
||||
funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0
|
||||
or
|
||||
funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2
|
||||
or
|
||||
funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1
|
||||
}
|
||||
|
||||
class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument {
|
||||
EllipticCurveAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
ellipticCurveAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getEllipticCurveType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Special cased to address the fact that arg index 3 (zero offset based) is the curve name.
|
||||
* ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve
|
||||
*/
|
||||
class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
|
||||
EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c, string funcName |
|
||||
funcName = c.getTarget().getName() and
|
||||
this = c.getArgument(3)
|
||||
|
|
||||
funcName = "EVP_PKEY_Q_keygen" and
|
||||
c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and
|
||||
c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getEllipticCurveType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html
|
||||
// https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
|
||||
predicate asymmetricAlgorithmSink(string funcName, int argInd) {
|
||||
funcName = "EVP_PKEY_CTX_new_id" and argInd = 0
|
||||
or
|
||||
funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1
|
||||
or
|
||||
funcName in [
|
||||
"EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key"
|
||||
] and
|
||||
argInd = 0
|
||||
or
|
||||
funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1
|
||||
or
|
||||
// special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs
|
||||
// funcName = "EVP_PKEY_Q_keygen" and argInd = 2
|
||||
funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1
|
||||
// TODO consider void cases EVP_PKEY_new
|
||||
}
|
||||
|
||||
class AsymmetricAlgorithmSink extends AlgorithmSinkArgument {
|
||||
AsymmetricAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd |
|
||||
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
||||
|
|
||||
asymmetricAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getAsymmetricType() }
|
||||
}
|
||||
|
||||
class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
|
||||
AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c, string funcName |
|
||||
funcName = c.getTarget().getName() and
|
||||
this = c.getArgument(2)
|
||||
|
|
||||
funcName = "EVP_PKEY_Q_keygen" and
|
||||
not exists(c.getArgument(3))
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getAsymmetricType() }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,121 @@
|
||||
import cpp
|
||||
import experimental.cryptography.utils.OpenSSL.LibraryFunction
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
|
||||
predicate inferredOpenSSLCryptoFunctionCall(Call c, string normalized, string algType) {
|
||||
inferredOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
|
||||
}
|
||||
|
||||
predicate inferredOpenSSLCryptoFunction(Function f, string normalized, string algType) {
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
normalizeFunctionName(f, algType) = normalized
|
||||
}
|
||||
|
||||
predicate isOpenSSLCryptoFunction(Function f, string normalized, string algType) {
|
||||
// NOTE: relying on inference as there are thousands of functions for crypto
|
||||
// enumerating them all and maintaining the list seems problematic.
|
||||
// For now, we will rely on dynamically inferring algorithms for function names.
|
||||
// This has been seen to be reasonably efficient and accurate.
|
||||
inferredOpenSSLCryptoFunction(f, normalized, algType)
|
||||
}
|
||||
|
||||
predicate isOpenSSLCryptoFunctionCall(Call c, string normalized, string algType) {
|
||||
isOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
|
||||
}
|
||||
|
||||
private string basicNormalizeFunctionName(Function f, string algType) {
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
isKnownAlgorithm(result, algType) and
|
||||
exists(string normStr | normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
|
||||
normStr.matches("%" + result + "%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
|
||||
*
|
||||
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
|
||||
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
|
||||
*
|
||||
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
|
||||
*
|
||||
* `f` is the function name to normalize.
|
||||
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
|
||||
*/
|
||||
private string privateNormalizeFunctionName(Function f, string algType) {
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
result = basicNormalizeFunctionName(f, algType) and
|
||||
not exists(string res2 |
|
||||
result != res2 and
|
||||
res2 = basicNormalizeFunctionName(f, algType) and
|
||||
res2.length() > result.length()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
|
||||
* A function is not, however, allowed to be UNKNOWN. The function either
|
||||
* normalizes to a known algorithm name, or the predicate does not hold (no result).
|
||||
*
|
||||
* The predicate attempts to restrict normalization to what looks like an openssl
|
||||
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
|
||||
* This may give false postive functions if a directory erronously appears to be openssl;
|
||||
* however, we take the stance that if a function
|
||||
* exists strongly mapping to a known function name in a directory such as these,
|
||||
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
|
||||
*/
|
||||
private string normalizeFunctionName(Function f, string algType) {
|
||||
algType != "UNKNOWN" and
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
result = privateNormalizeFunctionName(f, algType) and
|
||||
// Addressing false positives
|
||||
// For algorithm names less than or equal to 4, we must see the algorithm name
|
||||
// in the original function as upper case (it can't be split between tokens)
|
||||
// One exception found is DES_xcbc_encrypt, this is DESX
|
||||
(
|
||||
(result.length() <= 4 and result != "DESX")
|
||||
implies
|
||||
f.getName().toUpperCase().matches("%" + result + "%")
|
||||
) and
|
||||
(
|
||||
(result.length() <= 4 and result = "DESX")
|
||||
implies
|
||||
(f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%"))
|
||||
) and
|
||||
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
|
||||
// and
|
||||
// DES specific false positives
|
||||
(
|
||||
result.matches("DES")
|
||||
implies
|
||||
not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")
|
||||
) and
|
||||
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
|
||||
// f.getName().toUpperCase().matches("%" + s + "%"))) and
|
||||
// SEED specific false positives
|
||||
(
|
||||
result.matches("SEED")
|
||||
implies
|
||||
not exists(string s |
|
||||
s in [
|
||||
"SEED_SRC_GENERATE", "RAND", "NEW_SEED", "GEN_SEED", "SEED_GEN", "SET_SEED", "GET_SEED",
|
||||
"GET0_SEED", "RESEED", "SEEDING"
|
||||
]
|
||||
|
|
||||
f.getName().toUpperCase().matches("%" + s + "%")
|
||||
)
|
||||
) and
|
||||
// ARIA specific false positives
|
||||
(result.matches("ARIA") implies not f.getName().toUpperCase().matches("%VARIANT%")) and
|
||||
// CTR false positives
|
||||
(result.matches("CTR") implies not f.getName().toUpperCase().matches("%CTRL%")) and
|
||||
// ES false positives (e.g., ES256 from AES256)
|
||||
(result.matches("ES%") implies not f.getName().toUpperCase().matches("%AES%")) and
|
||||
// RSA false positives
|
||||
(result.matches("RSA") implies not f.getName().toUpperCase().matches("%UNIVERSAL%")) and
|
||||
//rsaz functions deemed to be too low level, and can be ignored
|
||||
not f.getLocation().getFile().getBaseName().matches("rsaz_exp.c") and
|
||||
// General False positives
|
||||
// Functions that 'get' do not set an algorithm, and therefore are considered ignorable
|
||||
not f.getName().toLowerCase().matches("%get%")
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* This file contains predicates create to build up initial data sets for OpenSSL
|
||||
* predicates. E.g., These predicates were used to assist in associating all
|
||||
* openSSL functions with their known crypto algorithms.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
import experimental.cryptography.utils.OpenSSL.CryptoFunction
|
||||
|
||||
private string basicNormalizeFunctionName(Function f, string algType) {
|
||||
isKnownAlgorithm(result, algType) and
|
||||
exists(string normStr | normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
|
||||
normStr.matches("%" + result + "%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
|
||||
*
|
||||
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
|
||||
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
|
||||
*
|
||||
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
|
||||
*
|
||||
* `f` is the function name to normalize.
|
||||
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
|
||||
*/
|
||||
private string privateNormalizeFunctionName(Function f, string algType) {
|
||||
result = basicNormalizeFunctionName(f, algType) and
|
||||
not exists(string res2 |
|
||||
result != res2 and
|
||||
res2 = basicNormalizeFunctionName(f, algType) and
|
||||
res2.length() > result.length()
|
||||
) and
|
||||
// Addressing bad normalization case-by-case
|
||||
// CASE: ES256 being identified when the algorithm is AES256
|
||||
(
|
||||
result.matches("ES256")
|
||||
implies
|
||||
not exists(string res2 | res2 = basicNormalizeFunctionName(f, _) and res2.matches("AES%"))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
|
||||
* A function is not, however, allowed to be UNKNOWN. The function either
|
||||
* normalizes to a known algorithm name, or the predicate does not hold (no result).
|
||||
*
|
||||
* The predicate attempts to restrict normalization to what looks like an openssl
|
||||
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
|
||||
* This may give false postive functions if a directory erronously appears to be openssl;
|
||||
* however, we take the stance that if a function
|
||||
* exists strongly mapping to a known function name in a directory such as these,
|
||||
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
|
||||
*/
|
||||
string normalizeFunctionName(Function f, string algType) {
|
||||
algType != "UNKNOWN" and
|
||||
result = privateNormalizeFunctionName(f, algType) and
|
||||
openSSLLibraryFunc(f) and
|
||||
// Addressing false positives
|
||||
// For algorithm names less than or equal to 4, we must see the algorithm name
|
||||
// in the original function as upper case (it can't be split between tokens)
|
||||
// One exception found is DES_xcbc_encrypt, this is DESX
|
||||
(
|
||||
(result.length() <= 4 and result != "DESX")
|
||||
implies
|
||||
f.getName().toUpperCase().matches("%" + result + "%")
|
||||
) and
|
||||
(
|
||||
(result.length() <= 4 and result = "DESX")
|
||||
implies
|
||||
(f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%"))
|
||||
) and
|
||||
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
|
||||
// and
|
||||
// DES specific false positives
|
||||
(
|
||||
result.matches("DES")
|
||||
implies
|
||||
not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")
|
||||
) and
|
||||
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
|
||||
// f.getName().toUpperCase().matches("%" + s + "%"))) and
|
||||
// SEED specific false positives
|
||||
(
|
||||
result.matches("%SEED%")
|
||||
implies
|
||||
not not exists(string s |
|
||||
s in ["NEW_SEED", "GEN_SEED", "SET_SEED", "GET_SEED", "GET0_SEED", "RESEED", "SEEDING"]
|
||||
|
|
||||
f.getName().toUpperCase().matches("%" + s + "%")
|
||||
)
|
||||
) and
|
||||
// ARIA specific false positives
|
||||
(result.matches("%ARIA%") implies not f.getName().toUpperCase().matches("%VARIANT%"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate to support name normalization.
|
||||
* Converts the raw name upper-case with no hyphen, slash, underscore, hash, or space.
|
||||
* Looks for substrings that are known algorithms, and normalizes the name.
|
||||
* If the algorithm cannot be determined or is in the ignorable list (`isIgnorableOpenSSLAlgorithm`)
|
||||
* this predicate will not resolve a name.
|
||||
*
|
||||
* Rationale for private: For normalization, we want to get the longest string for a normalized name match
|
||||
* for a given algorithm type. I found this easier to express if the public normalizeName
|
||||
* checks that the name is the longest, and that UNKNOWN is reserved if there exists no
|
||||
* result from this predicate that is known.
|
||||
*/
|
||||
bindingset[name]
|
||||
string privateNormalizeName(string name, string algType) {
|
||||
//not isIgnorableOpenSSLAlgorithm(name, _, _) and
|
||||
// targetOpenSSLAlgorithm(name, _) and
|
||||
isKnownAlgorithm(result, algType) and
|
||||
exists(string normStr | normStr = name.toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
|
||||
normStr.matches("%" + result + "%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
|
||||
*
|
||||
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
|
||||
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
|
||||
*
|
||||
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
|
||||
*
|
||||
* `name` is the name to normalize.
|
||||
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
|
||||
*/
|
||||
bindingset[name]
|
||||
string normalizeName(string name, string algType) {
|
||||
(
|
||||
if exists(privateNormalizeName(name, _))
|
||||
then result = privateNormalizeName(name, algType)
|
||||
else (
|
||||
result = unknownAlgorithm() and algType = "UNKNOWN"
|
||||
)
|
||||
) and
|
||||
not exists(string res2 |
|
||||
result != res2 and
|
||||
res2 = privateNormalizeName(name, algType) and
|
||||
res2.length() > result.length()
|
||||
) and
|
||||
// Addressing bad normalization case-by-case
|
||||
// CASE: ES256 being identified when the algorithm is AES256
|
||||
(
|
||||
result.matches("ES256")
|
||||
implies
|
||||
not exists(string res2 | res2 = privateNormalizeName(name, _) and res2.matches("AES%"))
|
||||
)
|
||||
}
|
||||
11292
cpp/ql/lib/experimental/cryptography/utils/OpenSSL/LibraryFunction.qll
Normal file
11292
cpp/ql/lib/experimental/cryptography/utils/OpenSSL/LibraryFunction.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
import cpp
|
||||
import experimental.cryptography.utils.OpenSSL.LibraryFunction
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
// TODO: possible use of extensible predicates here
|
||||
// NOTE: -1 for outInd represents the return value
|
||||
predicate knownPassthroughFunction(Function f, int inInd, int outInd) {
|
||||
// Trace through functions
|
||||
// See https://www.openssl.org/docs/man1.1.1/man3/OBJ_obj2txt
|
||||
// https://www.openssl.org/docs/man3.0/man3/EVP_CIPHER_get0_name
|
||||
openSSLLibraryFunc(f) and
|
||||
(
|
||||
f.getName() in [
|
||||
"OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn", "OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid",
|
||||
"OBJ_txt2nid", "OBJ_txt2obj", "OBJ_dup", "EVP_CIPHER_get0_name"
|
||||
] and
|
||||
inInd = 0 and
|
||||
outInd = -1
|
||||
or
|
||||
f.getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and
|
||||
inInd = 2 and
|
||||
outInd = 0
|
||||
or
|
||||
// Dup/copy pattern occurs in more places,
|
||||
//see: https://www.openssl.org/docs/manmaster/man3/EC_KEY_copy.html and https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_dup.html
|
||||
f.getName().matches("%_dup") and inInd = 0 and outInd = -1
|
||||
or
|
||||
f.getName().matches("%_copy") and inInd = 0 and outInd = -1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* `c` is a call to a function that preserves the algorithm but changes its form.
|
||||
* `onExpr` is the input argument passing through to, `outExpr` is the next expression in a dataflow step associated with `c`
|
||||
*/
|
||||
predicate knownPassthoughCall(Call c, Expr inExpr, Expr outExpr) {
|
||||
exists(int inInd, int outInd |
|
||||
knownPassthroughFunction(c.getTarget(), inInd, outInd) and
|
||||
inExpr = c.getArgument(inInd) and
|
||||
if outInd = -1 then outExpr = c else outExpr = c.getArgument(outInd)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Explicitly add flow through openssl functions that preserve the algorithm but alter the form (e.g., from NID to string)
|
||||
*/
|
||||
|
||||
predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(Expr cur, Expr next |
|
||||
(cur = node1.asExpr() or cur = node1.asIndirectArgument()) and
|
||||
(
|
||||
next = node2.asExpr() or
|
||||
next = node2.asIndirectArgument() or
|
||||
next = node2.asDefiningArgument()
|
||||
)
|
||||
|
|
||||
exists(Call c | knownPassthoughCall(c, cur, next))
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.9.2-dev
|
||||
version: 0.10.2-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -158,9 +158,7 @@ class NameQualifyingElement extends Element, @namequalifyingelement {
|
||||
/**
|
||||
* A special name-qualifying element. For example: `__super`.
|
||||
*/
|
||||
library class SpecialNameQualifyingElement extends NameQualifyingElement,
|
||||
@specialnamequalifyingelement
|
||||
{
|
||||
class SpecialNameQualifyingElement extends NameQualifyingElement, @specialnamequalifyingelement {
|
||||
/** Gets the name of this special qualifying element. */
|
||||
override string getName() { specialnamequalifyingelements(underlyingElement(this), result) }
|
||||
|
||||
|
||||
@@ -819,6 +819,30 @@ private predicate floatingPointTypeMapping(
|
||||
or
|
||||
// _Complex _Float16
|
||||
kind = 53 and base = 2 and domain = TComplexDomain() and realKind = 52 and extended = false
|
||||
or
|
||||
// __fp16
|
||||
kind = 54 and base = 2 and domain = TRealDomain() and realKind = 54 and extended = false
|
||||
or
|
||||
// __bf16
|
||||
kind = 55 and base = 2 and domain = TRealDomain() and realKind = 55 and extended = false
|
||||
or
|
||||
// std::float16_t
|
||||
kind = 56 and base = 2 and domain = TRealDomain() and realKind = 56 and extended = false
|
||||
or
|
||||
// _Complex _Float32
|
||||
kind = 57 and base = 2 and domain = TComplexDomain() and realKind = 45 and extended = false
|
||||
or
|
||||
// _Complex _Float32x
|
||||
kind = 58 and base = 2 and domain = TComplexDomain() and realKind = 46 and extended = true
|
||||
or
|
||||
// _Complex _Float64
|
||||
kind = 59 and base = 2 and domain = TComplexDomain() and realKind = 47 and extended = false
|
||||
or
|
||||
// _Complex _Float64x
|
||||
kind = 60 and base = 2 and domain = TComplexDomain() and realKind = 48 and extended = true
|
||||
or
|
||||
// _Complex _Float128
|
||||
kind = 61 and base = 2 and domain = TComplexDomain() and realKind = 49 and extended = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -73,6 +73,10 @@ private int isSource(Expr bufferExpr, Element why) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Same as `getBufferSize`, but with the `why` column projected away to prevent large duplications. */
|
||||
pragma[nomagic]
|
||||
int getBufferSizeProj(Expr bufferExpr) { result = getBufferSize(bufferExpr, _) }
|
||||
|
||||
/**
|
||||
* Get the size in bytes of the buffer pointed to by an expression (if this can be determined).
|
||||
*/
|
||||
@@ -87,7 +91,7 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
why = bufferVar and
|
||||
parentPtr = bufferExpr.(VariableAccess).getQualifier() and
|
||||
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
|
||||
result = getBufferSize(parentPtr, _) + bufferSize - parentClass.getSize()
|
||||
result = getBufferSizeProj(parentPtr) + bufferSize - parentClass.getSize()
|
||||
|
|
||||
if exists(bufferVar.getType().getSize())
|
||||
then bufferSize = bufferVar.getType().getSize()
|
||||
@@ -95,7 +99,6 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
)
|
||||
or
|
||||
// dataflow (all sources must be the same size)
|
||||
result = unique(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | getBufferSize(def, _)) and
|
||||
// find reason
|
||||
result = unique(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | getBufferSizeProj(def)) and
|
||||
exists(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | exists(getBufferSize(def, why)))
|
||||
}
|
||||
|
||||
@@ -27,9 +27,6 @@ predicate canValueFlow(Expr fromExpr, Expr toExpr) {
|
||||
fromExpr = toExpr.(ConditionalExpr).getElse()
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for AnalyzedString */
|
||||
deprecated class AnalysedString = AnalyzedString;
|
||||
|
||||
/**
|
||||
* An analyzed null terminated string.
|
||||
*/
|
||||
|
||||
@@ -78,7 +78,7 @@ predicate parameterUsePair(Parameter p, VariableAccess va) {
|
||||
/**
|
||||
* Utility class: A definition or use of a stack variable.
|
||||
*/
|
||||
library class DefOrUse extends ControlFlowNodeBase {
|
||||
class DefOrUse extends ControlFlowNodeBase {
|
||||
DefOrUse() {
|
||||
// Uninstantiated templates are purely syntax, and only on instantiation
|
||||
// will they be complete with information about types, conversions, call
|
||||
@@ -140,7 +140,7 @@ library class DefOrUse extends ControlFlowNodeBase {
|
||||
}
|
||||
|
||||
/** A definition of a stack variable. */
|
||||
library class Def extends DefOrUse {
|
||||
class Def extends DefOrUse {
|
||||
Def() { definition(_, this) }
|
||||
|
||||
override SemanticStackVariable getVariable(boolean isDef) {
|
||||
@@ -155,7 +155,7 @@ private predicate parameterIsOverwritten(Function f, Parameter p) {
|
||||
}
|
||||
|
||||
/** A definition of a parameter. */
|
||||
library class ParameterDef extends DefOrUse {
|
||||
class ParameterDef extends DefOrUse {
|
||||
ParameterDef() {
|
||||
// Optimization: parameters that are not overwritten do not require
|
||||
// reachability analysis
|
||||
@@ -169,7 +169,7 @@ library class ParameterDef extends DefOrUse {
|
||||
}
|
||||
|
||||
/** A use of a stack variable. */
|
||||
library class Use extends DefOrUse {
|
||||
class Use extends DefOrUse {
|
||||
Use() { useOfVar(_, this) }
|
||||
|
||||
override SemanticStackVariable getVariable(boolean isDef) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import SSAUtils
|
||||
* The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA.
|
||||
* This class provides the standard SSA logic.
|
||||
*/
|
||||
library class StandardSsa extends SsaHelper {
|
||||
class StandardSsa extends SsaHelper {
|
||||
StandardSsa() { this = 0 }
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ private predicate live_at_exit_of_bb(StackVariable v, BasicBlock b) {
|
||||
|
||||
/** Common SSA logic for standard SSA and range-analysis SSA. */
|
||||
cached
|
||||
library class SsaHelper extends int {
|
||||
class SsaHelper extends int {
|
||||
/* 0 = StandardSSA, 1 = RangeSSA */
|
||||
cached
|
||||
SsaHelper() { this in [0 .. 1] }
|
||||
|
||||
@@ -366,12 +366,12 @@ class CompileTimeConstantInt extends Expr {
|
||||
int getIntValue() { result = val }
|
||||
}
|
||||
|
||||
library class CompileTimeVariableExpr extends Expr {
|
||||
class CompileTimeVariableExpr extends Expr {
|
||||
CompileTimeVariableExpr() { not this instanceof CompileTimeConstantInt }
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of expressions. */
|
||||
library class ExprEvaluator extends int {
|
||||
class ExprEvaluator extends int {
|
||||
/*
|
||||
* 0 = ConditionEvaluator,
|
||||
* 1 = SwitchEvaluator,
|
||||
@@ -956,7 +956,7 @@ private predicate returnStmt(Function f, Expr value) {
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of conditions. */
|
||||
library class ConditionEvaluator extends ExprEvaluator {
|
||||
class ConditionEvaluator extends ExprEvaluator {
|
||||
ConditionEvaluator() { this = 0 }
|
||||
|
||||
override predicate interesting(Expr e) {
|
||||
@@ -967,7 +967,7 @@ library class ConditionEvaluator extends ExprEvaluator {
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of switch expressions. */
|
||||
library class SwitchEvaluator extends ExprEvaluator {
|
||||
class SwitchEvaluator extends ExprEvaluator {
|
||||
SwitchEvaluator() { this = 1 }
|
||||
|
||||
override predicate interesting(Expr e) { e = getASwitchExpr(_, _) }
|
||||
@@ -976,7 +976,7 @@ library class SwitchEvaluator extends ExprEvaluator {
|
||||
private int getSwitchValue(Expr e) { exists(SwitchEvaluator x | result = x.getValue(e)) }
|
||||
|
||||
/** A helper class for evaluation of loop entry conditions. */
|
||||
library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
LoopEntryConditionEvaluator() { this in [2 .. 3] }
|
||||
|
||||
abstract override predicate interesting(Expr e);
|
||||
@@ -1149,7 +1149,7 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of while-loop entry conditions. */
|
||||
library class WhileLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator {
|
||||
class WhileLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator {
|
||||
WhileLoopEntryConditionEvaluator() { this = 2 }
|
||||
|
||||
override predicate interesting(Expr e) { exists(WhileStmt while | e = while.getCondition()) }
|
||||
@@ -1162,7 +1162,7 @@ library class WhileLoopEntryConditionEvaluator extends LoopEntryConditionEvaluat
|
||||
}
|
||||
|
||||
/** A helper class for evaluation of for-loop entry conditions. */
|
||||
library class ForLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator {
|
||||
class ForLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator {
|
||||
ForLoopEntryConditionEvaluator() { this = 3 }
|
||||
|
||||
override predicate interesting(Expr e) { exists(ForStmt for | e = for.getCondition()) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -874,28 +874,3 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `BarrierGuard` module instead.
|
||||
*
|
||||
* A guard that validates some expression.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
* characteristic predicate precisely specifying the guard, and override
|
||||
* `checks` to specify what is being validated and in which branch.
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
deprecated class BarrierGuard extends GuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract predicate checks(Expr e, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final ExprNode getAGuardedNode() {
|
||||
exists(SsaDefinition def, Variable v, boolean branch |
|
||||
result.getExpr() = def.getAUse(v) and
|
||||
this.checks(def.getAUse(v), branch) and
|
||||
this.controls(result.getExpr().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -7,9 +7,12 @@ private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*
|
||||
* This predicate does not take additional call targets
|
||||
* from `AdditionalCallTarget` into account.
|
||||
*/
|
||||
cached
|
||||
DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
DataFlowCallable defaultViableCallable(DataFlowCall call) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
result = call.getStaticCallTarget()
|
||||
or
|
||||
@@ -29,6 +32,17 @@ DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*/
|
||||
cached
|
||||
DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
result = defaultViableCallable(call)
|
||||
or
|
||||
// Additional call targets
|
||||
result = any(AdditionalCallTarget additional).viableTarget(call.getUnconvertedResultExpression())
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides virtual dispatch support compatible with the original
|
||||
* implementation of `semmle.code.cpp.security.TaintTracking`.
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -555,7 +555,7 @@ predicate instructionForFullyConvertedCall(Instruction instr, CallInstruction ca
|
||||
}
|
||||
|
||||
/** Holds if `node` represents the output node for `call`. */
|
||||
private predicate simpleOutNode(Node node, CallInstruction call) {
|
||||
predicate simpleOutNode(Node node, CallInstruction call) {
|
||||
operandForFullyConvertedCall(node.asOperand(), call)
|
||||
or
|
||||
instructionForFullyConvertedCall(node.asInstruction(), call)
|
||||
|
||||
@@ -14,6 +14,7 @@ private import DataFlowPrivate
|
||||
private import ModelUtil
|
||||
private import SsaInternals as Ssa
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* The IR dataflow graph consists of the following nodes:
|
||||
@@ -1696,16 +1697,7 @@ private module Cached {
|
||||
// Reverse flow: data that flows from the definition node back into the indirection returned
|
||||
// by a function. This allows data to flow 'in' through references returned by a modeled
|
||||
// function such as `operator[]`.
|
||||
exists(Operand address, int indirectionIndex |
|
||||
nodeHasOperand(nodeTo.(IndirectReturnOutNode), address, indirectionIndex)
|
||||
|
|
||||
exists(StoreInstruction store |
|
||||
nodeHasInstruction(nodeFrom, store, indirectionIndex - 1) and
|
||||
store.getDestinationAddressOperand() = address
|
||||
)
|
||||
or
|
||||
Ssa::outNodeHasAddressAndIndex(nodeFrom, address, indirectionIndex)
|
||||
)
|
||||
reverseFlow(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
|
||||
@@ -1736,6 +1728,39 @@ private module Cached {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reverseFlow(Node nodeFrom, Node nodeTo) {
|
||||
reverseFlowOperand(nodeFrom, nodeTo)
|
||||
or
|
||||
reverseFlowInstruction(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
private predicate reverseFlowOperand(Node nodeFrom, IndirectReturnOutNode nodeTo) {
|
||||
exists(Operand address, int indirectionIndex |
|
||||
nodeHasOperand(nodeTo, address, indirectionIndex)
|
||||
|
|
||||
exists(StoreInstruction store |
|
||||
nodeHasInstruction(nodeFrom, store, indirectionIndex - 1) and
|
||||
store.getDestinationAddressOperand() = address
|
||||
)
|
||||
or
|
||||
// We also want a write coming out of an `OutNode` to flow `nodeTo`.
|
||||
// This is different from `reverseFlowInstruction` since `nodeFrom` can never
|
||||
// be an `OutNode` when it's defined by an instruction.
|
||||
Ssa::outNodeHasAddressAndIndex(nodeFrom, address, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reverseFlowInstruction(Node nodeFrom, IndirectReturnOutNode nodeTo) {
|
||||
exists(Instruction address, int indirectionIndex |
|
||||
nodeHasInstruction(nodeTo, address, indirectionIndex)
|
||||
|
|
||||
exists(StoreInstruction store |
|
||||
nodeHasInstruction(nodeFrom, store, indirectionIndex - 1) and
|
||||
store.getDestinationAddress() = address
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -2215,33 +2240,41 @@ module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardCheck
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `BarrierGuard` module instead.
|
||||
* A unit class for adding additional call steps.
|
||||
*
|
||||
* A guard that validates some instruction.
|
||||
* Extend this class to add additional call steps to the data flow graph.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
* characteristic predicate precisely specifying the guard, and override
|
||||
* `checks` to specify what is being validated and in which branch.
|
||||
* For example, if the following subclass is added:
|
||||
* ```ql
|
||||
* class MyAdditionalCallTarget extends DataFlow::AdditionalCallTarget {
|
||||
* override Function viableTarget(Call call) {
|
||||
* call.getTarget().hasName("f") and
|
||||
* result.hasName("g")
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* then flow from `source()` to `x` in `sink(x)` is reported in the following example:
|
||||
* ```cpp
|
||||
* void sink(int);
|
||||
* int source();
|
||||
* void f(int);
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
* void g(int x) {
|
||||
* sink(x);
|
||||
* }
|
||||
*
|
||||
* void test() {
|
||||
* int x = source();
|
||||
* f(x);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note: To prevent reevaluation of cached dataflow-related predicates any
|
||||
* subclass of `AdditionalCallTarget` must be imported in all dataflow queries.
|
||||
*/
|
||||
deprecated class BarrierGuard extends IRGuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
|
||||
predicate checksInstr(Instruction instr, boolean b) { none() }
|
||||
|
||||
/** Override this predicate to hold if this guard validates `expr` upon evaluating to `b`. */
|
||||
predicate checks(Expr e, boolean b) { none() }
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final Node getAGuardedNode() {
|
||||
exists(ValueNumber value, boolean edge |
|
||||
(
|
||||
this.checksInstr(value.getAnInstruction(), edge)
|
||||
or
|
||||
this.checks(value.getAnInstruction().getConvertedResultExpression(), edge)
|
||||
) and
|
||||
result.asInstruction() = value.getAnInstruction() and
|
||||
this.controls(result.asInstruction().getBlock(), edge)
|
||||
)
|
||||
}
|
||||
class AdditionalCallTarget extends Unit {
|
||||
/**
|
||||
* Gets a viable target for `call`.
|
||||
*/
|
||||
abstract DataFlowCallable viableTarget(Call call);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
/**
|
||||
@@ -35,7 +36,7 @@ DataFlow::Node callInput(CallInstruction call, FunctionInput input) {
|
||||
*/
|
||||
Node callOutput(CallInstruction call, FunctionOutput output) {
|
||||
// The return value
|
||||
result.asInstruction() = call and
|
||||
simpleOutNode(result, call) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
@@ -82,7 +83,7 @@ Node callOutput(CallInstruction call, FunctionOutput output, int d) {
|
||||
// If there isn't an indirect out node for the call with indirection `d` then
|
||||
// we conflate this with the underlying `CallInstruction`.
|
||||
not exists(getIndirectReturnOutNode(call, d)) and
|
||||
n.asInstruction() = result.asInstruction()
|
||||
n = result
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
|
||||
@@ -374,6 +374,8 @@ module ProductFlow {
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) { Config::isBarrier1(node, state) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { Config::isBarrier1(node) }
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) { Config::isBarrierOut1(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
@@ -408,6 +410,8 @@ module ProductFlow {
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) { Config::isBarrier2(node, state) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { Config::isBarrier2(node) }
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) { Config::isBarrierOut2(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
|
||||
@@ -645,12 +645,24 @@ private predicate adjustForPointerArith(PostUpdateNode pun, UseOrPhi use) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` flows to `nodeTo` because there is `def-use` or
|
||||
* `use-use` flow from `defOrUse` to `use`.
|
||||
*
|
||||
* `uncertain` is `true` if the `defOrUse` is an uncertain definition.
|
||||
*/
|
||||
private predicate localSsaFlow(
|
||||
SsaDefOrUse defOrUse, Node nodeFrom, UseOrPhi use, Node nodeTo, boolean uncertain
|
||||
) {
|
||||
nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and
|
||||
adjacentDefRead(defOrUse, use) and
|
||||
useToNode(use, nodeTo) and
|
||||
nodeFrom != nodeTo
|
||||
}
|
||||
|
||||
private predicate ssaFlowImpl(SsaDefOrUse defOrUse, Node nodeFrom, Node nodeTo, boolean uncertain) {
|
||||
exists(UseOrPhi use |
|
||||
nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and
|
||||
adjacentDefRead(defOrUse, use) and
|
||||
useToNode(use, nodeTo) and
|
||||
nodeFrom != nodeTo
|
||||
localSsaFlow(defOrUse, nodeFrom, use, nodeTo, uncertain)
|
||||
or
|
||||
// Initial global variable value to a first use
|
||||
nodeFrom.(InitialGlobalValue).getGlobalDef() = defOrUse and
|
||||
@@ -728,15 +740,62 @@ private predicate isArgumentOfCallable(DataFlowCall call, Node n) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if there is def-use or use-use flow from `pun` to `nodeTo`. */
|
||||
predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
|
||||
exists(UseOrPhi use, Node preUpdate |
|
||||
/**
|
||||
* Holds if there is use-use flow from `pun`'s pre-update node to `n`.
|
||||
*/
|
||||
private predicate postUpdateNodeToFirstUse(PostUpdateNode pun, Node n) {
|
||||
exists(UseOrPhi use |
|
||||
adjustForPointerArith(pun, use) and
|
||||
useToNode(use, nodeTo) and
|
||||
useToNode(use, n)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stepUntilNotInCall(DataFlowCall call, Node n1, Node n2) {
|
||||
isArgumentOfCallable(call, n1) and
|
||||
exists(Node mid | localSsaFlow(_, n1, _, mid, _) |
|
||||
isArgumentOfCallable(call, mid) and
|
||||
stepUntilNotInCall(call, mid, n2)
|
||||
or
|
||||
not isArgumentOfCallable(call, mid) and
|
||||
mid = n2
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[n1, n2]
|
||||
pragma[inline_late]
|
||||
private predicate isArgumentOfSameCall(DataFlowCall call, Node n1, Node n2) {
|
||||
isArgumentOfCallable(call, n1) and isArgumentOfCallable(call, n2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is def-use or use-use flow from `pun` to `nodeTo`.
|
||||
*
|
||||
* Note: This is more complex than it sounds. Consider a call such as:
|
||||
* ```cpp
|
||||
* write_first_argument(x, x);
|
||||
* sink(x);
|
||||
* ```
|
||||
* Assume flow comes out of the first argument to `write_first_argument`. We
|
||||
* don't want flow to go to the `x` that's also an argument to
|
||||
* `write_first_argument` (because we just flowed out of that function, and we
|
||||
* don't want to flow back into it again).
|
||||
*
|
||||
* We do, however, want flow from the output argument to `x` on the next line, and
|
||||
* similarly we want flow from the second argument of `write_first_argument` to `x`
|
||||
* on the next line.
|
||||
*/
|
||||
predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
|
||||
exists(Node preUpdate, Node mid |
|
||||
preUpdate = pun.getPreUpdateNode() and
|
||||
not exists(DataFlowCall call |
|
||||
isArgumentOfCallable(call, preUpdate) and isArgumentOfCallable(call, nodeTo)
|
||||
postUpdateNodeToFirstUse(pun, mid)
|
||||
|
|
||||
exists(DataFlowCall call |
|
||||
isArgumentOfSameCall(call, preUpdate, mid) and
|
||||
stepUntilNotInCall(call, mid, nodeTo)
|
||||
)
|
||||
or
|
||||
not isArgumentOfSameCall(_, preUpdate, mid) and
|
||||
nodeTo = mid
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -815,7 +815,7 @@ private module Cached {
|
||||
) {
|
||||
indirectionIndex = [1 .. countIndirectionsForCppType(getResultLanguageType(instr))] and
|
||||
exists(Instruction load, Operand address |
|
||||
address.getDef() = instr and
|
||||
address = unique( | | getAUse(instr)) and
|
||||
isDereference(load, address, false) and
|
||||
instrRepr = load and
|
||||
indirectionIndexRepr = indirectionIndex - 1
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,22 @@ private import internal.IRBlockImports as Imports
|
||||
import Imports::EdgeKind
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
|
||||
* sort keys of the block (derived from its first instruction)
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate blockSortKeys(
|
||||
IRFunction func, IRBlockBase block, int sortOverride, int sortKey1, int sortKey2
|
||||
) {
|
||||
block.getEnclosingIRFunction() = func and
|
||||
block.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if block.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
@@ -37,17 +53,14 @@ class IRBlockBase extends TIRBlock {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
exists(IRFunction func |
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
blockSortKeys(func, funcBlock, sortOverride, sortKey1, sortKey2)
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,14 +116,14 @@ class Instruction extends Construction::TStageInstruction {
|
||||
|
||||
private int getLineRank() {
|
||||
this.shouldGenerateDumpStrings() and
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr =
|
||||
getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(),
|
||||
this.getLocation().getStartLine())
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
exists(IRFunction enclosing, Language::File file, int line |
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr = getAnInstructionAtLine(enclosing, file, line)
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,22 @@ private import internal.IRBlockImports as Imports
|
||||
import Imports::EdgeKind
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
|
||||
* sort keys of the block (derived from its first instruction)
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate blockSortKeys(
|
||||
IRFunction func, IRBlockBase block, int sortOverride, int sortKey1, int sortKey2
|
||||
) {
|
||||
block.getEnclosingIRFunction() = func and
|
||||
block.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if block.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
@@ -37,17 +53,14 @@ class IRBlockBase extends TIRBlock {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
exists(IRFunction func |
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
blockSortKeys(func, funcBlock, sortOverride, sortKey1, sortKey2)
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,14 +116,14 @@ class Instruction extends Construction::TStageInstruction {
|
||||
|
||||
private int getLineRank() {
|
||||
this.shouldGenerateDumpStrings() and
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr =
|
||||
getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(),
|
||||
this.getLocation().getStartLine())
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
exists(IRFunction enclosing, Language::File file, int line |
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr = getAnInstructionAtLine(enclosing, file, line)
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -423,7 +423,12 @@ private module CachedForDebugging {
|
||||
cached
|
||||
predicate instructionHasSortKeys(Instruction instruction, int key1, int key2) {
|
||||
key1 = getInstructionTranslatedElement(instruction).getId() and
|
||||
getInstructionTag(instruction) =
|
||||
getInstructionTag(instruction) = tagByRank(key2)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private InstructionTag tagByRank(int key2) {
|
||||
result =
|
||||
rank[key2](InstructionTag tag, string tagId |
|
||||
tagId = getInstructionTagId(tag)
|
||||
|
|
||||
|
||||
@@ -361,6 +361,12 @@ predicate ignoreLoad(Expr expr) {
|
||||
or
|
||||
expr instanceof FunctionAccess
|
||||
or
|
||||
// The load is duplicated from the operand.
|
||||
expr instanceof ParenthesisExpr
|
||||
or
|
||||
// The load is duplicated from the right operand.
|
||||
expr instanceof CommaExpr
|
||||
or
|
||||
expr.(PointerDereferenceExpr).getOperand().getFullyConverted().getType().getUnspecifiedType()
|
||||
instanceof FunctionPointerType
|
||||
or
|
||||
|
||||
@@ -648,19 +648,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
|
||||
class TranslatedPrefixCrementOperation extends TranslatedCrementOperation {
|
||||
override PrefixCrementOperation expr;
|
||||
|
||||
override Instruction getResult() {
|
||||
if expr.isPRValueCategory()
|
||||
then
|
||||
// If this is C, then the result of a prefix crement is a prvalue for the
|
||||
// new value assigned to the operand. If this is C++, then the result is
|
||||
// an lvalue, but that lvalue is being loaded as part of this expression.
|
||||
// EDG doesn't mark this as a load.
|
||||
result = this.getInstruction(CrementOpTag())
|
||||
else
|
||||
// This is C++, where the result is an lvalue for the operand, and that
|
||||
// lvalue is not being loaded as part of this expression.
|
||||
result = this.getUnloadedOperand().getResult()
|
||||
}
|
||||
override Instruction getResult() { result = this.getUnloadedOperand().getResult() }
|
||||
}
|
||||
|
||||
class TranslatedPostfixCrementOperation extends TranslatedCrementOperation {
|
||||
@@ -1503,19 +1491,7 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr {
|
||||
result = this.getRightOperand().getFirstInstruction()
|
||||
}
|
||||
|
||||
final override Instruction getResult() {
|
||||
if expr.isPRValueCategory()
|
||||
then
|
||||
// If this is C, then the result of an assignment is a prvalue for the new
|
||||
// value assigned to the left operand. If this is C++, then the result is
|
||||
// an lvalue, but that lvalue is being loaded as part of this expression.
|
||||
// EDG doesn't mark this as a load.
|
||||
result = this.getRightOperand().getResult()
|
||||
else
|
||||
// This is C++, where the result is an lvalue for the left operand,
|
||||
// and that lvalue is not being loaded as part of this expression.
|
||||
result = this.getLeftOperand().getResult()
|
||||
}
|
||||
final override Instruction getResult() { result = this.getLeftOperand().getResult() }
|
||||
|
||||
final TranslatedExpr getLeftOperand() {
|
||||
result = getTranslatedExpr(expr.getLValue().getFullyConverted())
|
||||
@@ -1641,19 +1617,7 @@ class TranslatedAssignOperation extends TranslatedNonConstantExpr {
|
||||
result = this.getRightOperand().getFirstInstruction()
|
||||
}
|
||||
|
||||
final override Instruction getResult() {
|
||||
if expr.isPRValueCategory()
|
||||
then
|
||||
// If this is C, then the result of an assignment is a prvalue for the new
|
||||
// value assigned to the left operand. If this is C++, then the result is
|
||||
// an lvalue, but that lvalue is being loaded as part of this expression.
|
||||
// EDG doesn't mark this as a load.
|
||||
result = this.getStoredValue()
|
||||
else
|
||||
// This is C++, where the result is an lvalue for the left operand,
|
||||
// and that lvalue is not being loaded as part of this expression.
|
||||
result = this.getUnloadedLeftOperand().getResult()
|
||||
}
|
||||
final override Instruction getResult() { result = this.getUnloadedLeftOperand().getResult() }
|
||||
|
||||
final TranslatedExpr getUnloadedLeftOperand() {
|
||||
result = this.getLoadedLeftOperand().getOperand()
|
||||
@@ -2191,8 +2155,15 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
|
||||
not this.elseIsVoid() and tag = ConditionValueFalseStoreTag()
|
||||
) and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = this.getResultType()
|
||||
(
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
resultType = this.getResultType()
|
||||
or
|
||||
expr.hasLValueToRValueConversion() and
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
)
|
||||
or
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = this.getResultType()
|
||||
@@ -2222,8 +2193,15 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
|
||||
)
|
||||
or
|
||||
tag = ConditionValueResultTempAddressTag() and
|
||||
result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
(
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
or
|
||||
expr.hasLValueToRValueConversion() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
)
|
||||
or
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
)
|
||||
@@ -2252,18 +2230,23 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
|
||||
result = this.getElse().getResult()
|
||||
)
|
||||
or
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(ConditionValueResultTempAddressTag())
|
||||
)
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(ConditionValueResultTempAddressTag())
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
not this.resultIsVoid() and
|
||||
tag = ConditionValueTempVar() and
|
||||
type = this.getResultType()
|
||||
(
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
type = this.getResultType()
|
||||
or
|
||||
expr.hasLValueToRValueConversion() and
|
||||
type = getTypeForPRValue(expr.getType())
|
||||
)
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
@@ -2278,7 +2261,13 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
|
||||
|
||||
final override Instruction getResult() {
|
||||
not this.resultIsVoid() and
|
||||
result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
(
|
||||
expr.hasLValueToRValueConversion() and
|
||||
result = this.getInstruction(ConditionValueResultTempAddressTag())
|
||||
or
|
||||
not expr.hasLValueToRValueConversion() and
|
||||
result = this.getInstruction(ConditionValueResultLoadTag())
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
@@ -3237,11 +3226,9 @@ predicate exprNeedsCopyIfNotLoaded(Expr expr) {
|
||||
(
|
||||
expr instanceof AssignExpr
|
||||
or
|
||||
expr instanceof AssignOperation and
|
||||
not expr.isPRValueCategory() // is C++
|
||||
expr instanceof AssignOperation
|
||||
or
|
||||
expr instanceof PrefixCrementOperation and
|
||||
not expr.isPRValueCategory() // is C++
|
||||
expr instanceof PrefixCrementOperation
|
||||
or
|
||||
// Because the load is on the `e` in `e++`.
|
||||
expr instanceof PostfixCrementOperation
|
||||
|
||||
@@ -8,6 +8,22 @@ private import internal.IRBlockImports as Imports
|
||||
import Imports::EdgeKind
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
|
||||
* sort keys of the block (derived from its first instruction)
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate blockSortKeys(
|
||||
IRFunction func, IRBlockBase block, int sortOverride, int sortKey1, int sortKey2
|
||||
) {
|
||||
block.getEnclosingIRFunction() = func and
|
||||
block.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if block.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
@@ -37,17 +53,14 @@ class IRBlockBase extends TIRBlock {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
exists(IRFunction func |
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
blockSortKeys(func, funcBlock, sortOverride, sortKey1, sortKey2)
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,14 +116,14 @@ class Instruction extends Construction::TStageInstruction {
|
||||
|
||||
private int getLineRank() {
|
||||
this.shouldGenerateDumpStrings() and
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr =
|
||||
getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(),
|
||||
this.getLocation().getStartLine())
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
exists(IRFunction enclosing, Language::File file, int line |
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr = getAnInstructionAtLine(enclosing, file, line)
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,7 @@ private import RangeAnalysisUtils
|
||||
* The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA.
|
||||
* This class provides the range-analysis SSA logic.
|
||||
*/
|
||||
library class RangeSsa extends SsaHelper {
|
||||
class RangeSsa extends SsaHelper {
|
||||
RangeSsa() { this = 1 }
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,17 +60,31 @@ private import semmle.code.cpp.rangeanalysis.new.RangeAnalysisUtil
|
||||
|
||||
private VariableAccess getAVariableAccess(Expr e) { e.getAChild*() = result }
|
||||
|
||||
/**
|
||||
* Gets a (sub)expression that may be the result of evaluating `size`.
|
||||
*
|
||||
* For example, `getASizeCandidate(a ? b : c)` gives `a ? b : c`, `b` and `c`.
|
||||
*/
|
||||
bindingset[size]
|
||||
pragma[inline_late]
|
||||
private Expr getASizeCandidate(Expr size) {
|
||||
result = size
|
||||
or
|
||||
result = [size.(ConditionalExpr).getThen(), size.(ConditionalExpr).getElse()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `(n, state)` pair represents the source of flow for the size
|
||||
* expression associated with `alloc`.
|
||||
*/
|
||||
predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
|
||||
exists(VariableAccess va, Expr size, int delta |
|
||||
exists(VariableAccess va, Expr size, int delta, Expr s |
|
||||
size = alloc.getSizeExpr() and
|
||||
s = getASizeCandidate(size) and
|
||||
// Get the unique variable in a size expression like `x` in `malloc(x + 1)`.
|
||||
va = unique( | | getAVariableAccess(size)) and
|
||||
va = unique( | | getAVariableAccess(s)) and
|
||||
// Compute `delta` as the constant difference between `x` and `x + 1`.
|
||||
bounded1(any(Instruction instr | instr.getUnconvertedResultExpression() = size),
|
||||
bounded1(any(Instruction instr | instr.getUnconvertedResultExpression() = s),
|
||||
any(LoadInstruction load | load.getUnconvertedResultExpression() = va), delta) and
|
||||
n.asExpr() = va and
|
||||
state = delta
|
||||
@@ -284,10 +298,33 @@ private module Config implements ProductFlow::StateConfigSig {
|
||||
pointerAddInstructionHasBounds0(_, allocSink, sizeSink, sizeAddend)
|
||||
}
|
||||
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
predicate isBarrier2(DataFlow::Node node, FlowState2 state) {
|
||||
node = SizeBarrier::getABarrierNode(state)
|
||||
}
|
||||
|
||||
predicate isBarrier2(DataFlow::Node node) {
|
||||
// Block flow from `*p` to `*(p + n)` when `n` is not `0`. This removes
|
||||
// false positives
|
||||
// when tracking the size of the allocation as an element of an array such
|
||||
// as:
|
||||
// ```
|
||||
// size_t* p = new size_t[n];
|
||||
// ...
|
||||
// p[0] = n;
|
||||
// int i = p[1];
|
||||
// p[i] = ...
|
||||
// ```
|
||||
// In the above case, this barrier blocks flow from the indirect node
|
||||
// for `p` to `p[1]`.
|
||||
exists(Operand operand, PointerAddInstruction add |
|
||||
node.(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and
|
||||
add.getLeftOperand() = operand and
|
||||
add.getRight().(ConstantInstruction).getValue() != "0"
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrierIn1(DataFlow::Node node) { isSourcePair(node, _, _, _) }
|
||||
|
||||
predicate isBarrierOut2(DataFlow::Node node) {
|
||||
|
||||
@@ -612,6 +612,14 @@ case @builtintype.kind of
|
||||
| 51 = @char8_t
|
||||
| 52 = @float16 // _Float16
|
||||
| 53 = @complex_float16 // _Complex _Float16
|
||||
| 54 = @fp16 // __fp16
|
||||
| 55 = @std_bfloat16 // __bf16
|
||||
| 56 = @std_float16 // std::float16_t
|
||||
| 57 = @complex_std_float32 // _Complex _Float32
|
||||
| 58 = @complex_float32x // _Complex _Float32x
|
||||
| 59 = @complex_std_float64 // _Complex _Float64
|
||||
| 60 = @complex_float64x // _Complex _Float64x
|
||||
| 61 = @complex_std_float128 // _Complex _Float128
|
||||
;
|
||||
|
||||
builtintypes(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Introduce new floating-point types from C23 and C++23
|
||||
compatibility: full
|
||||
@@ -1,3 +1,40 @@
|
||||
## 0.8.1
|
||||
|
||||
### New Queries
|
||||
|
||||
* The query `cpp/redundant-null-check-simple` has been promoted to Code Scanning. The query finds cases where a pointer is compared to null after it has already been dereferenced. Such comparisons likely indicate a bug at the place where the pointer is dereferenced, or where the pointer is compared to null.
|
||||
|
||||
Note: This query was incorrectly noted as being promoted to Code Scanning in CodeQL version 2.14.6.
|
||||
|
||||
## 0.8.0
|
||||
|
||||
### Query Metadata Changes
|
||||
|
||||
* The `cpp/double-free` query has been further improved to reduce false positives and its precision has been increased from `medium` to `high`.
|
||||
* The `cpp/use-after-free` query has been further improved to reduce false positives and its precision has been increased from `medium` to `high`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The queries `cpp/double-free` and `cpp/use-after-free` find fewer false positives
|
||||
in cases where a non-returning function is called.
|
||||
* The number of duplicated dataflow paths reported by queries has been significantly reduced.
|
||||
|
||||
## 0.7.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.7.4
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `cpp/invalid-pointer-deref`, to detect out-of-bounds pointer reads and writes.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Comparison where assignment was intended" query (`cpp/compare-where-assign-meant`) no longer reports comparisons that appear in macro expansions.
|
||||
* Some queries that had repeated results corresponding to different levels of indirection for `argv` now only have a single result.
|
||||
* The `cpp/non-constant-format` query no longer considers an assignment on the right-hand side of another assignment to be a source of non-constant format strings. As a result, the query may now produce fewer results.
|
||||
|
||||
## 0.7.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Potential double free
|
||||
* @description Freeing a resource more than once can lead to undefined behavior and cause memory corruption.
|
||||
* @kind path-problem
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @id cpp/double-free
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.3
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Potential use after free
|
||||
* @description An allocated memory block is used after it has been freed. Behavior in such cases is undefined and can cause memory corruption.
|
||||
* @kind path-problem
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @id cpp/use-after-free
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.3
|
||||
@@ -29,8 +29,7 @@ private predicate externalCallNeverDereferences(FormattingFunctionCall call, int
|
||||
)
|
||||
}
|
||||
|
||||
predicate isUse0(DataFlow::Node n, Expr e) {
|
||||
e = n.asExpr() and
|
||||
predicate isUse0(Expr e) {
|
||||
not isFree(_, e, _) and
|
||||
(
|
||||
e = any(PointerDereferenceExpr pde).getOperand()
|
||||
@@ -43,7 +42,7 @@ predicate isUse0(DataFlow::Node n, Expr e) {
|
||||
or
|
||||
// Assume any function without a body will dereference the pointer
|
||||
exists(int i, Call call, Function f |
|
||||
n.asExpr() = call.getArgument(i) and
|
||||
e = call.getArgument(i) and
|
||||
f = call.getTarget() and
|
||||
not f.hasEntryPoint() and
|
||||
// Exclude known functions we know won't dereference the pointer.
|
||||
@@ -57,7 +56,7 @@ module ParameterSinks {
|
||||
import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
predicate flowsToUse(DataFlow::Node n) {
|
||||
isUse0(n, _)
|
||||
isUse0(n.asExpr())
|
||||
or
|
||||
exists(DataFlow::Node succ |
|
||||
flowsToUse(succ) and
|
||||
@@ -90,7 +89,7 @@ module ParameterSinks {
|
||||
) {
|
||||
pragma[only_bind_out](source.asParameter()) = pragma[only_bind_out](init.getParameter()) and
|
||||
paramToUse(source, sink) and
|
||||
isUse0(sink, _)
|
||||
isUse0(sink.asExpr())
|
||||
}
|
||||
|
||||
private InitializeParameterInstruction getAnAlwaysDereferencedParameter0() {
|
||||
@@ -139,7 +138,7 @@ module IsUse {
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
|
||||
|
||||
predicate isUse(DataFlow::Node n, Expr e) {
|
||||
isUse0(n, e)
|
||||
isUse0(e) and n.asExpr() = e
|
||||
or
|
||||
exists(CallInstruction call, InitializeParameterInstruction init |
|
||||
n.asOperand().getDef().getUnconvertedResultExpression() = e and
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
* it should be moved before the dereference.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id cpp/redundant-null-check-simple
|
||||
* @tags reliability
|
||||
* correctness
|
||||
* security
|
||||
* external/cwe/cwe-476
|
||||
*/
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ private predicate annotatesAtPosition(SalPosition pos, DeclarationEntry d, File
|
||||
* A SAL element, that is, a SAL annotation or a declaration entry
|
||||
* that may have SAL annotations.
|
||||
*/
|
||||
library class SalElement extends Element {
|
||||
class SalElement extends Element {
|
||||
SalElement() {
|
||||
containsSalAnnotation(this.(DeclarationEntry).getFile()) or
|
||||
this instanceof SalAnnotation
|
||||
|
||||
@@ -13,15 +13,13 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Environment
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.ir.IR
|
||||
import Flow::PathGraph
|
||||
|
||||
/** A call that prints its arguments to `stdout`. */
|
||||
class PrintStdoutCall extends FunctionCall {
|
||||
PrintStdoutCall() {
|
||||
this.getTarget().hasGlobalOrStdName("puts") or
|
||||
this.getTarget().hasGlobalOrStdName("printf")
|
||||
}
|
||||
PrintStdoutCall() { this.getTarget().hasGlobalOrStdName(["puts", "printf"]) }
|
||||
}
|
||||
|
||||
/** A read of the QUERY_STRING environment variable */
|
||||
@@ -29,19 +27,25 @@ class QueryString extends EnvironmentRead {
|
||||
QueryString() { this.getEnvironmentVariable() = "QUERY_STRING" }
|
||||
}
|
||||
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSource(Expr source) { source instanceof QueryString }
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node.asIndirectExpr() instanceof QueryString }
|
||||
|
||||
override predicate isSink(Element tainted) {
|
||||
exists(PrintStdoutCall call | call.getAnArgument() = tainted)
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(PrintStdoutCall call | call.getAnArgument() = [node.asIndirectExpr(), node.asExpr()])
|
||||
}
|
||||
|
||||
override predicate isBarrier(Expr e) {
|
||||
super.isBarrier(e) or e.getUnspecifiedType() instanceof IntegralType
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
isSink(node) and node.asExpr().getUnspecifiedType() instanceof ArithmeticType
|
||||
or
|
||||
node.asInstruction().(StoreInstruction).getResultType() instanceof ArithmeticType
|
||||
}
|
||||
}
|
||||
|
||||
from QueryString query, Element printedArg, PathNode sourceNode, PathNode sinkNode
|
||||
where taintedWithPath(query, printedArg, sourceNode, sinkNode)
|
||||
select printedArg, sourceNode, sinkNode, "Cross-site scripting vulnerability due to $@.", query,
|
||||
"this query data"
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
from QueryString query, Flow::PathNode sourceNode, Flow::PathNode sinkNode
|
||||
where
|
||||
Flow::flowPath(sourceNode, sinkNode) and
|
||||
query = sourceNode.getNode().asIndirectExpr()
|
||||
select sinkNode.getNode(), sourceNode, sinkNode, "Cross-site scripting vulnerability due to $@.",
|
||||
query, "this query data"
|
||||
|
||||
@@ -82,36 +82,20 @@ module ValidState {
|
||||
* library will perform, and visit all the places where the size argument is modified.
|
||||
* 2. Once that dataflow traversal is done, we accumulate the offsets added at each places
|
||||
* where the offset is modified (see `validStateImpl`).
|
||||
*
|
||||
* Because we want to guarantee that each place where we modify the offset has a `PathNode`
|
||||
* we "flip" a boolean flow state in each `isAdditionalFlowStep`. This ensures that the node
|
||||
* has a corresponding `PathNode`.
|
||||
*/
|
||||
private module ValidStateConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = boolean;
|
||||
private module ValidStateConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { hasSize(_, source, _) }
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
hasSize(_, source, _) and
|
||||
state = false
|
||||
}
|
||||
predicate isSink(DataFlow::Node sink) { isSinkPairImpl(_, _, sink, _, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
isSinkPairImpl(_, _, sink, _, _) and
|
||||
state = [false, true]
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
isAdditionalFlowStep2(node1, node2, _) and
|
||||
state1 = [false, true] and
|
||||
state2 = state1.booleanNot()
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalFlowStep2(node1, node2, _)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any() }
|
||||
}
|
||||
|
||||
private import DataFlow::GlobalWithState<ValidStateConfig>
|
||||
private import DataFlow::Global<ValidStateConfig>
|
||||
|
||||
private predicate inLoop(PathNode n) { n.getASuccessor+() = n }
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `cpp/invalid-pointer-deref`, to detect out-of-bounds pointer reads and writes.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Some queries that had repeated results corresponding to different levels of indirection for `argv` now only have a single result.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `cpp/non-constant-format` query no longer considers an assignment on the right-hand side of another assignment to be a source of non-constant format strings. As a result, the query may now produce fewer results.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The "Comparison where assignment was intended" query (`cpp/compare-where-assign-meant`) no longer reports comparisons that appear in macro expansions.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The number of duplicated dataflow paths reported by queries has been significantly reduced.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The queries `cpp/double-free` and `cpp/use-after-free` find fewer false positives
|
||||
in cases where a non-returning function is called.
|
||||
11
cpp/ql/src/change-notes/released/0.7.4.md
Normal file
11
cpp/ql/src/change-notes/released/0.7.4.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## 0.7.4
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `cpp/invalid-pointer-deref`, to detect out-of-bounds pointer reads and writes.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Comparison where assignment was intended" query (`cpp/compare-where-assign-meant`) no longer reports comparisons that appear in macro expansions.
|
||||
* Some queries that had repeated results corresponding to different levels of indirection for `argv` now only have a single result.
|
||||
* The `cpp/non-constant-format` query no longer considers an assignment on the right-hand side of another assignment to be a source of non-constant format strings. As a result, the query may now produce fewer results.
|
||||
3
cpp/ql/src/change-notes/released/0.7.5.md
Normal file
3
cpp/ql/src/change-notes/released/0.7.5.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.7.5
|
||||
|
||||
No user-facing changes.
|
||||
12
cpp/ql/src/change-notes/released/0.8.0.md
Normal file
12
cpp/ql/src/change-notes/released/0.8.0.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## 0.8.0
|
||||
|
||||
### Query Metadata Changes
|
||||
|
||||
* The `cpp/double-free` query has been further improved to reduce false positives and its precision has been increased from `medium` to `high`.
|
||||
* The `cpp/use-after-free` query has been further improved to reduce false positives and its precision has been increased from `medium` to `high`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The queries `cpp/double-free` and `cpp/use-after-free` find fewer false positives
|
||||
in cases where a non-returning function is called.
|
||||
* The number of duplicated dataflow paths reported by queries has been significantly reduced.
|
||||
7
cpp/ql/src/change-notes/released/0.8.1.md
Normal file
7
cpp/ql/src/change-notes/released/0.8.1.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## 0.8.1
|
||||
|
||||
### New Queries
|
||||
|
||||
* The query `cpp/redundant-null-check-simple` has been promoted to Code Scanning. The query finds cases where a pointer is compared to null after it has already been dereferenced. Such comparisons likely indicate a bug at the place where the pointer is dereferenced, or where the pointer is compared to null.
|
||||
|
||||
Note: This query was incorrectly noted as being promoted to Code Scanning in CodeQL version 2.14.6.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user