Merge branch 'main' into py-restframework

This commit is contained in:
Mathew Payne
2023-10-20 11:39:07 +01:00
committed by GitHub
1369 changed files with 93501 additions and 15520 deletions

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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/

View File

@@ -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"

View File

@@ -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 = [

View File

@@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
description: Introduce new floating-point types from C23 and C++23
compatibility: backwards
builtintypes.rel: run builtintypes.qlo

View File

@@ -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"],

View File

@@ -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.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.

View File

@@ -1,4 +0,0 @@
---
category: feature
---
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`

View File

@@ -1,4 +0,0 @@
---
category: deprecated
---
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.

View File

@@ -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.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added support for new floating-point types in C23 and C++23.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1,4 @@
---
category: feature
---
* Added a new class `AdditionalCallTarget` for specifying additional call targets.

View 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.

View 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.

View 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`.

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.9.1
lastReleaseVersion: 0.10.1

View File

@@ -0,0 +1,3 @@
import experimental.cryptography.CryptoArtifact
import experimental.cryptography.CryptoAlgorithmNames
import experimental.cryptography.modules.OpenSSL as OpenSSL

View 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"] }

View 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() }
}

View 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() }
}
}

View File

@@ -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

View File

@@ -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%")
}

View File

@@ -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%"))
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -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))
)
}

View File

@@ -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

View File

@@ -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) }

View File

@@ -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
}
/**

View File

@@ -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)))
}

View File

@@ -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.
*/

View File

@@ -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) {

View File

@@ -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 }
}

View File

@@ -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] }

View File

@@ -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()) }

View File

@@ -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) }

View File

@@ -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) }

View File

@@ -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) }

View File

@@ -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) }

View File

@@ -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) }

View File

@@ -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)
)
}
}

View File

@@ -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.
*/

View File

@@ -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.
*/

View File

@@ -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`.

View File

@@ -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) }

View File

@@ -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) }

View File

@@ -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) }

View File

@@ -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) }

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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 |

View File

@@ -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(

View File

@@ -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
)
}

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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.
*/

View File

@@ -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.
*/

View File

@@ -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
)
)
}
/**

View File

@@ -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()
)
)
}
/**

View File

@@ -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
)
)
}
/**

View File

@@ -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()
)
)
}
/**

View File

@@ -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)
|

View File

@@ -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

View File

@@ -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

View File

@@ -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
)
)
}
/**

View File

@@ -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()
)
)
}
/**

View File

@@ -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 }
/**

View File

@@ -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) {

View File

@@ -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

View File

@@ -0,0 +1,2 @@
description: Introduce new floating-point types from C23 and C++23
compatibility: full

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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"

View File

@@ -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 }

View File

@@ -1,4 +0,0 @@
---
category: newQuery
---
* Added a new query, `cpp/invalid-pointer-deref`, to detect out-of-bounds pointer reads and writes.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The number of duplicated dataflow paths reported by queries has been significantly reduced.

View File

@@ -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.

View 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.

View File

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

View 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.

View 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