mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'main' into redsun82/cargo-upgrade-2
This commit is contained in:
7
.bazelrc
7
.bazelrc
@@ -30,6 +30,13 @@ common --registry=https://bcr.bazel.build
|
||||
|
||||
common --@rules_dotnet//dotnet/settings:strict_deps=false
|
||||
|
||||
# we only configure a nightly toolchain
|
||||
common --@rules_rust//rust/toolchain/channel=nightly
|
||||
|
||||
# rust does not like the gold linker, while bazel does by default, so let's avoid using it
|
||||
common:linux --linkopt=-fuse-ld=lld
|
||||
common:macos --linkopt=-fuse-ld=lld
|
||||
|
||||
# Reduce this eventually to empty, once we've fixed all our usages of java, and https://github.com/bazel-contrib/rules_go/issues/4193 is fixed
|
||||
common --incompatible_autoload_externally="+@rules_java,+@rules_shell"
|
||||
|
||||
|
||||
47
MODULE.bazel
47
MODULE.bazel
@@ -28,7 +28,7 @@ bazel_dep(name = "rules_kotlin", version = "2.1.3-codeql.1")
|
||||
bazel_dep(name = "gazelle", version = "0.40.0")
|
||||
bazel_dep(name = "rules_dotnet", version = "0.17.4")
|
||||
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
|
||||
bazel_dep(name = "rules_rust", version = "0.58.0")
|
||||
bazel_dep(name = "rules_rust", version = "0.63.0")
|
||||
bazel_dep(name = "zstd", version = "1.5.5.bcr.1")
|
||||
|
||||
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||
@@ -38,7 +38,10 @@ bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True
|
||||
RUST_EDITION = "2024"
|
||||
|
||||
# run buildutils-internal/scripts/fill-rust-sha256s.py when updating (internal repo)
|
||||
RUST_VERSION = "1.88.0"
|
||||
# a nightly toolchain is required to enable experimental_use_cc_common_link, which we require internally
|
||||
# we prefer to run the same version as internally, even if experimental_use_cc_common_link is not really
|
||||
# required in this repo
|
||||
RUST_VERSION = "nightly/2025-08-01"
|
||||
|
||||
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
|
||||
rust.toolchain(
|
||||
@@ -50,26 +53,26 @@ rust.toolchain(
|
||||
],
|
||||
# generated by buildutils-internal/scripts/fill-rust-sha256s.py (internal repo)
|
||||
sha256s = {
|
||||
"rustc-1.88.0-x86_64-unknown-linux-gnu.tar.xz": "b049fd57fce274d10013e2cf0e05f215f68f6580865abc52178f66ae9bf43fd8",
|
||||
"rustc-1.88.0-x86_64-apple-darwin.tar.xz": "c8f1ea4fc3e507c8e733809bd3ad91a00f5b209d85620be9013bea5f97f31f24",
|
||||
"rustc-1.88.0-aarch64-apple-darwin.tar.xz": "249f4cacd3fac1f718af19373c73e9d3b9a595965972d8b1f3947c578110f520",
|
||||
"rustc-1.88.0-x86_64-pc-windows-msvc.tar.xz": "238616f0a578d6d4c034ffb8897064fa8df68a3823df201df48ab2baf68a639f",
|
||||
"clippy-1.88.0-x86_64-unknown-linux-gnu.tar.xz": "db09c9e4a8a0b486781d87403f74a203a58d9ef0a58ba10c39264916d93ac603",
|
||||
"clippy-1.88.0-x86_64-apple-darwin.tar.xz": "d25711565eccaf1ead038a626f14eddb8e7db114fb73c24e93264dae4d4298d3",
|
||||
"clippy-1.88.0-aarch64-apple-darwin.tar.xz": "9ad90cddc3ebd892c9d69c9ecd45c30d236e1e4af5993312c6f4538af9dcf3e7",
|
||||
"clippy-1.88.0-x86_64-pc-windows-msvc.tar.xz": "0d02a7b3a8eb407c6a62c75a56f365b312f8ec2732cac5ecfc7a062526fddbe3",
|
||||
"cargo-1.88.0-x86_64-unknown-linux-gnu.tar.xz": "856962610ee821648cee32e3d6abac667af7bb7ea6ec6f3d184cc31e66044f6b",
|
||||
"cargo-1.88.0-x86_64-apple-darwin.tar.xz": "e7f672132591df180b58f8e7af875e1971a10fe71243f7d84f9b3f6742f998bc",
|
||||
"cargo-1.88.0-aarch64-apple-darwin.tar.xz": "71c08c8fab9b7a9cd13b6119886d50ce48efa8261d08e1fd328ed3ee1c84e2e0",
|
||||
"cargo-1.88.0-x86_64-pc-windows-msvc.tar.xz": "5e3b21d77733e0dbb5542015f89b15de1844bd6e3270fdc90bb821b2a04b1cda",
|
||||
"llvm-tools-1.88.0-x86_64-unknown-linux-gnu.tar.xz": "16e8d9b4187cc3936feddd9ceccde0157a4a1b2be98ca9c202cda304e0e81853",
|
||||
"llvm-tools-1.88.0-x86_64-apple-darwin.tar.xz": "92780b5be0950c206d998a6f7094d4ee29b992d1d2f46371465e8bdaa4e619a4",
|
||||
"llvm-tools-1.88.0-aarch64-apple-darwin.tar.xz": "c9bf981651b573d2abb619a5b3ae038686772e51e7ec53a8b5e585c51c1431e5",
|
||||
"llvm-tools-1.88.0-x86_64-pc-windows-msvc.tar.xz": "6522371a06d183effaf080c59d2a8c0720088157ae693123386bc7070ba62a73",
|
||||
"rust-std-1.88.0-x86_64-unknown-linux-gnu.tar.xz": "36d7eacf46bd5199cb433e49a9ed9c9b380d82f8a0ebc05e89b43b51c070c955",
|
||||
"rust-std-1.88.0-x86_64-apple-darwin.tar.xz": "2570350a6651e60a2fe0aa438be5cd123ed3543b4b44c916284ff7e7e331d16a",
|
||||
"rust-std-1.88.0-aarch64-apple-darwin.tar.xz": "532be07511af557cb67f33bfc77044a787363ab281b963752542bc837ce90e96",
|
||||
"rust-std-1.88.0-x86_64-pc-windows-msvc.tar.xz": "6b65df769259ad18428271aea110ec1a5027e922f3e36d77923dc69a38ff6318",
|
||||
"2025-08-01/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "9bbeaf5d3fc7247d31463a9083aa251c995cc50662c8219e7a2254d76a72a9a4",
|
||||
"2025-08-01/rustc-nightly-x86_64-apple-darwin.tar.xz": "c9ea539a8eff0d5d162701f99f9e1aabe14dd0dfb420d62362817a5d09219de7",
|
||||
"2025-08-01/rustc-nightly-aarch64-apple-darwin.tar.xz": "ae83feebbc39cfd982e4ecc8297731fe79c185173aee138467b334c5404b3773",
|
||||
"2025-08-01/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "9f170c30d802a349be60cf52ec46260802093cb1013ad667fc0d528b7b10152f",
|
||||
"2025-08-01/clippy-nightly-x86_64-unknown-linux-gnu.tar.xz": "9ae5f3cd8f557c4f6df522597c69d14398cf604cfaed2b83e767c4b77a7eaaf6",
|
||||
"2025-08-01/clippy-nightly-x86_64-apple-darwin.tar.xz": "983cb9ee0b6b968188e04ab2d33743d54764b2681ce565e1b3f2b9135c696a3e",
|
||||
"2025-08-01/clippy-nightly-aarch64-apple-darwin.tar.xz": "ed2219dbc49d088225e1b7c5c4390fa295066e071fddaa2714018f6bb39ddbf0",
|
||||
"2025-08-01/clippy-nightly-x86_64-pc-windows-msvc.tar.xz": "911f40ab5cbdd686f40e00965271fe47c4805513a308ed01f30eafb25b448a50",
|
||||
"2025-08-01/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "106463c284e48e4904c717471eeec2be5cc83a9d2cae8d6e948b52438cad2e69",
|
||||
"2025-08-01/cargo-nightly-x86_64-apple-darwin.tar.xz": "6ad35c40efc41a8c531ea43235058347b6902d98a9693bf0aed7fc16d5590cef",
|
||||
"2025-08-01/cargo-nightly-aarch64-apple-darwin.tar.xz": "dd28c365e9d298abc3154c797720ad36a0058f131265c9978b4c8e4e37012c8a",
|
||||
"2025-08-01/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "7b431286e12d6b3834b038f078389a00cac73f351e8c3152b2504a3c06420b3b",
|
||||
"2025-08-01/llvm-tools-nightly-x86_64-unknown-linux-gnu.tar.xz": "e342e305d7927cc288d386983b2bc253cfad3776b113386e903d0b302648ef47",
|
||||
"2025-08-01/llvm-tools-nightly-x86_64-apple-darwin.tar.xz": "e44dd3506524d85c37b3a54bcc91d01378fd2c590b2db5c5974d12f05c1b84d1",
|
||||
"2025-08-01/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "0c1b5f46dd81be4a9227b10283a0fcaa39c14fea7e81aea6fd6d9887ff6cdc41",
|
||||
"2025-08-01/llvm-tools-nightly-x86_64-pc-windows-msvc.tar.xz": "423e5fd11406adccbc31b8456ceb7375ce055cdf45e90d2c3babeb2d7f58383f",
|
||||
"2025-08-01/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "3c0ceb46a252647a1d4c7116d9ccae684fa5e42aaf3296419febd2c962c3b41d",
|
||||
"2025-08-01/rust-std-nightly-x86_64-apple-darwin.tar.xz": "3be416003cab10f767390a753d1d16ae4d26c7421c03c98992cf1943e5b0efe8",
|
||||
"2025-08-01/rust-std-nightly-aarch64-apple-darwin.tar.xz": "4046ac0ef951cb056b5028a399124f60999fa37792eab69d008d8d7965f389b4",
|
||||
"2025-08-01/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "191ed9d8603c3a4fe5a7bbbc2feb72049078dae2df3d3b7d5dedf3abbf823e6e",
|
||||
},
|
||||
versions = [RUST_VERSION],
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Overview
|
||||
|
||||
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
|
||||
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed (e.g., due to a modified build script) in a privileged job.
|
||||
|
||||
## Recommendation
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
- run: |
|
||||
npm install
|
||||
npm install # scripts in package.json from PR would be executed here
|
||||
npm build
|
||||
|
||||
- uses: completely/fakeaction@v2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Overview
|
||||
|
||||
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
|
||||
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed (e.g., due to a modified build script) in a privileged job.
|
||||
|
||||
## Recommendation
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
- run: |
|
||||
npm install
|
||||
npm install # scripts in package.json from PR would be executed here
|
||||
npm build
|
||||
|
||||
- uses: completely/fakeaction@v2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Overview
|
||||
|
||||
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
|
||||
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed (e.g., due to a modified build script) in a privileged job.
|
||||
|
||||
## Recommendation
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
- run: |
|
||||
npm install
|
||||
npm install # scripts in package.json from PR would be executed here
|
||||
npm build
|
||||
|
||||
- uses: completely/fakeaction@v2
|
||||
|
||||
4
cpp/ql/lib/change-notes/2025-08-02-gvn.md
Normal file
4
cpp/ql/lib/change-notes/2025-08-02-gvn.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The global value numbering library (`semmle.code.cpp.valuenumbering.GlobalValueNumbering` and `semmle.code.cpp.ir.ValueNumbering`) has been improved so more expressions are assigned the same value number.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved dataflow through global variables in the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow` and `semmle.code.cpp.dataflow.new.TaintTracking`). Queries based on these libraries will produce more results on codebases with many global variables.
|
||||
@@ -57,6 +57,18 @@ private Class getRootType(FieldAccess fa) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of `v`. This predicate does not have a result when the
|
||||
* unspecified type of `v` is a `ReferenceType`.
|
||||
*/
|
||||
private int getVariableSize(Variable v) {
|
||||
exists(Type t |
|
||||
t = v.getUnspecifiedType() and
|
||||
not t instanceof ReferenceType and
|
||||
result = t.getSize()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the buffer access at `va`.
|
||||
*/
|
||||
@@ -64,12 +76,8 @@ private int getSize(VariableAccess va) {
|
||||
exists(Variable v | va.getTarget() = v |
|
||||
// If `v` is not a field then the size of the buffer is just
|
||||
// the size of the type of `v`.
|
||||
exists(Type t |
|
||||
t = v.getUnspecifiedType() and
|
||||
not v instanceof Field and
|
||||
not t instanceof ReferenceType and
|
||||
result = t.getSize()
|
||||
)
|
||||
result = getVariableSize(v)
|
||||
or
|
||||
exists(Class c, int trueSize |
|
||||
// Otherwise, we find the "outermost" object and compute the size
|
||||
@@ -92,7 +100,7 @@ private int getSize(VariableAccess va) {
|
||||
// buffer is `12 - 4 = 8`.
|
||||
c = getRootType(va) and
|
||||
// we calculate the size based on the last field, to avoid including any padding after it
|
||||
trueSize = max(Field f | | f.getOffsetInClass(c) + f.getUnspecifiedType().getSize()) and
|
||||
trueSize = max(Field f | | f.getOffsetInClass(c) + getVariableSize(f)) and
|
||||
result = trueSize - v.(Field).getOffsetInClass(c)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -332,6 +332,13 @@ private module IndirectInstructions {
|
||||
|
||||
import IndirectInstructions
|
||||
|
||||
predicate isPostUpdateNodeImpl(Operand operand, int indirectionIndex) {
|
||||
operand = any(FieldAddress fa).getObjectAddressOperand() and
|
||||
indirectionIndex = [0 .. Ssa::countIndirectionsForCppType(Ssa::getLanguageType(operand))]
|
||||
or
|
||||
Ssa::isModifiableByCall(operand, indirectionIndex)
|
||||
}
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
||||
|
||||
|
||||
@@ -42,11 +42,7 @@ private newtype TIRDataFlowNode =
|
||||
[getMinIndirectionsForType(var.getUnspecifiedType()) .. SsaImpl::getMaxIndirectionsForType(var.getUnspecifiedType())]
|
||||
} or
|
||||
TPostUpdateNodeImpl(Operand operand, int indirectionIndex) {
|
||||
operand = any(FieldAddress fa).getObjectAddressOperand() and
|
||||
indirectionIndex =
|
||||
[0 .. SsaImpl::countIndirectionsForCppType(SsaImpl::getLanguageType(operand))]
|
||||
or
|
||||
SsaImpl::isModifiableByCall(operand, indirectionIndex)
|
||||
isPostUpdateNodeImpl(operand, indirectionIndex)
|
||||
} or
|
||||
TSsaSynthNode(SsaImpl::SynthNode n) or
|
||||
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
|
||||
|
||||
@@ -143,7 +143,14 @@ private predicate isGlobalUse(
|
||||
min(int cand, VariableAddressInstruction vai |
|
||||
vai.getEnclosingIRFunction() = f and
|
||||
vai.getAstVariable() = v and
|
||||
(
|
||||
isDef(_, _, _, vai, cand, indirectionIndex)
|
||||
or
|
||||
exists(Operand operand |
|
||||
isUse(_, operand, vai, cand, indirectionIndex) and
|
||||
isPostUpdateNodeImpl(operand, indirectionIndex)
|
||||
)
|
||||
)
|
||||
|
|
||||
cand
|
||||
)
|
||||
|
||||
@@ -43,6 +43,23 @@ newtype TValueNumber =
|
||||
} or
|
||||
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
|
||||
|
||||
/**
|
||||
* A `ConvertInstruction` which converts data of type `T` to data of type `U`
|
||||
* where `T` and `U` only differ in specifiers. For example, if `T` is `int`
|
||||
* and `U` is `const T` this is a conversion from a non-const integer to a
|
||||
* const integer.
|
||||
*
|
||||
* Generally, the value number of a converted value is different from the value
|
||||
* number of an unconverted value, but conversions which only modify specifiers
|
||||
* leave the resulting value bitwise identical to the old value.
|
||||
*/
|
||||
class TypePreservingConvertInstruction extends ConvertInstruction {
|
||||
TypePreservingConvertInstruction() {
|
||||
pragma[only_bind_out](this.getResultType().getUnspecifiedType()) =
|
||||
pragma[only_bind_out](this.getUnary().getResultType().getUnspecifiedType())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
@@ -216,6 +233,7 @@ private predicate unaryValueNumber(
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
not instr instanceof FieldAddressInstruction and
|
||||
not instr instanceof TypePreservingConvertInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
@@ -351,6 +369,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
or
|
||||
// The value number of a type-preserving conversion is just the value
|
||||
// number of the unconverted value.
|
||||
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -43,6 +43,23 @@ newtype TValueNumber =
|
||||
} or
|
||||
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
|
||||
|
||||
/**
|
||||
* A `ConvertInstruction` which converts data of type `T` to data of type `U`
|
||||
* where `T` and `U` only differ in specifiers. For example, if `T` is `int`
|
||||
* and `U` is `const T` this is a conversion from a non-const integer to a
|
||||
* const integer.
|
||||
*
|
||||
* Generally, the value number of a converted value is different from the value
|
||||
* number of an unconverted value, but conversions which only modify specifiers
|
||||
* leave the resulting value bitwise identical to the old value.
|
||||
*/
|
||||
class TypePreservingConvertInstruction extends ConvertInstruction {
|
||||
TypePreservingConvertInstruction() {
|
||||
pragma[only_bind_out](this.getResultType().getUnspecifiedType()) =
|
||||
pragma[only_bind_out](this.getUnary().getResultType().getUnspecifiedType())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
@@ -216,6 +233,7 @@ private predicate unaryValueNumber(
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
not instr instanceof FieldAddressInstruction and
|
||||
not instr instanceof TypePreservingConvertInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
@@ -351,6 +369,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
or
|
||||
// The value number of a type-preserving conversion is just the value
|
||||
// number of the unconverted value.
|
||||
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,7 +96,8 @@ newtype TInstructionTag =
|
||||
exists(Expr e | exists(e.getImplicitDestructorCall(index))) or
|
||||
exists(Stmt s | exists(s.getImplicitDestructorCall(index)))
|
||||
} or
|
||||
CoAwaitBranchTag()
|
||||
CoAwaitBranchTag() or
|
||||
BoolToIntConversionTag()
|
||||
|
||||
class InstructionTag extends TInstructionTag {
|
||||
final string toString() { result = getInstructionTagId(this) }
|
||||
@@ -286,4 +287,6 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
)
|
||||
or
|
||||
tag = CoAwaitBranchTag() and result = "CoAwaitBranch"
|
||||
or
|
||||
tag = BoolToIntConversionTag() and result = "BoolToIntConversion"
|
||||
}
|
||||
|
||||
@@ -509,6 +509,41 @@ predicate hasTranslatedSyntheticTemporaryObject(Expr expr) {
|
||||
not expr.hasLValueToRValueConversion()
|
||||
}
|
||||
|
||||
Opcode comparisonOpcode(ComparisonOperation expr) {
|
||||
expr instanceof EQExpr and result instanceof Opcode::CompareEQ
|
||||
or
|
||||
expr instanceof NEExpr and result instanceof Opcode::CompareNE
|
||||
or
|
||||
expr instanceof LTExpr and result instanceof Opcode::CompareLT
|
||||
or
|
||||
expr instanceof GTExpr and result instanceof Opcode::CompareGT
|
||||
or
|
||||
expr instanceof LEExpr and result instanceof Opcode::CompareLE
|
||||
or
|
||||
expr instanceof GEExpr and result instanceof Opcode::CompareGE
|
||||
}
|
||||
|
||||
private predicate parentExpectsBool(Expr child) {
|
||||
any(NotExpr notExpr).getOperand() = child
|
||||
or
|
||||
usedAsCondition(child)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` should have a `TranslatedSyntheticBoolToIntConversion` on it.
|
||||
*/
|
||||
predicate hasTranslatedSyntheticBoolToIntConversion(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
not isIRConstant(expr) and
|
||||
not parentExpectsBool(expr) and
|
||||
expr.getUnspecifiedType() instanceof IntType and
|
||||
(
|
||||
expr instanceof NotExpr
|
||||
or
|
||||
exists(comparisonOpcode(expr))
|
||||
)
|
||||
}
|
||||
|
||||
class StaticInitializedStaticLocalVariable extends StaticLocalVariable {
|
||||
StaticInitializedStaticLocalVariable() {
|
||||
this.hasInitializer() and
|
||||
@@ -647,6 +682,9 @@ newtype TTranslatedElement =
|
||||
// A temporary object that we had to synthesize ourselves, so that we could do a field access or
|
||||
// method call on a prvalue.
|
||||
TTranslatedSyntheticTemporaryObject(Expr expr) { hasTranslatedSyntheticTemporaryObject(expr) } or
|
||||
TTranslatedSyntheticBoolToIntConversion(Expr expr) {
|
||||
hasTranslatedSyntheticBoolToIntConversion(expr)
|
||||
} or
|
||||
// For expressions that would not otherwise generate an instruction.
|
||||
TTranslatedResultCopy(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
|
||||
@@ -216,7 +216,8 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
|
||||
not hasTranslatedLoad(expr) and
|
||||
not hasTranslatedSyntheticTemporaryObject(expr) and
|
||||
// If there's a result copy, then this expression's result is the copy.
|
||||
not exprNeedsCopyIfNotLoaded(expr)
|
||||
not exprNeedsCopyIfNotLoaded(expr) and
|
||||
not hasTranslatedSyntheticBoolToIntConversion(expr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,11 +359,12 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a node synthesized to adjust the value category of its operand.
|
||||
* The IR translation of a node synthesized to adjust the value category or type of its operand.
|
||||
* One of:
|
||||
* - `TranslatedLoad` - Convert from glvalue to prvalue by loading from the location.
|
||||
* - `TranslatedSyntheticTemporaryObject` - Convert from prvalue to glvalue by storing to a
|
||||
* temporary variable.
|
||||
* - `TranslatedSyntheticBoolToIntConversion` - Convert a prvalue Boolean to a prvalue integer.
|
||||
*/
|
||||
abstract class TranslatedValueCategoryAdjustment extends TranslatedExpr {
|
||||
final override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
@@ -513,6 +515,45 @@ class TranslatedSyntheticTemporaryObject extends TranslatedValueCategoryAdjustme
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedSyntheticBoolToIntConversion extends TranslatedValueCategoryAdjustment,
|
||||
TTranslatedSyntheticBoolToIntConversion
|
||||
{
|
||||
TranslatedSyntheticBoolToIntConversion() { this = TTranslatedSyntheticBoolToIntConversion(expr) }
|
||||
|
||||
override string toString() { result = "Bool-to-int conversion of " + expr.toString() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
opcode instanceof Opcode::Convert and
|
||||
tag = BoolToIntConversionTag() and
|
||||
resultType = getIntType()
|
||||
}
|
||||
|
||||
override predicate isResultGLValue() { none() }
|
||||
|
||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
|
||||
tag = BoolToIntConversionTag() and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getInstruction(BoolToIntConversionTag())
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
child = this.getOperand() and
|
||||
result = this.getInstruction(BoolToIntConversionTag()) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = this.getInstruction(BoolToIntConversionTag()) }
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = BoolToIntConversionTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = this.getOperand().getResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of an expression that simply returns its result. We generate an otherwise useless
|
||||
* `CopyValue` instruction for these expressions so that there is at least one instruction
|
||||
@@ -1794,20 +1835,6 @@ private Opcode binaryArithmeticOpcode(BinaryArithmeticOperation expr) {
|
||||
expr instanceof PointerDiffExpr and result instanceof Opcode::PointerDiff
|
||||
}
|
||||
|
||||
private Opcode comparisonOpcode(ComparisonOperation expr) {
|
||||
expr instanceof EQExpr and result instanceof Opcode::CompareEQ
|
||||
or
|
||||
expr instanceof NEExpr and result instanceof Opcode::CompareNE
|
||||
or
|
||||
expr instanceof LTExpr and result instanceof Opcode::CompareLT
|
||||
or
|
||||
expr instanceof GTExpr and result instanceof Opcode::CompareGT
|
||||
or
|
||||
expr instanceof LEExpr and result instanceof Opcode::CompareLE
|
||||
or
|
||||
expr instanceof GEExpr and result instanceof Opcode::CompareGE
|
||||
}
|
||||
|
||||
private Opcode spaceShipOpcode(SpaceshipExpr expr) {
|
||||
exists(expr) and
|
||||
result instanceof Opcode::Spaceship
|
||||
|
||||
@@ -43,6 +43,23 @@ newtype TValueNumber =
|
||||
} or
|
||||
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
|
||||
|
||||
/**
|
||||
* A `ConvertInstruction` which converts data of type `T` to data of type `U`
|
||||
* where `T` and `U` only differ in specifiers. For example, if `T` is `int`
|
||||
* and `U` is `const T` this is a conversion from a non-const integer to a
|
||||
* const integer.
|
||||
*
|
||||
* Generally, the value number of a converted value is different from the value
|
||||
* number of an unconverted value, but conversions which only modify specifiers
|
||||
* leave the resulting value bitwise identical to the old value.
|
||||
*/
|
||||
class TypePreservingConvertInstruction extends ConvertInstruction {
|
||||
TypePreservingConvertInstruction() {
|
||||
pragma[only_bind_out](this.getResultType().getUnspecifiedType()) =
|
||||
pragma[only_bind_out](this.getUnary().getResultType().getUnspecifiedType())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
@@ -216,6 +233,7 @@ private predicate unaryValueNumber(
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
not instr instanceof FieldAddressInstruction and
|
||||
not instr instanceof TypePreservingConvertInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
@@ -351,6 +369,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
or
|
||||
// The value number of a type-preserving conversion is just the value
|
||||
// number of the unconverted value.
|
||||
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
4
cpp/ql/src/change-notes/2025-08-08-overflow-buffer.md
Normal file
4
cpp/ql/src/change-notes/2025-08-08-overflow-buffer.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Fixed a false positive in `cpp/overflow-buffer` when the type of the destination buffer is a reference to a class/struct type.
|
||||
@@ -43,6 +43,10 @@ argHasPostUpdate
|
||||
| test.cpp:813:19:813:35 | * ... | ArgumentNode is missing PostUpdateNode. |
|
||||
| test.cpp:848:23:848:25 | rpx | ArgumentNode is missing PostUpdateNode. |
|
||||
| test.cpp:1093:19:1093:21 | * ... | ArgumentNode is missing PostUpdateNode. |
|
||||
| test.cpp:1206:19:1206:37 | * ... | ArgumentNode is missing PostUpdateNode. |
|
||||
| test.cpp:1207:10:1207:28 | * ... | ArgumentNode is missing PostUpdateNode. |
|
||||
| test.cpp:1224:19:1224:37 | * ... | ArgumentNode is missing PostUpdateNode. |
|
||||
| test.cpp:1225:10:1225:28 | * ... | ArgumentNode is missing PostUpdateNode. |
|
||||
postWithInFlow
|
||||
| BarrierGuard.cpp:49:6:49:6 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| BarrierGuard.cpp:60:7:60:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
@@ -193,6 +197,10 @@ postWithInFlow
|
||||
| test.cpp:1139:4:1139:7 | data [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:1153:5:1153:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:1153:6:1153:6 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:1165:5:1165:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:1165:6:1165:6 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:1195:5:1195:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:1195:6:1195:6 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -134,6 +134,11 @@ astFlow
|
||||
| test.cpp:1086:12:1086:12 | a | test.cpp:1088:8:1088:9 | & ... |
|
||||
| test.cpp:1137:7:1137:10 | data | test.cpp:1140:8:1140:18 | * ... |
|
||||
| test.cpp:1138:17:1138:22 | call to source | test.cpp:1140:8:1140:18 | * ... |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1170:19:1170:32 | global_int_ptr |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1234:19:1234:34 | global_int_array |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1239:10:1239:26 | * ... |
|
||||
| test.cpp:1195:10:1195:24 | call to indirect_source | test.cpp:1200:19:1200:36 | global_int_ptr_ptr |
|
||||
| test.cpp:1195:10:1195:24 | call to indirect_source | test.cpp:1201:10:1201:27 | global_int_ptr_ptr |
|
||||
| true_upon_entry.cpp:17:11:17:16 | call to source | true_upon_entry.cpp:21:8:21:8 | x |
|
||||
| true_upon_entry.cpp:27:9:27:14 | call to source | true_upon_entry.cpp:29:8:29:8 | x |
|
||||
| true_upon_entry.cpp:33:11:33:16 | call to source | true_upon_entry.cpp:39:8:39:8 | x |
|
||||
@@ -327,6 +332,20 @@ irFlow
|
||||
| test.cpp:1117:27:1117:34 | call to source | test.cpp:1117:27:1117:34 | call to source |
|
||||
| test.cpp:1132:11:1132:16 | call to source | test.cpp:1121:8:1121:8 | x |
|
||||
| test.cpp:1138:17:1138:22 | call to source | test.cpp:1140:8:1140:18 | * ... |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1170:19:1170:32 | *global_int_ptr |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1175:10:1175:24 | * ... |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1184:19:1184:32 | *global_int_ptr |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1189:10:1189:24 | * ... |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1234:19:1234:34 | *global_int_array |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1239:10:1239:26 | * ... |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1248:19:1248:34 | *global_int_array |
|
||||
| test.cpp:1165:10:1165:15 | call to source | test.cpp:1253:10:1253:26 | * ... |
|
||||
| test.cpp:1195:10:1195:24 | *call to indirect_source | test.cpp:1200:19:1200:36 | **global_int_ptr_ptr |
|
||||
| test.cpp:1195:10:1195:24 | *call to indirect_source | test.cpp:1206:19:1206:37 | ** ... |
|
||||
| test.cpp:1195:10:1195:24 | *call to indirect_source | test.cpp:1209:10:1209:29 | * ... |
|
||||
| test.cpp:1195:10:1195:24 | *call to indirect_source | test.cpp:1218:19:1218:36 | **global_int_ptr_ptr |
|
||||
| test.cpp:1195:10:1195:24 | *call to indirect_source | test.cpp:1224:19:1224:37 | ** ... |
|
||||
| test.cpp:1195:10:1195:24 | *call to indirect_source | test.cpp:1227:10:1227:29 | * ... |
|
||||
| true_upon_entry.cpp:9:11:9:16 | call to source | true_upon_entry.cpp:13:8:13:8 | x |
|
||||
| true_upon_entry.cpp:17:11:17:16 | call to source | true_upon_entry.cpp:21:8:21:8 | x |
|
||||
| true_upon_entry.cpp:27:9:27:14 | call to source | true_upon_entry.cpp:29:8:29:8 | x |
|
||||
|
||||
@@ -1156,3 +1156,100 @@ namespace conflation_regression {
|
||||
}
|
||||
|
||||
int recursion = (sink(recursion), source()); // clean
|
||||
|
||||
|
||||
namespace globals_without_explicit_def {
|
||||
int* global_int_ptr;
|
||||
|
||||
void set(int* p) { // $ ast-def=p ir-def=*p
|
||||
*p = source();
|
||||
}
|
||||
|
||||
void test1() {
|
||||
set(global_int_ptr);
|
||||
indirect_sink(global_int_ptr); // $ ir,ast
|
||||
}
|
||||
|
||||
void test2() {
|
||||
set(global_int_ptr);
|
||||
sink(*global_int_ptr); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void calls_set() {
|
||||
set(global_int_ptr);
|
||||
}
|
||||
|
||||
void test3() {
|
||||
calls_set();
|
||||
indirect_sink(global_int_ptr); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test4() {
|
||||
calls_set();
|
||||
sink(*global_int_ptr); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
int** global_int_ptr_ptr;
|
||||
|
||||
void set_indirect(int** p) { // $ ast-def=p ir-def=*p ir-def=**p
|
||||
*p = indirect_source();
|
||||
}
|
||||
|
||||
void test5() {
|
||||
set_indirect(global_int_ptr_ptr);
|
||||
indirect_sink(global_int_ptr_ptr); // $ ir,ast
|
||||
sink(global_int_ptr_ptr); // $ SPURIOUS: ast
|
||||
}
|
||||
|
||||
void test6() {
|
||||
set_indirect(global_int_ptr_ptr);
|
||||
indirect_sink(*global_int_ptr_ptr); // $ ir MISSING: ast
|
||||
sink(*global_int_ptr_ptr);
|
||||
indirect_sink(**global_int_ptr_ptr);
|
||||
sink(**global_int_ptr_ptr); // $ ir
|
||||
}
|
||||
|
||||
void calls_set_indirect() {
|
||||
set_indirect(global_int_ptr_ptr);
|
||||
}
|
||||
|
||||
void test7() {
|
||||
calls_set_indirect();
|
||||
indirect_sink(global_int_ptr_ptr); // $ ir MISSING: ast
|
||||
sink(global_int_ptr_ptr); // $ MISSING: ast
|
||||
}
|
||||
|
||||
void test8() {
|
||||
calls_set_indirect();
|
||||
indirect_sink(*global_int_ptr_ptr); // $ ir MISSING: ast
|
||||
sink(*global_int_ptr_ptr);
|
||||
indirect_sink(**global_int_ptr_ptr);
|
||||
sink(**global_int_ptr_ptr); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
int global_int_array[10];
|
||||
|
||||
void test9() {
|
||||
set(global_int_array);
|
||||
indirect_sink(global_int_array); // $ ir,ast
|
||||
}
|
||||
|
||||
void test10() {
|
||||
set(global_int_array);
|
||||
sink(*global_int_array); // $ ir,ast
|
||||
}
|
||||
|
||||
void calls_set_array() {
|
||||
set(global_int_array);
|
||||
}
|
||||
|
||||
void test11() {
|
||||
calls_set_array();
|
||||
indirect_sink(global_int_array); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test12() {
|
||||
calls_set_array();
|
||||
sink(*global_int_array); // $ ir MISSING: ast
|
||||
}
|
||||
}
|
||||
@@ -205,3 +205,64 @@ void deep_member_field_arrow_different_fields(S2 *ps2) {
|
||||
taint_a_ptr(&ps2->s.m1);
|
||||
sink(ps2->s.m2);
|
||||
}
|
||||
|
||||
|
||||
namespace GlobalFieldFlow {
|
||||
S global_s;
|
||||
S2 global_s2;
|
||||
|
||||
void set_field() {
|
||||
global_s.m1 = user_input();
|
||||
}
|
||||
|
||||
void read_field() {
|
||||
sink(global_s.m1); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void set_nested_field() {
|
||||
global_s2.s.m1 = user_input();
|
||||
}
|
||||
|
||||
void read_nested_field() {
|
||||
sink(global_s2.s.m1); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
S* global_s_ptr;
|
||||
S2* global_s2_ptr;
|
||||
|
||||
void set_field_ptr() {
|
||||
global_s_ptr->m1 = user_input();
|
||||
}
|
||||
|
||||
void read_field_ptr() {
|
||||
sink(global_s_ptr->m1); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void set_nested_field_ptr() {
|
||||
global_s2_ptr->s.m1 = user_input();
|
||||
}
|
||||
|
||||
void read_nested_field_ptr() {
|
||||
sink(global_s2_ptr->s.m1); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
S_with_pointer global_s_with_pointer;
|
||||
|
||||
void set_field_indirect() {
|
||||
*global_s_with_pointer.data = user_input();
|
||||
}
|
||||
|
||||
void read_field_indirect() {
|
||||
sink(*global_s_with_pointer.data); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
S_with_array global_s_with_array;
|
||||
|
||||
void set_field_array() {
|
||||
*global_s_with_array.data = user_input();
|
||||
}
|
||||
|
||||
void read_field_array() {
|
||||
sink(*global_s_with_array.data); // $ ir MISSING: ast
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,14 @@ postWithInFlow
|
||||
| aliasing.cpp:194:21:194:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:200:23:200:24 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:205:23:205:24 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:215:14:215:15 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:223:17:223:18 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:234:19:234:20 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:242:22:242:23 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:252:5:252:31 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:252:28:252:31 | data [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:262:5:262:29 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aliasing.cpp:262:26:262:29 | data [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| arrays.cpp:6:3:6:5 | arr [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| arrays.cpp:6:3:6:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| arrays.cpp:15:3:15:10 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -346,6 +346,46 @@ edges
|
||||
| aliasing.cpp:200:21:200:21 | *s [post update] [m1] | aliasing.cpp:200:16:200:18 | *ps2 [post update] [s, m1] | provenance | |
|
||||
| aliasing.cpp:201:8:201:10 | *ps2 [s, m1] | aliasing.cpp:201:13:201:13 | *s [m1] | provenance | |
|
||||
| aliasing.cpp:201:13:201:13 | *s [m1] | aliasing.cpp:201:15:201:16 | m1 | provenance | |
|
||||
| aliasing.cpp:211:5:211:12 | *global_s [m1] | aliasing.cpp:211:5:211:12 | *global_s [m1] | provenance | |
|
||||
| aliasing.cpp:211:5:211:12 | *global_s [m1] | aliasing.cpp:219:10:219:17 | *global_s [m1] | provenance | |
|
||||
| aliasing.cpp:212:6:212:14 | *global_s2 [s, m1] | aliasing.cpp:212:6:212:14 | *global_s2 [s, m1] | provenance | |
|
||||
| aliasing.cpp:212:6:212:14 | *global_s2 [s, m1] | aliasing.cpp:227:10:227:18 | *global_s2 [s, m1] | provenance | |
|
||||
| aliasing.cpp:215:5:215:12 | *global_s [post update] [m1] | aliasing.cpp:211:5:211:12 | *global_s [m1] | provenance | |
|
||||
| aliasing.cpp:215:5:215:30 | ... = ... | aliasing.cpp:215:5:215:12 | *global_s [post update] [m1] | provenance | |
|
||||
| aliasing.cpp:215:19:215:28 | call to user_input | aliasing.cpp:215:5:215:30 | ... = ... | provenance | |
|
||||
| aliasing.cpp:219:10:219:17 | *global_s [m1] | aliasing.cpp:219:19:219:20 | m1 | provenance | |
|
||||
| aliasing.cpp:223:5:223:13 | *global_s2 [post update] [s, m1] | aliasing.cpp:212:6:212:14 | *global_s2 [s, m1] | provenance | |
|
||||
| aliasing.cpp:223:5:223:33 | ... = ... | aliasing.cpp:223:15:223:15 | *s [post update] [m1] | provenance | |
|
||||
| aliasing.cpp:223:15:223:15 | *s [post update] [m1] | aliasing.cpp:223:5:223:13 | *global_s2 [post update] [s, m1] | provenance | |
|
||||
| aliasing.cpp:223:22:223:31 | call to user_input | aliasing.cpp:223:5:223:33 | ... = ... | provenance | |
|
||||
| aliasing.cpp:227:10:227:18 | *global_s2 [s, m1] | aliasing.cpp:227:20:227:20 | *s [m1] | provenance | |
|
||||
| aliasing.cpp:227:20:227:20 | *s [m1] | aliasing.cpp:227:22:227:23 | m1 | provenance | |
|
||||
| aliasing.cpp:230:6:230:17 | **global_s_ptr [m1] | aliasing.cpp:230:6:230:17 | **global_s_ptr [m1] | provenance | |
|
||||
| aliasing.cpp:230:6:230:17 | **global_s_ptr [m1] | aliasing.cpp:238:10:238:21 | *global_s_ptr [m1] | provenance | |
|
||||
| aliasing.cpp:231:7:231:19 | **global_s2_ptr [s, m1] | aliasing.cpp:231:7:231:19 | **global_s2_ptr [s, m1] | provenance | |
|
||||
| aliasing.cpp:231:7:231:19 | **global_s2_ptr [s, m1] | aliasing.cpp:246:10:246:22 | *global_s2_ptr [s, m1] | provenance | |
|
||||
| aliasing.cpp:234:5:234:16 | *global_s_ptr [post update] [m1] | aliasing.cpp:230:6:230:17 | **global_s_ptr [m1] | provenance | |
|
||||
| aliasing.cpp:234:5:234:35 | ... = ... | aliasing.cpp:234:5:234:16 | *global_s_ptr [post update] [m1] | provenance | |
|
||||
| aliasing.cpp:234:24:234:33 | call to user_input | aliasing.cpp:234:5:234:35 | ... = ... | provenance | |
|
||||
| aliasing.cpp:238:10:238:21 | *global_s_ptr [m1] | aliasing.cpp:238:24:238:25 | m1 | provenance | |
|
||||
| aliasing.cpp:242:5:242:17 | *global_s2_ptr [post update] [s, m1] | aliasing.cpp:231:7:231:19 | **global_s2_ptr [s, m1] | provenance | |
|
||||
| aliasing.cpp:242:5:242:38 | ... = ... | aliasing.cpp:242:20:242:20 | *s [post update] [m1] | provenance | |
|
||||
| aliasing.cpp:242:20:242:20 | *s [post update] [m1] | aliasing.cpp:242:5:242:17 | *global_s2_ptr [post update] [s, m1] | provenance | |
|
||||
| aliasing.cpp:242:27:242:36 | call to user_input | aliasing.cpp:242:5:242:38 | ... = ... | provenance | |
|
||||
| aliasing.cpp:246:10:246:22 | *global_s2_ptr [s, m1] | aliasing.cpp:246:25:246:25 | *s [m1] | provenance | |
|
||||
| aliasing.cpp:246:25:246:25 | *s [m1] | aliasing.cpp:246:27:246:28 | m1 | provenance | |
|
||||
| aliasing.cpp:249:18:249:38 | *global_s_with_pointer [*data] | aliasing.cpp:249:18:249:38 | *global_s_with_pointer [*data] | provenance | |
|
||||
| aliasing.cpp:249:18:249:38 | *global_s_with_pointer [*data] | aliasing.cpp:256:11:256:31 | *global_s_with_pointer [*data] | provenance | |
|
||||
| aliasing.cpp:252:5:252:46 | ... = ... | aliasing.cpp:252:6:252:26 | *global_s_with_pointer [post update] [*data] | provenance | |
|
||||
| aliasing.cpp:252:6:252:26 | *global_s_with_pointer [post update] [*data] | aliasing.cpp:249:18:249:38 | *global_s_with_pointer [*data] | provenance | |
|
||||
| aliasing.cpp:252:35:252:44 | call to user_input | aliasing.cpp:252:5:252:46 | ... = ... | provenance | |
|
||||
| aliasing.cpp:256:11:256:31 | *global_s_with_pointer [*data] | aliasing.cpp:256:10:256:36 | * ... | provenance | |
|
||||
| aliasing.cpp:259:16:259:34 | *global_s_with_array [data] | aliasing.cpp:259:16:259:34 | *global_s_with_array [data] | provenance | |
|
||||
| aliasing.cpp:259:16:259:34 | *global_s_with_array [data] | aliasing.cpp:266:11:266:29 | *global_s_with_array [data] | provenance | |
|
||||
| aliasing.cpp:262:5:262:44 | ... = ... | aliasing.cpp:262:6:262:24 | *global_s_with_array [post update] [data] | provenance | |
|
||||
| aliasing.cpp:262:6:262:24 | *global_s_with_array [post update] [data] | aliasing.cpp:259:16:259:34 | *global_s_with_array [data] | provenance | |
|
||||
| aliasing.cpp:262:33:262:42 | call to user_input | aliasing.cpp:262:5:262:44 | ... = ... | provenance | |
|
||||
| aliasing.cpp:266:11:266:29 | *global_s_with_array [data] | aliasing.cpp:266:10:266:34 | * ... | provenance | |
|
||||
| arrays.cpp:6:3:6:23 | ... = ... | arrays.cpp:7:8:7:13 | access to array | provenance | |
|
||||
| arrays.cpp:6:3:6:23 | ... = ... | arrays.cpp:8:8:8:13 | access to array | provenance | |
|
||||
| arrays.cpp:6:3:6:23 | ... = ... | arrays.cpp:9:8:9:11 | * ... | provenance | |
|
||||
@@ -1244,6 +1284,46 @@ nodes
|
||||
| aliasing.cpp:201:8:201:10 | *ps2 [s, m1] | semmle.label | *ps2 [s, m1] |
|
||||
| aliasing.cpp:201:13:201:13 | *s [m1] | semmle.label | *s [m1] |
|
||||
| aliasing.cpp:201:15:201:16 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:211:5:211:12 | *global_s [m1] | semmle.label | *global_s [m1] |
|
||||
| aliasing.cpp:212:6:212:14 | *global_s2 [s, m1] | semmle.label | *global_s2 [s, m1] |
|
||||
| aliasing.cpp:215:5:215:12 | *global_s [post update] [m1] | semmle.label | *global_s [post update] [m1] |
|
||||
| aliasing.cpp:215:5:215:30 | ... = ... | semmle.label | ... = ... |
|
||||
| aliasing.cpp:215:19:215:28 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:219:10:219:17 | *global_s [m1] | semmle.label | *global_s [m1] |
|
||||
| aliasing.cpp:219:19:219:20 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:223:5:223:13 | *global_s2 [post update] [s, m1] | semmle.label | *global_s2 [post update] [s, m1] |
|
||||
| aliasing.cpp:223:5:223:33 | ... = ... | semmle.label | ... = ... |
|
||||
| aliasing.cpp:223:15:223:15 | *s [post update] [m1] | semmle.label | *s [post update] [m1] |
|
||||
| aliasing.cpp:223:22:223:31 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:227:10:227:18 | *global_s2 [s, m1] | semmle.label | *global_s2 [s, m1] |
|
||||
| aliasing.cpp:227:20:227:20 | *s [m1] | semmle.label | *s [m1] |
|
||||
| aliasing.cpp:227:22:227:23 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:230:6:230:17 | **global_s_ptr [m1] | semmle.label | **global_s_ptr [m1] |
|
||||
| aliasing.cpp:231:7:231:19 | **global_s2_ptr [s, m1] | semmle.label | **global_s2_ptr [s, m1] |
|
||||
| aliasing.cpp:234:5:234:16 | *global_s_ptr [post update] [m1] | semmle.label | *global_s_ptr [post update] [m1] |
|
||||
| aliasing.cpp:234:5:234:35 | ... = ... | semmle.label | ... = ... |
|
||||
| aliasing.cpp:234:24:234:33 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:238:10:238:21 | *global_s_ptr [m1] | semmle.label | *global_s_ptr [m1] |
|
||||
| aliasing.cpp:238:24:238:25 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:242:5:242:17 | *global_s2_ptr [post update] [s, m1] | semmle.label | *global_s2_ptr [post update] [s, m1] |
|
||||
| aliasing.cpp:242:5:242:38 | ... = ... | semmle.label | ... = ... |
|
||||
| aliasing.cpp:242:20:242:20 | *s [post update] [m1] | semmle.label | *s [post update] [m1] |
|
||||
| aliasing.cpp:242:27:242:36 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:246:10:246:22 | *global_s2_ptr [s, m1] | semmle.label | *global_s2_ptr [s, m1] |
|
||||
| aliasing.cpp:246:25:246:25 | *s [m1] | semmle.label | *s [m1] |
|
||||
| aliasing.cpp:246:27:246:28 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:249:18:249:38 | *global_s_with_pointer [*data] | semmle.label | *global_s_with_pointer [*data] |
|
||||
| aliasing.cpp:252:5:252:46 | ... = ... | semmle.label | ... = ... |
|
||||
| aliasing.cpp:252:6:252:26 | *global_s_with_pointer [post update] [*data] | semmle.label | *global_s_with_pointer [post update] [*data] |
|
||||
| aliasing.cpp:252:35:252:44 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:256:10:256:36 | * ... | semmle.label | * ... |
|
||||
| aliasing.cpp:256:11:256:31 | *global_s_with_pointer [*data] | semmle.label | *global_s_with_pointer [*data] |
|
||||
| aliasing.cpp:259:16:259:34 | *global_s_with_array [data] | semmle.label | *global_s_with_array [data] |
|
||||
| aliasing.cpp:262:5:262:44 | ... = ... | semmle.label | ... = ... |
|
||||
| aliasing.cpp:262:6:262:24 | *global_s_with_array [post update] [data] | semmle.label | *global_s_with_array [post update] [data] |
|
||||
| aliasing.cpp:262:33:262:42 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:266:10:266:34 | * ... | semmle.label | * ... |
|
||||
| aliasing.cpp:266:11:266:29 | *global_s_with_array [data] | semmle.label | *global_s_with_array [data] |
|
||||
| arrays.cpp:6:3:6:23 | ... = ... | semmle.label | ... = ... |
|
||||
| arrays.cpp:6:12:6:21 | call to user_input | semmle.label | call to user_input |
|
||||
| arrays.cpp:7:8:7:13 | access to array | semmle.label | access to array |
|
||||
@@ -1902,6 +1982,12 @@ subpaths
|
||||
| aliasing.cpp:176:13:176:14 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:176:13:176:14 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:189:15:189:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:189:15:189:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:201:15:201:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:201:15:201:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:219:19:219:20 | m1 | aliasing.cpp:215:19:215:28 | call to user_input | aliasing.cpp:219:19:219:20 | m1 | m1 flows from $@ | aliasing.cpp:215:19:215:28 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:227:22:227:23 | m1 | aliasing.cpp:223:22:223:31 | call to user_input | aliasing.cpp:227:22:227:23 | m1 | m1 flows from $@ | aliasing.cpp:223:22:223:31 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:238:24:238:25 | m1 | aliasing.cpp:234:24:234:33 | call to user_input | aliasing.cpp:238:24:238:25 | m1 | m1 flows from $@ | aliasing.cpp:234:24:234:33 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:246:27:246:28 | m1 | aliasing.cpp:242:27:242:36 | call to user_input | aliasing.cpp:246:27:246:28 | m1 | m1 flows from $@ | aliasing.cpp:242:27:242:36 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:256:10:256:36 | * ... | aliasing.cpp:252:35:252:44 | call to user_input | aliasing.cpp:256:10:256:36 | * ... | * ... flows from $@ | aliasing.cpp:252:35:252:44 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:266:10:266:34 | * ... | aliasing.cpp:262:33:262:42 | call to user_input | aliasing.cpp:266:10:266:34 | * ... | * ... flows from $@ | aliasing.cpp:262:33:262:42 | call to user_input | call to user_input |
|
||||
| arrays.cpp:7:8:7:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
|
||||
| arrays.cpp:8:8:8:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
|
||||
| arrays.cpp:9:8:9:11 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
|
||||
|
||||
@@ -141,6 +141,20 @@ WARNING: module 'DataFlow' has been deprecated and may be removed in future (par
|
||||
| aliasing.cpp:201:13:201:13 | s | IR only |
|
||||
| aliasing.cpp:206:8:206:10 | ps2 | IR only |
|
||||
| aliasing.cpp:206:13:206:13 | s | IR only |
|
||||
| aliasing.cpp:215:14:215:15 | m1 | AST only |
|
||||
| aliasing.cpp:219:10:219:17 | global_s | IR only |
|
||||
| aliasing.cpp:223:17:223:18 | m1 | AST only |
|
||||
| aliasing.cpp:227:10:227:18 | global_s2 | IR only |
|
||||
| aliasing.cpp:227:20:227:20 | s | IR only |
|
||||
| aliasing.cpp:234:19:234:20 | m1 | AST only |
|
||||
| aliasing.cpp:238:10:238:21 | global_s_ptr | IR only |
|
||||
| aliasing.cpp:242:22:242:23 | m1 | AST only |
|
||||
| aliasing.cpp:246:10:246:22 | global_s2_ptr | IR only |
|
||||
| aliasing.cpp:246:25:246:25 | s | IR only |
|
||||
| aliasing.cpp:252:5:252:31 | * ... | AST only |
|
||||
| aliasing.cpp:256:11:256:31 | global_s_with_pointer | IR only |
|
||||
| aliasing.cpp:262:5:262:29 | * ... | AST only |
|
||||
| aliasing.cpp:266:11:266:29 | global_s_with_array | IR only |
|
||||
| arrays.cpp:6:3:6:8 | access to array | AST only |
|
||||
| arrays.cpp:7:8:7:13 | access to array | IR only |
|
||||
| arrays.cpp:7:8:7:13 | access to array | IR only |
|
||||
|
||||
@@ -285,6 +285,22 @@
|
||||
| aliasing.cpp:205:21:205:21 | s |
|
||||
| aliasing.cpp:206:8:206:10 | ps2 |
|
||||
| aliasing.cpp:206:13:206:13 | s |
|
||||
| aliasing.cpp:215:5:215:12 | global_s |
|
||||
| aliasing.cpp:219:10:219:17 | global_s |
|
||||
| aliasing.cpp:223:5:223:13 | global_s2 |
|
||||
| aliasing.cpp:223:15:223:15 | s |
|
||||
| aliasing.cpp:227:10:227:18 | global_s2 |
|
||||
| aliasing.cpp:227:20:227:20 | s |
|
||||
| aliasing.cpp:234:5:234:16 | global_s_ptr |
|
||||
| aliasing.cpp:238:10:238:21 | global_s_ptr |
|
||||
| aliasing.cpp:242:5:242:17 | global_s2_ptr |
|
||||
| aliasing.cpp:242:20:242:20 | s |
|
||||
| aliasing.cpp:246:10:246:22 | global_s2_ptr |
|
||||
| aliasing.cpp:246:25:246:25 | s |
|
||||
| aliasing.cpp:252:6:252:26 | global_s_with_pointer |
|
||||
| aliasing.cpp:256:11:256:31 | global_s_with_pointer |
|
||||
| aliasing.cpp:262:6:262:24 | global_s_with_array |
|
||||
| aliasing.cpp:266:11:266:29 | global_s_with_array |
|
||||
| arrays.cpp:7:8:7:13 | access to array |
|
||||
| arrays.cpp:8:8:8:13 | access to array |
|
||||
| arrays.cpp:9:8:9:11 | * ... |
|
||||
|
||||
@@ -225,6 +225,20 @@ WARNING: module 'DataFlow' has been deprecated and may be removed in future (par
|
||||
| aliasing.cpp:205:15:205:24 | & ... |
|
||||
| aliasing.cpp:205:16:205:18 | ps2 |
|
||||
| aliasing.cpp:205:21:205:21 | s |
|
||||
| aliasing.cpp:215:5:215:12 | global_s |
|
||||
| aliasing.cpp:215:14:215:15 | m1 |
|
||||
| aliasing.cpp:223:5:223:13 | global_s2 |
|
||||
| aliasing.cpp:223:15:223:15 | s |
|
||||
| aliasing.cpp:223:17:223:18 | m1 |
|
||||
| aliasing.cpp:234:5:234:16 | global_s_ptr |
|
||||
| aliasing.cpp:234:19:234:20 | m1 |
|
||||
| aliasing.cpp:242:5:242:17 | global_s2_ptr |
|
||||
| aliasing.cpp:242:20:242:20 | s |
|
||||
| aliasing.cpp:242:22:242:23 | m1 |
|
||||
| aliasing.cpp:252:5:252:31 | * ... |
|
||||
| aliasing.cpp:252:6:252:26 | global_s_with_pointer |
|
||||
| aliasing.cpp:262:5:262:29 | * ... |
|
||||
| aliasing.cpp:262:6:262:24 | global_s_with_array |
|
||||
| arrays.cpp:6:3:6:8 | access to array |
|
||||
| arrays.cpp:15:3:15:10 | * ... |
|
||||
| arrays.cpp:36:3:36:3 | o |
|
||||
|
||||
@@ -4932,7 +4932,20 @@ ir.c:
|
||||
# 103| Type = [IntType] int
|
||||
# 103| ValueCategory = prvalue(load)
|
||||
# 103| getThen(): [BlockStmt] { ... }
|
||||
# 104| getStmt(16): [ReturnStmt] return ...
|
||||
# 105| getStmt(16): [DeclStmt] declaration
|
||||
# 105| getDeclarationEntry(0): [VariableDeclarationEntry] definition of double_negation
|
||||
# 105| Type = [IntType] int
|
||||
# 105| getVariable().getInitializer(): [Initializer] initializer for double_negation
|
||||
# 105| getExpr(): [NotExpr] ! ...
|
||||
# 105| Type = [IntType] int
|
||||
# 105| ValueCategory = prvalue
|
||||
# 105| getOperand(): [NotExpr] ! ...
|
||||
# 105| Type = [IntType] int
|
||||
# 105| ValueCategory = prvalue
|
||||
# 105| getOperand(): [VariableAccess] x1
|
||||
# 105| Type = [IntType] int
|
||||
# 105| ValueCategory = prvalue(load)
|
||||
# 106| getStmt(17): [ReturnStmt] return ...
|
||||
ir.cpp:
|
||||
# 1| [TopLevelFunction] void Constants()
|
||||
# 1| <params>:
|
||||
|
||||
@@ -3706,9 +3706,10 @@ ir.c:
|
||||
# 88| r88_3(int) = Load[x1] : &:r88_2, m84_6
|
||||
# 88| r88_4(int) = Constant[0] :
|
||||
# 88| r88_5(bool) = CompareEQ : r88_3, r88_4
|
||||
# 88| m88_6(int) = Store[y] : &:r88_1, r88_5
|
||||
# 88| r88_6(int) = Convert : r88_5
|
||||
# 88| m88_7(int) = Store[y] : &:r88_1, r88_6
|
||||
# 89| r89_1(glval<int>) = VariableAddress[y] :
|
||||
# 89| r89_2(int) = Load[y] : &:r89_1, m88_6
|
||||
# 89| r89_2(int) = Load[y] : &:r89_1, m88_7
|
||||
# 89| r89_3(int) = Constant[0] :
|
||||
# 89| r89_4(bool) = CompareNE : r89_2, r89_3
|
||||
# 89| v89_5(void) = ConditionalBranch : r89_4
|
||||
@@ -3721,7 +3722,7 @@ ir.c:
|
||||
|
||||
# 90| Block 6
|
||||
# 90| r90_1(glval<int>) = VariableAddress[y] :
|
||||
# 90| r90_2(int) = Load[y] : &:r90_1, m88_6
|
||||
# 90| r90_2(int) = Load[y] : &:r90_1, m88_7
|
||||
# 90| r90_3(int) = Constant[0] :
|
||||
# 90| r90_4(bool) = CompareEQ : r90_2, r90_3
|
||||
# 90| v90_5(void) = ConditionalBranch : r90_4
|
||||
@@ -3969,8 +3970,16 @@ ir.c:
|
||||
# 103| v103_6(void) = NoOp :
|
||||
#-----| Goto -> Block 40
|
||||
|
||||
# 104| Block 40
|
||||
# 104| v104_1(void) = NoOp :
|
||||
# 105| Block 40
|
||||
# 105| r105_1(glval<int>) = VariableAddress[double_negation] :
|
||||
# 105| r105_2(glval<int>) = VariableAddress[x1] :
|
||||
# 105| r105_3(int) = Load[x1] : &:r105_2, m84_6
|
||||
# 105| r105_4(int) = Constant[0] :
|
||||
# 105| r105_5(bool) = CompareEQ : r105_3, r105_4
|
||||
# 105| r105_6(bool) = LogicalNot : r105_5
|
||||
# 105| r105_7(int) = Convert : r105_6
|
||||
# 105| m105_8(int) = Store[double_negation] : &:r105_1, r105_7
|
||||
# 106| v106_1(void) = NoOp :
|
||||
# 84| v84_9(void) = ReturnVoid :
|
||||
# 84| v84_10(void) = AliasedUse : m84_3
|
||||
# 84| v84_11(void) = ExitFunction :
|
||||
|
||||
@@ -101,6 +101,8 @@ void branch_on_integral_in_c(int x1, int x2) {
|
||||
int x_1_and_2 = x1 && x2;
|
||||
if(x_1_and_2) {}
|
||||
if(!x_1_and_2) {}
|
||||
|
||||
int double_negation = !!x1;
|
||||
}
|
||||
|
||||
// semmle-extractor-options: --microsoft
|
||||
|
||||
@@ -3343,7 +3343,8 @@ ir.c:
|
||||
# 88| r88_3(int) = Load[x1] : &:r88_2, ~m?
|
||||
# 88| r88_4(int) = Constant[0] :
|
||||
# 88| r88_5(bool) = CompareEQ : r88_3, r88_4
|
||||
# 88| mu88_6(int) = Store[y] : &:r88_1, r88_5
|
||||
# 88| r88_6(int) = Convert : r88_5
|
||||
# 88| mu88_7(int) = Store[y] : &:r88_1, r88_6
|
||||
# 89| r89_1(glval<int>) = VariableAddress[y] :
|
||||
# 89| r89_2(int) = Load[y] : &:r89_1, ~m?
|
||||
# 89| r89_3(int) = Constant[0] :
|
||||
@@ -3605,8 +3606,16 @@ ir.c:
|
||||
# 103| v103_6(void) = NoOp :
|
||||
#-----| Goto -> Block 40
|
||||
|
||||
# 104| Block 40
|
||||
# 104| v104_1(void) = NoOp :
|
||||
# 105| Block 40
|
||||
# 105| r105_1(glval<int>) = VariableAddress[double_negation] :
|
||||
# 105| r105_2(glval<int>) = VariableAddress[x1] :
|
||||
# 105| r105_3(int) = Load[x1] : &:r105_2, ~m?
|
||||
# 105| r105_4(int) = Constant[0] :
|
||||
# 105| r105_5(bool) = CompareEQ : r105_3, r105_4
|
||||
# 105| r105_6(bool) = LogicalNot : r105_5
|
||||
# 105| r105_7(int) = Convert : r105_6
|
||||
# 105| mu105_8(int) = Store[double_negation] : &:r105_1, r105_7
|
||||
# 106| v106_1(void) = NoOp :
|
||||
# 84| v84_8(void) = ReturnVoid :
|
||||
# 84| v84_9(void) = AliasedUse : ~m?
|
||||
# 84| v84_10(void) = ExitFunction :
|
||||
|
||||
@@ -1145,3 +1145,43 @@ test.cpp:
|
||||
# 152| v152_7(void) = ReturnVoid :
|
||||
# 152| v152_8(void) = AliasedUse : ~m156_7
|
||||
# 152| v152_9(void) = ExitFunction :
|
||||
|
||||
# 166| void test_constMemberFunction()
|
||||
# 166| Block 0
|
||||
# 166| v166_1(void) = EnterFunction :
|
||||
# 166| m166_2(unknown) = AliasedDefinition :
|
||||
# 166| valnum = unique
|
||||
# 166| m166_3(unknown) = InitializeNonLocal :
|
||||
# 166| valnum = unique
|
||||
# 166| m166_4(unknown) = Chi : total:m166_2, partial:m166_3
|
||||
# 166| valnum = unique
|
||||
# 167| r167_1(glval<StructWithConstMemberFunction>) = VariableAddress[s] :
|
||||
# 167| valnum = r167_1, r168_2, r169_1, r169_2
|
||||
# 167| m167_2(StructWithConstMemberFunction) = Uninitialized[s] : &:r167_1
|
||||
# 167| valnum = m167_2, m168_4, r168_3
|
||||
# 167| m167_3(unknown) = Chi : total:m166_4, partial:m167_2
|
||||
# 167| valnum = unique
|
||||
# 168| r168_1(glval<StructWithConstMemberFunction>) = VariableAddress[s2] :
|
||||
# 168| valnum = unique
|
||||
# 168| r168_2(glval<StructWithConstMemberFunction>) = VariableAddress[s] :
|
||||
# 168| valnum = r167_1, r168_2, r169_1, r169_2
|
||||
# 168| r168_3(StructWithConstMemberFunction) = Load[s] : &:r168_2, m167_2
|
||||
# 168| valnum = m167_2, m168_4, r168_3
|
||||
# 168| m168_4(StructWithConstMemberFunction) = Store[s2] : &:r168_1, r168_3
|
||||
# 168| valnum = m167_2, m168_4, r168_3
|
||||
# 169| r169_1(glval<StructWithConstMemberFunction>) = VariableAddress[s] :
|
||||
# 169| valnum = r167_1, r168_2, r169_1, r169_2
|
||||
# 169| r169_2(glval<StructWithConstMemberFunction>) = Convert : r169_1
|
||||
# 169| valnum = r167_1, r168_2, r169_1, r169_2
|
||||
# 169| r169_3(glval<unknown>) = FunctionAddress[constMemberFunction] :
|
||||
# 169| valnum = unique
|
||||
# 169| v169_4(void) = Call[constMemberFunction] : func:r169_3, this:r169_2
|
||||
# 169| m169_5(unknown) = ^CallSideEffect : ~m167_3
|
||||
# 169| valnum = unique
|
||||
# 169| m169_6(unknown) = Chi : total:m167_3, partial:m169_5
|
||||
# 169| valnum = unique
|
||||
# 169| v169_7(void) = ^IndirectReadSideEffect[-1] : &:r169_2, ~m169_6
|
||||
# 170| v170_1(void) = NoOp :
|
||||
# 166| v166_5(void) = ReturnVoid :
|
||||
# 166| v166_6(void) = AliasedUse : ~m169_6
|
||||
# 166| v166_7(void) = ExitFunction :
|
||||
|
||||
@@ -157,3 +157,14 @@ void test_read_global_different(int n) {
|
||||
|
||||
int d = global_a->x;
|
||||
}
|
||||
|
||||
struct StructWithConstMemberFunction {
|
||||
int x;
|
||||
void constMemberFunction() const;
|
||||
};
|
||||
|
||||
void test_constMemberFunction() {
|
||||
StructWithConstMemberFunction s;
|
||||
StructWithConstMemberFunction s2 = s;
|
||||
s.constMemberFunction();
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
| test.cpp:49:2:49:9 | call to strcpy_s | Potentially unsafe call to strcpy_s; second argument should be size of destination. |
|
||||
| test.cpp:62:3:62:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
| test.cpp:65:3:65:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
| test.cpp:70:2:70:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
| test.cpp:81:3:81:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
| test.cpp:84:3:84:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
| test.cpp:105:2:105:10 | call to wcsxfrm_l | Potentially unsafe call to wcsxfrm_l; third argument should be size of destination. |
|
||||
|
||||
@@ -27,8 +27,8 @@ edges
|
||||
| main.cpp:9:29:9:32 | *argv | tests_restrict.c:15:41:15:44 | *argv | provenance | |
|
||||
| main.cpp:9:29:9:32 | tests_restrict_main output argument | main.cpp:10:20:10:23 | **argv | provenance | |
|
||||
| main.cpp:9:29:9:32 | tests_restrict_main output argument | main.cpp:10:20:10:23 | *argv | provenance | |
|
||||
| main.cpp:10:20:10:23 | **argv | tests.cpp:1060:32:1060:35 | **argv | provenance | |
|
||||
| main.cpp:10:20:10:23 | *argv | tests.cpp:1060:32:1060:35 | *argv | provenance | |
|
||||
| main.cpp:10:20:10:23 | **argv | tests.cpp:1074:32:1074:35 | **argv | provenance | |
|
||||
| main.cpp:10:20:10:23 | *argv | tests.cpp:1074:32:1074:35 | *argv | provenance | |
|
||||
| overflowdestination.cpp:23:45:23:48 | **argv | overflowdestination.cpp:23:45:23:48 | **argv | provenance | |
|
||||
| overflowdestination.cpp:23:45:23:48 | **argv | overflowdestination.cpp:23:45:23:48 | *argv | provenance | |
|
||||
| test_buffer_overrun.cpp:32:46:32:49 | **argv | test_buffer_overrun.cpp:32:46:32:49 | **argv | provenance | |
|
||||
@@ -41,12 +41,12 @@ edges
|
||||
| tests.cpp:649:14:649:14 | *s [*home] | tests.cpp:649:14:649:19 | *home | provenance | |
|
||||
| tests.cpp:649:14:649:14 | *s [*home] | tests.cpp:649:16:649:19 | *home | provenance | |
|
||||
| tests.cpp:649:16:649:19 | *home | tests.cpp:649:14:649:19 | *home | provenance | |
|
||||
| tests.cpp:1060:32:1060:35 | **argv | tests.cpp:1085:9:1085:15 | *access to array | provenance | |
|
||||
| tests.cpp:1060:32:1060:35 | **argv | tests.cpp:1086:9:1086:15 | *access to array | provenance | |
|
||||
| tests.cpp:1060:32:1060:35 | *argv | tests.cpp:1085:9:1085:15 | *access to array | provenance | |
|
||||
| tests.cpp:1060:32:1060:35 | *argv | tests.cpp:1086:9:1086:15 | *access to array | provenance | |
|
||||
| tests.cpp:1085:9:1085:15 | *access to array | tests.cpp:634:19:634:24 | *source | provenance | |
|
||||
| tests.cpp:1086:9:1086:15 | *access to array | tests.cpp:643:19:643:24 | *source | provenance | |
|
||||
| tests.cpp:1074:32:1074:35 | **argv | tests.cpp:1099:9:1099:15 | *access to array | provenance | |
|
||||
| tests.cpp:1074:32:1074:35 | **argv | tests.cpp:1100:9:1100:15 | *access to array | provenance | |
|
||||
| tests.cpp:1074:32:1074:35 | *argv | tests.cpp:1099:9:1099:15 | *access to array | provenance | |
|
||||
| tests.cpp:1074:32:1074:35 | *argv | tests.cpp:1100:9:1100:15 | *access to array | provenance | |
|
||||
| tests.cpp:1099:9:1099:15 | *access to array | tests.cpp:634:19:634:24 | *source | provenance | |
|
||||
| tests.cpp:1100:9:1100:15 | *access to array | tests.cpp:643:19:643:24 | *source | provenance | |
|
||||
| tests_restrict.c:15:41:15:44 | **argv | tests_restrict.c:15:41:15:44 | **argv | provenance | |
|
||||
| tests_restrict.c:15:41:15:44 | *argv | tests_restrict.c:15:41:15:44 | *argv | provenance | |
|
||||
nodes
|
||||
@@ -80,10 +80,10 @@ nodes
|
||||
| tests.cpp:649:14:649:14 | *s [*home] | semmle.label | *s [*home] |
|
||||
| tests.cpp:649:14:649:19 | *home | semmle.label | *home |
|
||||
| tests.cpp:649:16:649:19 | *home | semmle.label | *home |
|
||||
| tests.cpp:1060:32:1060:35 | **argv | semmle.label | **argv |
|
||||
| tests.cpp:1060:32:1060:35 | *argv | semmle.label | *argv |
|
||||
| tests.cpp:1085:9:1085:15 | *access to array | semmle.label | *access to array |
|
||||
| tests.cpp:1086:9:1086:15 | *access to array | semmle.label | *access to array |
|
||||
| tests.cpp:1074:32:1074:35 | **argv | semmle.label | **argv |
|
||||
| tests.cpp:1074:32:1074:35 | *argv | semmle.label | *argv |
|
||||
| tests.cpp:1099:9:1099:15 | *access to array | semmle.label | *access to array |
|
||||
| tests.cpp:1100:9:1100:15 | *access to array | semmle.label | *access to array |
|
||||
| tests_restrict.c:15:41:15:44 | **argv | semmle.label | **argv |
|
||||
| tests_restrict.c:15:41:15:44 | **argv | semmle.label | **argv |
|
||||
| tests_restrict.c:15:41:15:44 | *argv | semmle.label | *argv |
|
||||
|
||||
@@ -1057,6 +1057,20 @@ void test30() {
|
||||
strncpy(us.buffer2, "", sizeof(us) - 1); // BAD
|
||||
}
|
||||
|
||||
struct S_Size16 {
|
||||
unsigned short uint16;
|
||||
unsigned char uint8;
|
||||
unsigned char raw[13];
|
||||
};
|
||||
|
||||
void test31() {
|
||||
S_Size16 e;
|
||||
|
||||
[&e](void* data){
|
||||
memcpy(&e, data, sizeof(e)); // GOOD
|
||||
};
|
||||
}
|
||||
|
||||
int tests_main(int argc, char *argv[])
|
||||
{
|
||||
long long arr17[19];
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
edges
|
||||
| globalVars.c:8:7:8:10 | **copy | globalVars.c:8:7:8:10 | **copy | provenance | |
|
||||
| globalVars.c:8:7:8:10 | **copy | globalVars.c:27:9:27:12 | *copy | provenance | |
|
||||
| globalVars.c:8:7:8:10 | **copy | globalVars.c:30:15:30:18 | *copy | provenance | |
|
||||
| globalVars.c:8:7:8:10 | **copy | globalVars.c:30:15:30:18 | *copy | provenance | |
|
||||
| globalVars.c:8:7:8:10 | **copy | globalVars.c:35:11:35:14 | *copy | provenance | |
|
||||
| globalVars.c:9:7:9:11 | **copy2 | globalVars.c:9:7:9:11 | **copy2 | provenance | |
|
||||
| globalVars.c:9:7:9:11 | **copy2 | globalVars.c:38:9:38:13 | *copy2 | provenance | |
|
||||
| globalVars.c:9:7:9:11 | **copy2 | globalVars.c:41:15:41:19 | *copy2 | provenance | |
|
||||
| globalVars.c:9:7:9:11 | **copy2 | globalVars.c:41:15:41:19 | *copy2 | provenance | |
|
||||
| globalVars.c:9:7:9:11 | **copy2 | globalVars.c:50:9:50:13 | *copy2 | provenance | |
|
||||
| globalVars.c:11:22:11:25 | **argv | globalVars.c:12:2:12:15 | *... = ... | provenance | |
|
||||
| globalVars.c:12:2:12:15 | *... = ... | globalVars.c:8:7:8:10 | **copy | provenance | |
|
||||
| globalVars.c:15:21:15:23 | *val | globalVars.c:15:21:15:23 | *val | provenance | |
|
||||
| globalVars.c:15:21:15:23 | *val | globalVars.c:16:2:16:12 | *... = ... | provenance | |
|
||||
| globalVars.c:16:2:16:12 | *... = ... | globalVars.c:9:7:9:11 | **copy2 | provenance | |
|
||||
| globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | *str | provenance | |
|
||||
@@ -16,10 +19,14 @@ edges
|
||||
| globalVars.c:24:11:24:14 | **argv | globalVars.c:11:22:11:25 | **argv | provenance | |
|
||||
| globalVars.c:30:15:30:18 | *copy | globalVars.c:19:25:19:27 | *str | provenance | |
|
||||
| globalVars.c:30:15:30:18 | *copy | globalVars.c:30:15:30:18 | printWrapper output argument | provenance | |
|
||||
| globalVars.c:30:15:30:18 | printWrapper output argument | globalVars.c:8:7:8:10 | **copy | provenance | |
|
||||
| globalVars.c:30:15:30:18 | printWrapper output argument | globalVars.c:35:11:35:14 | *copy | provenance | |
|
||||
| globalVars.c:35:11:35:14 | *copy | globalVars.c:15:21:15:23 | *val | provenance | |
|
||||
| globalVars.c:35:11:35:14 | *copy | globalVars.c:35:11:35:14 | setCopy2 output argument | provenance | |
|
||||
| globalVars.c:35:11:35:14 | setCopy2 output argument | globalVars.c:8:7:8:10 | **copy | provenance | |
|
||||
| globalVars.c:41:15:41:19 | *copy2 | globalVars.c:19:25:19:27 | *str | provenance | |
|
||||
| globalVars.c:41:15:41:19 | *copy2 | globalVars.c:41:15:41:19 | printWrapper output argument | provenance | |
|
||||
| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:9:7:9:11 | **copy2 | provenance | |
|
||||
| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | *copy2 | provenance | |
|
||||
nodes
|
||||
| globalVars.c:8:7:8:10 | **copy | semmle.label | **copy |
|
||||
@@ -27,6 +34,7 @@ nodes
|
||||
| globalVars.c:11:22:11:25 | **argv | semmle.label | **argv |
|
||||
| globalVars.c:12:2:12:15 | *... = ... | semmle.label | *... = ... |
|
||||
| globalVars.c:15:21:15:23 | *val | semmle.label | *val |
|
||||
| globalVars.c:15:21:15:23 | *val | semmle.label | *val |
|
||||
| globalVars.c:16:2:16:12 | *... = ... | semmle.label | *... = ... |
|
||||
| globalVars.c:19:25:19:27 | *str | semmle.label | *str |
|
||||
| globalVars.c:19:25:19:27 | *str | semmle.label | *str |
|
||||
@@ -37,6 +45,7 @@ nodes
|
||||
| globalVars.c:30:15:30:18 | *copy | semmle.label | *copy |
|
||||
| globalVars.c:30:15:30:18 | printWrapper output argument | semmle.label | printWrapper output argument |
|
||||
| globalVars.c:35:11:35:14 | *copy | semmle.label | *copy |
|
||||
| globalVars.c:35:11:35:14 | setCopy2 output argument | semmle.label | setCopy2 output argument |
|
||||
| globalVars.c:38:9:38:13 | *copy2 | semmle.label | *copy2 |
|
||||
| globalVars.c:41:15:41:19 | *copy2 | semmle.label | *copy2 |
|
||||
| globalVars.c:41:15:41:19 | *copy2 | semmle.label | *copy2 |
|
||||
@@ -44,6 +53,7 @@ nodes
|
||||
| globalVars.c:50:9:50:13 | *copy2 | semmle.label | *copy2 |
|
||||
subpaths
|
||||
| globalVars.c:30:15:30:18 | *copy | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | *str | globalVars.c:30:15:30:18 | printWrapper output argument |
|
||||
| globalVars.c:35:11:35:14 | *copy | globalVars.c:15:21:15:23 | *val | globalVars.c:15:21:15:23 | *val | globalVars.c:35:11:35:14 | setCopy2 output argument |
|
||||
| globalVars.c:41:15:41:19 | *copy2 | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | *str | globalVars.c:41:15:41:19 | printWrapper output argument |
|
||||
#select
|
||||
| globalVars.c:27:9:27:12 | *copy | globalVars.c:23:27:23:30 | **argv | globalVars.c:27:9:27:12 | *copy | The value of this argument may come from $@ and is being used as a formatting argument to printf(format). | globalVars.c:23:27:23:30 | **argv | a command-line argument |
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
edges
|
||||
| tests.cpp:62:7:62:18 | **global_token | tests.cpp:62:7:62:18 | **global_token | provenance | |
|
||||
| tests.cpp:62:7:62:18 | **global_token | tests.cpp:69:2:69:43 | *... = ... | provenance | |
|
||||
| tests.cpp:62:7:62:18 | **global_token | tests.cpp:71:27:71:38 | *global_token | provenance | |
|
||||
| tests.cpp:62:22:62:27 | *call to getenv | tests.cpp:62:7:62:18 | **global_token | provenance | |
|
||||
|
||||
@@ -7,9 +7,11 @@ edges
|
||||
| tests2.cpp:49:12:49:12 | call to SAXParser | tests2.cpp:51:2:51:2 | *p | provenance | |
|
||||
| tests3.cpp:23:21:23:53 | *call to createXMLReader | tests3.cpp:23:21:23:53 | *call to createXMLReader | provenance | |
|
||||
| tests3.cpp:23:21:23:53 | *call to createXMLReader | tests3.cpp:25:2:25:2 | *p | provenance | |
|
||||
| tests3.cpp:35:16:35:20 | **p_3_3 | tests3.cpp:35:16:35:20 | **p_3_3 | provenance | |
|
||||
| tests3.cpp:35:16:35:20 | **p_3_3 | tests3.cpp:38:2:38:6 | *p_3_3 | provenance | |
|
||||
| tests3.cpp:35:24:35:56 | *call to createXMLReader | tests3.cpp:35:16:35:20 | **p_3_3 | provenance | |
|
||||
| tests3.cpp:35:24:35:56 | *call to createXMLReader | tests3.cpp:35:24:35:56 | *call to createXMLReader | provenance | |
|
||||
| tests3.cpp:48:16:48:20 | **p_3_5 | tests3.cpp:48:16:48:20 | **p_3_5 | provenance | |
|
||||
| tests3.cpp:48:16:48:20 | **p_3_5 | tests3.cpp:56:2:56:6 | *p_3_5 | provenance | |
|
||||
| tests3.cpp:48:24:48:56 | *call to createXMLReader | tests3.cpp:48:16:48:20 | **p_3_5 | provenance | |
|
||||
| tests3.cpp:48:24:48:56 | *call to createXMLReader | tests3.cpp:48:24:48:56 | *call to createXMLReader | provenance | |
|
||||
@@ -26,6 +28,7 @@ edges
|
||||
| tests5.cpp:55:25:55:38 | *call to createLSParser | tests5.cpp:55:25:55:38 | *call to createLSParser | provenance | |
|
||||
| tests5.cpp:55:25:55:38 | *call to createLSParser | tests5.cpp:59:2:59:2 | *p | provenance | |
|
||||
| tests5.cpp:55:25:55:38 | *call to createLSParser | tests5.cpp:59:2:59:2 | *p | provenance | Config |
|
||||
| tests5.cpp:63:21:63:24 | **g_p2 | tests5.cpp:63:21:63:24 | **g_p2 | provenance | |
|
||||
| tests5.cpp:63:21:63:24 | **g_p2 | tests5.cpp:77:2:77:5 | *g_p2 | provenance | |
|
||||
| tests5.cpp:70:2:70:32 | *... = ... | tests5.cpp:63:21:63:24 | **g_p2 | provenance | |
|
||||
| tests5.cpp:70:17:70:30 | *call to createLSParser | tests5.cpp:70:2:70:32 | *... = ... | provenance | |
|
||||
|
||||
@@ -201,7 +201,7 @@ class ValueOrRefType extends Type, Attributable, @value_or_ref_type {
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate hasCallable(Callable c) {
|
||||
this.hasMethod(c)
|
||||
this.hasMember(c)
|
||||
or
|
||||
this.hasMember(c.(Accessor).getDeclaration())
|
||||
}
|
||||
|
||||
@@ -230,6 +230,11 @@ blockquote.pull-quote > :last-child {
|
||||
font-family: "monospace";
|
||||
}
|
||||
|
||||
/* Fixes a bug in "Supported languages and frameworks" where footnotes were incorrectly indented */
|
||||
aside .label {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* -- PRINT VIEW ----------------------------------------------------------------------------*/
|
||||
|
||||
@media print {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
def test(codeql, use_java_11, java, android_sdk):
|
||||
def test(codeql, use_java_17, java, android_sdk):
|
||||
codeql.database.create()
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
def test(codeql, use_java_11, java, android_sdk):
|
||||
def test(codeql, use_java_17, java, android_sdk):
|
||||
codeql.database.create()
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
def test(codeql, use_java_11, java, android_sdk, actions_toolchains_file):
|
||||
def test(codeql, use_java_17, java, android_sdk, actions_toolchains_file):
|
||||
codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
def test(codeql, use_java_11, java, android_sdk, actions_toolchains_file):
|
||||
def test(codeql, use_java_17, java, android_sdk, actions_toolchains_file):
|
||||
codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
|
||||
|
||||
4
java/ql/lib/change-notes/2025-07-28-guardwrappers.md
Normal file
4
java/ql/lib/change-notes/2025-07-28-guardwrappers.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Guard implication logic involving wrapper methods has been improved. In particular, this means fewer false positives for `java/dereferenced-value-may-be-null`.
|
||||
@@ -57,7 +57,7 @@ private module Input implements BB::InputSig<Location> {
|
||||
* Holds if `node` represents an exit node to be used when calculating
|
||||
* post dominance.
|
||||
*/
|
||||
predicate nodeIsPostDominanceExit(Node node) { node instanceof ControlFlow::ExitNode }
|
||||
predicate nodeIsPostDominanceExit(Node node) { node instanceof ControlFlow::NormalExitNode }
|
||||
}
|
||||
|
||||
private module BbImpl = BB::Make<Location, Input>;
|
||||
|
||||
@@ -146,6 +146,8 @@ private module GuardsInput implements SharedGuards::InputSig<Location> {
|
||||
|
||||
class ControlFlowNode = J::ControlFlowNode;
|
||||
|
||||
class NormalExitNode = ControlFlow::NormalExitNode;
|
||||
|
||||
class BasicBlock = J::BasicBlock;
|
||||
|
||||
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { J::dominatingEdge(bb1, bb2) }
|
||||
@@ -322,6 +324,55 @@ private module GuardsInput implements SharedGuards::InputSig<Location> {
|
||||
|
||||
Expr getElse() { result = super.getFalseExpr() }
|
||||
}
|
||||
|
||||
class Parameter = J::Parameter;
|
||||
|
||||
private int parameterPosition() { result in [-1, any(Parameter p).getPosition()] }
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition extends int {
|
||||
ParameterPosition() { this = parameterPosition() }
|
||||
}
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition extends int {
|
||||
ArgumentPosition() { this = parameterPosition() }
|
||||
}
|
||||
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
overlay[caller?]
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
final private class FinalMethod = Method;
|
||||
|
||||
class NonOverridableMethod extends FinalMethod {
|
||||
NonOverridableMethod() { not super.isOverridable() }
|
||||
|
||||
Parameter getParameter(ParameterPosition ppos) {
|
||||
super.getParameter(ppos) = result and
|
||||
not result.isVarargs()
|
||||
}
|
||||
|
||||
GuardsInput::Expr getAReturnExpr() {
|
||||
exists(ReturnStmt ret |
|
||||
this = ret.getEnclosingCallable() and
|
||||
ret.getResult() = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate nonOverridableMethodCall(MethodCall call, NonOverridableMethod m) {
|
||||
call.getMethod().getSourceDeclaration() = m
|
||||
}
|
||||
|
||||
class NonOverridableMethodCall extends GuardsInput::Expr instanceof MethodCall {
|
||||
NonOverridableMethodCall() { nonOverridableMethodCall(this, _) }
|
||||
|
||||
NonOverridableMethod getMethod() { nonOverridableMethodCall(this, result) }
|
||||
|
||||
GuardsInput::Expr getArgument(ArgumentPosition apos) { result = super.getArgument(apos) }
|
||||
}
|
||||
}
|
||||
|
||||
private module GuardsImpl = SharedGuards::Make<Location, GuardsInput>;
|
||||
@@ -340,6 +391,17 @@ private module LogicInputCommon {
|
||||
NullGuards::nullCheckMethod(call.getMethod(), val.asBooleanValue(), isNull)
|
||||
)
|
||||
}
|
||||
|
||||
predicate additionalImpliesStep(
|
||||
GuardsImpl::PreGuard g1, GuardValue v1, GuardsImpl::PreGuard g2, GuardValue v2
|
||||
) {
|
||||
exists(MethodCall check, int argIndex |
|
||||
g1 = check and
|
||||
v1.getDualValue().isThrowsException() and
|
||||
conditionCheckArgument(check, argIndex, v2.asBooleanValue()) and
|
||||
g2 = check.getArgument(argIndex)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module LogicInput_v1 implements GuardsImpl::LogicInputSig {
|
||||
@@ -364,18 +426,13 @@ private module LogicInput_v1 implements GuardsImpl::LogicInputSig {
|
||||
}
|
||||
}
|
||||
|
||||
predicate parameterDefinition(Parameter p, SsaDefinition def) {
|
||||
def.(BaseSsaImplicitInit).isParameterDefinition(p)
|
||||
}
|
||||
|
||||
predicate additionalNullCheck = LogicInputCommon::additionalNullCheck/4;
|
||||
|
||||
predicate additionalImpliesStep(
|
||||
GuardsImpl::PreGuard g1, GuardValue v1, GuardsImpl::PreGuard g2, GuardValue v2
|
||||
) {
|
||||
exists(MethodCall check, int argIndex |
|
||||
g1 = check and
|
||||
v1.getDualValue().isThrowsException() and
|
||||
conditionCheckArgument(check, argIndex, v2.asBooleanValue()) and
|
||||
g2 = check.getArgument(argIndex)
|
||||
)
|
||||
}
|
||||
predicate additionalImpliesStep = LogicInputCommon::additionalImpliesStep/4;
|
||||
}
|
||||
|
||||
private module LogicInput_v2 implements GuardsImpl::LogicInputSig {
|
||||
@@ -400,15 +457,13 @@ private module LogicInput_v2 implements GuardsImpl::LogicInputSig {
|
||||
}
|
||||
}
|
||||
|
||||
predicate parameterDefinition(Parameter p, SsaDefinition def) {
|
||||
def.(SSA::SsaImplicitInit).isParameterDefinition(p)
|
||||
}
|
||||
|
||||
predicate additionalNullCheck = LogicInputCommon::additionalNullCheck/4;
|
||||
|
||||
predicate additionalImpliesStep(
|
||||
GuardsImpl::PreGuard g1, GuardValue v1, GuardsImpl::PreGuard g2, GuardValue v2
|
||||
) {
|
||||
LogicInput_v1::additionalImpliesStep(g1, v1, g2, v2)
|
||||
or
|
||||
CustomGuard::additionalImpliesStep(g1, v1, g2, v2)
|
||||
}
|
||||
predicate additionalImpliesStep = LogicInputCommon::additionalImpliesStep/4;
|
||||
}
|
||||
|
||||
private module LogicInput_v3 implements GuardsImpl::LogicInputSig {
|
||||
@@ -421,70 +476,11 @@ private module LogicInput_v3 implements GuardsImpl::LogicInputSig {
|
||||
|
||||
predicate additionalNullCheck = LogicInputCommon::additionalNullCheck/4;
|
||||
|
||||
predicate additionalImpliesStep = LogicInput_v2::additionalImpliesStep/4;
|
||||
}
|
||||
|
||||
private module CustomGuardInput implements Guards_v2::CustomGuardInputSig {
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
|
||||
private int parameterPosition() { result in [-1, any(Parameter p).getPosition()] }
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition extends int {
|
||||
ParameterPosition() { this = parameterPosition() }
|
||||
}
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition extends int {
|
||||
ArgumentPosition() { this = parameterPosition() }
|
||||
}
|
||||
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
overlay[caller?]
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
final private class FinalMethod = Method;
|
||||
|
||||
class BooleanMethod extends FinalMethod {
|
||||
BooleanMethod() {
|
||||
super.getReturnType().(PrimitiveType).hasName("boolean") and
|
||||
not super.isOverridable()
|
||||
}
|
||||
|
||||
LogicInput_v2::SsaDefinition getParameter(ParameterPosition ppos) {
|
||||
exists(Parameter p |
|
||||
super.getParameter(ppos) = p and
|
||||
not p.isVarargs() and
|
||||
result.(SsaImplicitInit).isParameterDefinition(p)
|
||||
)
|
||||
}
|
||||
|
||||
GuardsInput::Expr getAReturnExpr() {
|
||||
exists(ReturnStmt ret |
|
||||
this = ret.getEnclosingCallable() and
|
||||
ret.getResult() = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate booleanMethodCall(MethodCall call, BooleanMethod m) {
|
||||
call.getMethod().getSourceDeclaration() = m
|
||||
}
|
||||
|
||||
class BooleanMethodCall extends GuardsInput::Expr instanceof MethodCall {
|
||||
BooleanMethodCall() { booleanMethodCall(this, _) }
|
||||
|
||||
BooleanMethod getMethod() { booleanMethodCall(this, result) }
|
||||
|
||||
GuardsInput::Expr getArgument(ArgumentPosition apos) { result = super.getArgument(apos) }
|
||||
}
|
||||
predicate additionalImpliesStep = LogicInputCommon::additionalImpliesStep/4;
|
||||
}
|
||||
|
||||
class GuardValue = GuardsImpl::GuardValue;
|
||||
|
||||
private module CustomGuard = Guards_v2::CustomGuard<CustomGuardInput>;
|
||||
|
||||
/** INTERNAL: Don't use. */
|
||||
module Guards_v1 = GuardsImpl::Logic<LogicInput_v1>;
|
||||
|
||||
|
||||
@@ -348,6 +348,16 @@ predicate expectsContent(Node n, ContentSet c) {
|
||||
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate numericRepresentative(RefType t) {
|
||||
t.(BoxedType).getPrimitiveType().getName() = "double"
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate booleanRepresentative(RefType t) {
|
||||
t.(BoxedType).getPrimitiveType().getName() = "boolean"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a representative (boxed) type for `t` for the purpose of pruning
|
||||
* possible flow. A single type is used for all numeric types to account for
|
||||
@@ -356,10 +366,10 @@ predicate expectsContent(Node n, ContentSet c) {
|
||||
RefType getErasedRepr(Type t) {
|
||||
exists(Type e | e = t.getErasure() |
|
||||
if e instanceof NumericOrCharType
|
||||
then result.(BoxedType).getPrimitiveType().getName() = "double"
|
||||
then numericRepresentative(result)
|
||||
else
|
||||
if e instanceof BooleanType
|
||||
then result.(BoxedType).getPrimitiveType().getName() = "boolean"
|
||||
then booleanRepresentative(result)
|
||||
else result = e
|
||||
)
|
||||
or
|
||||
|
||||
@@ -214,24 +214,35 @@ private predicate relevantNode(ObjNode n) {
|
||||
exists(ObjNode mid | relevantNode(mid) and objStep(mid, n) and relevantNodeBack(n))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate objStepPruned(ObjNode n1, ObjNode n2) {
|
||||
objStep(n1, n2) and relevantNode(n1) and relevantNode(n2)
|
||||
private newtype TObjFlowNode =
|
||||
TObjNode(ObjNode n) { relevantNode(n) } or
|
||||
TObjType(RefType t) { source(t, _) }
|
||||
|
||||
private predicate objStepPruned(TObjFlowNode node1, TObjFlowNode node2) {
|
||||
exists(ObjNode n1, ObjNode n2 |
|
||||
node1 = TObjNode(n1) and
|
||||
node2 = TObjNode(n2) and
|
||||
objStep(n1, n2)
|
||||
)
|
||||
or
|
||||
exists(RefType t, ObjNode n |
|
||||
node1 = TObjType(t) and
|
||||
node2 = TObjNode(n) and
|
||||
source(t, n)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stepPlus(Node n1, Node n2) = fastTC(objStepPruned/2)(n1, n2)
|
||||
private predicate flowSrc(TObjFlowNode src) { src instanceof TObjType }
|
||||
|
||||
private predicate flowSink(TObjFlowNode sink) { exists(ObjNode n | sink = TObjNode(n) and sink(n)) }
|
||||
|
||||
private predicate stepPlus(TObjFlowNode n1, TObjFlowNode n2) =
|
||||
doublyBoundedFastTC(objStepPruned/2, flowSrc/1, flowSink/1)(n1, n2)
|
||||
|
||||
/**
|
||||
* Holds if the qualifier `n` of an `Object.toString()` call might have type `t`.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate objType(ObjNode n, RefType t) {
|
||||
exists(ObjNode n2 |
|
||||
sink(n) and
|
||||
(stepPlus(n2, n) or n2 = n) and
|
||||
source(t, n2)
|
||||
)
|
||||
}
|
||||
private predicate objType(ObjNode n, RefType t) { stepPlus(TObjType(t), TObjNode(n)) }
|
||||
|
||||
private VirtualMethodCall objectToString(ObjNode n) {
|
||||
result.getQualifier() = n.asExpr() and sink(n)
|
||||
|
||||
@@ -212,26 +212,29 @@ private LocalVariableDecl getCloseableVariable(CloseableInitExpr cie) {
|
||||
/**
|
||||
* A variable on which a "close" method is called, implicitly or explicitly, directly or indirectly.
|
||||
*/
|
||||
private predicate closeCalled(Variable v) {
|
||||
private predicate closeCalled(LocalScopeVariable v) {
|
||||
// `close()` is implicitly called on variables declared or referenced
|
||||
// in the resources clause of try-with-resource statements.
|
||||
exists(TryStmt try | try.getAResourceVariable() = v)
|
||||
or
|
||||
// Otherwise, there should be an explicit call to a method whose name contains "close".
|
||||
exists(MethodCall e |
|
||||
v = getCloseableVariable(_) or v instanceof Parameter or v instanceof LocalVariableDecl
|
||||
|
|
||||
e.getMethod().getName().toLowerCase().matches("%close%") and
|
||||
exists(VarAccess va | va = v.getAnAccess() |
|
||||
e.getQualifier() = va or
|
||||
e.getAnArgument() = va
|
||||
)
|
||||
)
|
||||
or
|
||||
// The "close" call could happen indirectly inside a helper method of unknown name.
|
||||
exists(int i | e.getArgument(i) = v.getAnAccess() |
|
||||
exists(Parameter p, int j | p.getPosition() = j and p.getCallable() = e.getMethod() |
|
||||
closeCalled(p) and i = j
|
||||
exists(Parameter p |
|
||||
closeCalled(p) and p.getAnArgument() = v.getAnAccess() and p.getCallable() instanceof Method
|
||||
)
|
||||
or
|
||||
exists(MethodCall e, int i | e.getArgument(i) = v.getAnAccess() |
|
||||
exists(Parameter p, int j |
|
||||
p.getPosition() = j and p.getCallable() = e.getMethod().getSourceDeclaration()
|
||||
|
|
||||
// The helper method could be iterating over a varargs parameter.
|
||||
exists(EnhancedForStmt for | for.getExpr() = p.getAnAccess() |
|
||||
closeCalled(for.getVariable().getVariable())
|
||||
@@ -240,7 +243,6 @@ private predicate closeCalled(Variable v) {
|
||||
j <= i
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -208,6 +208,12 @@
|
||||
| Test.kt:101:5:103:5 | ... -> ... | Test.kt:101:5:103:5 | <Expr>; |
|
||||
| Test.kt:101:5:103:5 | <Expr>; | Test.kt:100:25:110:1 | { ... } |
|
||||
| Test.kt:102:9:102:25 | throw ... | Test.kt:101:33:103:5 | { ... } |
|
||||
| Test.kt:105:5:109:5 | <Expr>; | Test.kt:100:25:110:1 | { ... } |
|
||||
| Test.kt:105:5:109:5 | <Expr>; | Test.kt:101:5:103:5 | ... -> ... |
|
||||
| Test.kt:105:5:109:5 | <Expr>; | Test.kt:101:5:103:5 | <Expr>; |
|
||||
| Test.kt:105:9:107:5 | ... -> ... | Test.kt:100:25:110:1 | { ... } |
|
||||
| Test.kt:105:9:107:5 | ... -> ... | Test.kt:101:5:103:5 | ... -> ... |
|
||||
| Test.kt:105:9:107:5 | ... -> ... | Test.kt:101:5:103:5 | <Expr>; |
|
||||
| Test.kt:105:9:107:5 | ... -> ... | Test.kt:105:5:109:5 | <Expr>; |
|
||||
| Test.kt:106:9:106:29 | <Expr>; | Test.kt:105:20:107:5 | { ... } |
|
||||
| Test.kt:108:9:108:29 | <Expr>; | Test.kt:107:27:109:5 | { ... } |
|
||||
|
||||
@@ -208,6 +208,12 @@
|
||||
| Test.kt:101:9:103:5 | ... -> ... | Test.kt:100:25:110:1 | { ... } |
|
||||
| Test.kt:101:9:103:5 | ... -> ... | Test.kt:101:5:103:5 | <Expr>; |
|
||||
| Test.kt:102:9:102:25 | throw ... | Test.kt:101:33:103:5 | { ... } |
|
||||
| Test.kt:105:5:109:5 | <Expr>; | Test.kt:100:25:110:1 | { ... } |
|
||||
| Test.kt:105:5:109:5 | <Expr>; | Test.kt:101:5:103:5 | <Expr>; |
|
||||
| Test.kt:105:5:109:5 | <Expr>; | Test.kt:101:9:103:5 | ... -> ... |
|
||||
| Test.kt:105:9:107:5 | ... -> ... | Test.kt:100:25:110:1 | { ... } |
|
||||
| Test.kt:105:9:107:5 | ... -> ... | Test.kt:101:5:103:5 | <Expr>; |
|
||||
| Test.kt:105:9:107:5 | ... -> ... | Test.kt:101:9:103:5 | ... -> ... |
|
||||
| Test.kt:105:9:107:5 | ... -> ... | Test.kt:105:5:109:5 | <Expr>; |
|
||||
| Test.kt:106:9:106:29 | <Expr>; | Test.kt:105:20:107:5 | { ... } |
|
||||
| Test.kt:108:9:108:29 | <Expr>; | Test.kt:107:27:109:5 | { ... } |
|
||||
|
||||
@@ -143,4 +143,73 @@ public class Guards {
|
||||
chk(); // $ guarded=found:true guarded='i < a.length:false'
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean testNotNull1(String input) {
|
||||
return input != null && input.length() > 0;
|
||||
}
|
||||
|
||||
public static boolean testNotNull2(String input) {
|
||||
if (input == null) return false;
|
||||
return input.length() > 0;
|
||||
}
|
||||
|
||||
public static int getNumOrDefault(Integer number) {
|
||||
return number == null ? 0 : number;
|
||||
}
|
||||
|
||||
public static String concatNonNull(String s1, String s2) {
|
||||
if (s1 == null || s2 == null) return null;
|
||||
return s1 + s2;
|
||||
}
|
||||
|
||||
public static Status testEnumWrapper(boolean flag) {
|
||||
return flag ? Status.SUCCESS : Status.FAILURE;
|
||||
}
|
||||
|
||||
enum Status { SUCCESS, FAILURE }
|
||||
|
||||
void testWrappers(String s, Integer i) {
|
||||
if (testNotNull1(s)) {
|
||||
chk(); // $ guarded='s:not null' guarded=testNotNull1(...):true
|
||||
} else {
|
||||
chk(); // $ guarded=testNotNull1(...):false
|
||||
}
|
||||
|
||||
if (testNotNull2(s)) {
|
||||
chk(); // $ guarded='s:not null' guarded=testNotNull2(...):true
|
||||
} else {
|
||||
chk(); // $ guarded=testNotNull2(...):false
|
||||
}
|
||||
|
||||
if (0 == getNumOrDefault(i)) {
|
||||
chk(); // $ guarded='0 == getNumOrDefault(...):true' guarded='getNumOrDefault(...):0'
|
||||
} else {
|
||||
chk(); // $ guarded='0 == getNumOrDefault(...):false' guarded='getNumOrDefault(...):not 0' guarded='i:not 0' guarded='i:not null'
|
||||
}
|
||||
|
||||
if (null == concatNonNull(s, "suffix")) {
|
||||
chk(); // $ guarded='concatNonNull(...):null' guarded='null == concatNonNull(...):true'
|
||||
} else {
|
||||
chk(); // $ guarded='concatNonNull(...):not null' guarded='null == concatNonNull(...):false' guarded='s:not null'
|
||||
}
|
||||
|
||||
switch (testEnumWrapper(g(1))) {
|
||||
case SUCCESS:
|
||||
chk(); // $ guarded='testEnumWrapper(...):SUCCESS' guarded='testEnumWrapper(...):match SUCCESS' guarded=g(1):true
|
||||
break;
|
||||
case FAILURE:
|
||||
chk(); // $ guarded='testEnumWrapper(...):FAILURE' guarded='testEnumWrapper(...):match FAILURE' guarded=g(1):false
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ensureNotNull(Object o) throws Exception {
|
||||
if (o == null) throw new Exception();
|
||||
}
|
||||
|
||||
void testExceptionWrapper(String s) throws Exception {
|
||||
chk(); // nothing guards here
|
||||
ensureNotNull(s);
|
||||
chk(); // $ guarded='ensureNotNull(...):no exception' guarded='s:not null'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,3 +89,28 @@
|
||||
| Guards.java:139:9:139:13 | chk(...) | found:true |
|
||||
| Guards.java:143:7:143:11 | chk(...) | 'i < a.length:false' |
|
||||
| Guards.java:143:7:143:11 | chk(...) | found:true |
|
||||
| Guards.java:173:7:173:11 | chk(...) | 's:not null' |
|
||||
| Guards.java:173:7:173:11 | chk(...) | testNotNull1(...):true |
|
||||
| Guards.java:175:7:175:11 | chk(...) | testNotNull1(...):false |
|
||||
| Guards.java:179:7:179:11 | chk(...) | 's:not null' |
|
||||
| Guards.java:179:7:179:11 | chk(...) | testNotNull2(...):true |
|
||||
| Guards.java:181:7:181:11 | chk(...) | testNotNull2(...):false |
|
||||
| Guards.java:185:7:185:11 | chk(...) | '0 == getNumOrDefault(...):true' |
|
||||
| Guards.java:185:7:185:11 | chk(...) | 'getNumOrDefault(...):0' |
|
||||
| Guards.java:187:7:187:11 | chk(...) | '0 == getNumOrDefault(...):false' |
|
||||
| Guards.java:187:7:187:11 | chk(...) | 'getNumOrDefault(...):not 0' |
|
||||
| Guards.java:187:7:187:11 | chk(...) | 'i:not 0' |
|
||||
| Guards.java:187:7:187:11 | chk(...) | 'i:not null' |
|
||||
| Guards.java:191:7:191:11 | chk(...) | 'concatNonNull(...):null' |
|
||||
| Guards.java:191:7:191:11 | chk(...) | 'null == concatNonNull(...):true' |
|
||||
| Guards.java:193:7:193:11 | chk(...) | 'concatNonNull(...):not null' |
|
||||
| Guards.java:193:7:193:11 | chk(...) | 'null == concatNonNull(...):false' |
|
||||
| Guards.java:193:7:193:11 | chk(...) | 's:not null' |
|
||||
| Guards.java:198:9:198:13 | chk(...) | 'testEnumWrapper(...):SUCCESS' |
|
||||
| Guards.java:198:9:198:13 | chk(...) | 'testEnumWrapper(...):match SUCCESS' |
|
||||
| Guards.java:198:9:198:13 | chk(...) | g(1):true |
|
||||
| Guards.java:201:9:201:13 | chk(...) | 'testEnumWrapper(...):FAILURE' |
|
||||
| Guards.java:201:9:201:13 | chk(...) | 'testEnumWrapper(...):match FAILURE' |
|
||||
| Guards.java:201:9:201:13 | chk(...) | g(1):false |
|
||||
| Guards.java:213:5:213:9 | chk(...) | 'ensureNotNull(...):no exception' |
|
||||
| Guards.java:213:5:213:9 | chk(...) | 's:not null' |
|
||||
|
||||
@@ -408,4 +408,32 @@ public class B {
|
||||
x.hashCode(); // NPE
|
||||
}
|
||||
}
|
||||
|
||||
public void corrCondLoop1(boolean a[]) {
|
||||
Object x = new Object();
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
boolean b = a[i];
|
||||
if (b) {
|
||||
x = null;
|
||||
}
|
||||
if (!b) {
|
||||
x.hashCode(); // NPE - false negative
|
||||
}
|
||||
// flow can loop around from one iteration to the next
|
||||
}
|
||||
}
|
||||
|
||||
public void corrCondLoop2(boolean a[]) {
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
// x is local to the loop iteration and thus cannot loop around and reach the sink
|
||||
Object x = new Object();
|
||||
boolean b = a[i];
|
||||
if (b) {
|
||||
x = null;
|
||||
}
|
||||
if (!b) {
|
||||
x.hashCode(); // OK
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ public class C {
|
||||
obj = new Object();
|
||||
} else if (a[i] == 2) {
|
||||
verifyNotNull(obj);
|
||||
obj.hashCode(); // NPE - false positive
|
||||
obj.hashCode(); // OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
| C.java:137:7:137:10 | obj2 | Variable $@ may be null at this access as suggested by $@ null guard. | C.java:131:5:131:23 | Object obj2 | obj2 | C.java:132:9:132:20 | ... != ... | this |
|
||||
| C.java:144:15:144:15 | a | Variable $@ may be null at this access as suggested by $@ null guard. | C.java:141:20:141:26 | a | a | C.java:142:13:142:21 | ... == ... | this |
|
||||
| C.java:188:9:188:11 | obj | Variable $@ may be null at this access because of $@ assignment. | C.java:181:5:181:22 | Object obj | obj | C.java:181:12:181:21 | obj | this |
|
||||
| C.java:207:9:207:11 | obj | Variable $@ may be null at this access because of $@ assignment. | C.java:201:5:201:22 | Object obj | obj | C.java:201:12:201:21 | obj | this |
|
||||
| C.java:219:9:219:10 | o1 | Variable $@ may be null at this access as suggested by $@ null guard. | C.java:212:20:212:28 | o1 | o1 | C.java:213:9:213:18 | ... == ... | this |
|
||||
| C.java:233:7:233:8 | xs | Variable $@ may be null at this access because of $@ assignment. | C.java:231:5:231:56 | int[] xs | xs | C.java:231:11:231:55 | xs | this |
|
||||
| F.java:11:5:11:7 | obj | Variable $@ may be null at this access as suggested by $@ null guard. | F.java:8:18:8:27 | obj | obj | F.java:9:9:9:19 | ... == ... | this |
|
||||
|
||||
@@ -212,15 +212,7 @@ abstract class PersistentWriteAccess extends DataFlow::Node {
|
||||
module Cryptography {
|
||||
private import ConceptsShared::Cryptography as SC
|
||||
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CryptographicOperation::Range` instead.
|
||||
*/
|
||||
class CryptographicOperation extends SC::CryptographicOperation instanceof CryptographicOperation::Range
|
||||
{ }
|
||||
class CryptographicOperation = SC::CryptographicOperation;
|
||||
|
||||
class EncryptionAlgorithm = SC::EncryptionAlgorithm;
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ private import internal.ApiGraphModels as Shared
|
||||
private import internal.ApiGraphModelsSpecific as Specific
|
||||
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate
|
||||
private import semmle.javascript.endpoints.EndpointNaming as EndpointNaming
|
||||
private import semmle.javascript.dataflow.AdditionalFlowSteps
|
||||
private import semmle.javascript.dataflow.AdditionalTaintSteps
|
||||
import Shared::ModelInput as ModelInput
|
||||
import Shared::ModelOutput as ModelOutput
|
||||
|
||||
@@ -87,9 +89,6 @@ private predicate shouldInduceStepsFromSummary(string type, string path) {
|
||||
pragma[nomagic]
|
||||
private predicate relevantInputOutputPath(API::InvokeNode base, AccessPath inputOrOutput) {
|
||||
exists(string type, string input, string output, string path |
|
||||
// If the summary for 'callable' could not be handled as a flow summary, we need to evaluate
|
||||
// its inputs and outputs to a set of nodes, so we can generate steps instead.
|
||||
shouldInduceStepsFromSummary(type, path) and
|
||||
ModelOutput::resolvedSummaryBase(type, path, base) and
|
||||
ModelOutput::relevantSummaryModel(type, path, input, output, _, _) and
|
||||
inputOrOutput = [input, output]
|
||||
@@ -118,22 +117,26 @@ private API::Node getNodeFromInputOutputPath(API::InvokeNode baseNode, AccessPat
|
||||
result = getNodeFromInputOutputPath(baseNode, path, path.getNumToken())
|
||||
}
|
||||
|
||||
private predicate summaryStep(API::Node pred, API::Node succ, string kind) {
|
||||
private predicate summaryStep(API::Node pred, API::Node succ, string kind, boolean shouldInduceSteps) {
|
||||
exists(string type, string path, API::InvokeNode base, AccessPath input, AccessPath output |
|
||||
shouldInduceStepsFromSummary(type, path) and
|
||||
ModelOutput::relevantSummaryModel(type, path, input, output, kind, _) and
|
||||
ModelOutput::resolvedSummaryBase(type, path, base) and
|
||||
pred = getNodeFromInputOutputPath(base, input) and
|
||||
succ = getNodeFromInputOutputPath(base, output)
|
||||
succ = getNodeFromInputOutputPath(base, output) and
|
||||
if shouldInduceStepsFromSummary(type, path)
|
||||
then shouldInduceSteps = true
|
||||
else shouldInduceSteps = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `ModelOutput::summaryStep` but with API nodes mapped to data-flow nodes.
|
||||
*/
|
||||
private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, string kind) {
|
||||
private predicate summaryStepNodes(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string kind, boolean shouldInduceSteps
|
||||
) {
|
||||
exists(API::Node predNode, API::Node succNode |
|
||||
summaryStep(predNode, succNode, kind) and
|
||||
summaryStep(predNode, succNode, kind, shouldInduceSteps) and
|
||||
pred = predNode.asSink() and
|
||||
succ = succNode.asSource()
|
||||
)
|
||||
@@ -142,14 +145,26 @@ private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, str
|
||||
/** Data flow steps induced by summary models of kind `value`. */
|
||||
private class DataFlowStepFromSummary extends DataFlow::SharedFlowStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
summaryStepNodes(pred, succ, "value")
|
||||
summaryStepNodes(pred, succ, "value", true)
|
||||
}
|
||||
}
|
||||
|
||||
private class LegacyDataFlowStepFromSummary extends LegacyFlowStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
summaryStepNodes(pred, succ, "value", false)
|
||||
}
|
||||
}
|
||||
|
||||
/** Taint steps induced by summary models of kind `taint`. */
|
||||
private class TaintStepFromSummary extends TaintTracking::SharedTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
summaryStepNodes(pred, succ, "taint")
|
||||
summaryStepNodes(pred, succ, "taint", true)
|
||||
}
|
||||
}
|
||||
|
||||
private class LegacyTaintStepFromSummary extends LegacyTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
summaryStepNodes(pred, succ, "taint", false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,4 @@
|
||||
legacyDataFlowDifference
|
||||
| test.js:5:30:5:37 | source() | test.js:5:8:5:38 | testlib ... urce()) | only flow with NEW data flow library |
|
||||
| test.js:6:22:6:29 | source() | test.js:6:8:6:30 | preserv ... urce()) | only flow with NEW data flow library |
|
||||
| test.js:7:41:7:48 | source() | test.js:7:8:7:49 | require ... urce()) | only flow with NEW data flow library |
|
||||
| test.js:11:38:11:45 | source() | test.js:11:8:11:55 | testlib ... , 1, 1) | only flow with NEW data flow library |
|
||||
| test.js:13:44:13:51 | source() | test.js:13:8:13:55 | testlib ... e(), 1) | only flow with NEW data flow library |
|
||||
| test.js:17:47:17:54 | source() | test.js:17:8:17:61 | testlib ... , 1, 1) | only flow with NEW data flow library |
|
||||
| test.js:18:50:18:57 | source() | test.js:18:8:18:61 | testlib ... e(), 1) | only flow with NEW data flow library |
|
||||
| test.js:19:53:19:60 | source() | test.js:19:8:19:61 | testlib ... urce()) | only flow with NEW data flow library |
|
||||
| test.js:21:34:21:41 | source() | test.js:21:8:21:51 | testlib ... , 1, 1) | only flow with NEW data flow library |
|
||||
| test.js:22:37:22:44 | source() | test.js:22:8:22:51 | testlib ... , 1, 1) | only flow with NEW data flow library |
|
||||
| test.js:23:40:23:47 | source() | test.js:23:8:23:51 | testlib ... e(), 1) | only flow with NEW data flow library |
|
||||
| test.js:24:43:24:50 | source() | test.js:24:8:24:51 | testlib ... urce()) | only flow with NEW data flow library |
|
||||
| test.js:31:29:31:36 | source() | test.js:32:10:32:10 | y | only flow with NEW data flow library |
|
||||
| test.js:37:29:37:36 | source() | test.js:38:10:38:10 | y | only flow with NEW data flow library |
|
||||
| test.js:43:29:43:36 | source() | test.js:44:10:44:10 | y | only flow with NEW data flow library |
|
||||
| test.js:47:33:47:40 | source() | test.js:49:10:49:13 | this | only flow with NEW data flow library |
|
||||
| test.js:102:16:102:34 | testlib.getSource() | test.js:104:8:104:24 | source.continue() | only flow with NEW data flow library |
|
||||
consistencyIssue
|
||||
taintFlow
|
||||
| guardedRouteHandler.js:6:10:6:28 | req.injectedReqData | guardedRouteHandler.js:6:10:6:28 | req.injectedReqData |
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* Made the following changes to `NetHttpRequest`
|
||||
* Adds `connectionNode`, like other Ruby HTTP clients
|
||||
* Makes `requestNode` and `connectionNode` public so subclasses can use them
|
||||
* Adds detection of `Net::HTTP.start`, a common way to make HTTP requests in Ruby
|
||||
@@ -12,38 +12,55 @@ private import codeql.ruby.DataFlow
|
||||
/**
|
||||
* A `Net::HTTP` call which initiates an HTTP request.
|
||||
* ```ruby
|
||||
* # one-off request
|
||||
* Net::HTTP.get("http://example.com/")
|
||||
* Net::HTTP.post("http://example.com/", "some_data")
|
||||
* req = Net::HTTP.new("example.com")
|
||||
* response = req.get("/")
|
||||
*
|
||||
* # connection re-use
|
||||
* Net::HTTP.start("http://example.com") do |http|
|
||||
* http.get("/")
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class NetHttpRequest extends Http::Client::Request::Range instanceof DataFlow::CallNode {
|
||||
private DataFlow::CallNode request;
|
||||
private API::Node requestNode;
|
||||
API::Node requestNode;
|
||||
API::Node connectionNode;
|
||||
private boolean returnsResponseBody;
|
||||
|
||||
NetHttpRequest() {
|
||||
exists(string method |
|
||||
request = requestNode.asSource() and
|
||||
this = request
|
||||
this = request and
|
||||
requestNode = connectionNode.getReturn(method)
|
||||
|
|
||||
// Net::HTTP.get(...)
|
||||
method in ["get", "get_response"] and
|
||||
requestNode = API::getTopLevelMember("Net").getMember("HTTP").getReturn(method) and
|
||||
connectionNode = API::getTopLevelMember("Net").getMember("HTTP") and
|
||||
returnsResponseBody = true
|
||||
or
|
||||
// Net::HTTP.post(...).body
|
||||
method in ["post", "post_form"] and
|
||||
requestNode = API::getTopLevelMember("Net").getMember("HTTP").getReturn(method) and
|
||||
connectionNode = API::getTopLevelMember("Net").getMember("HTTP") and
|
||||
returnsResponseBody = false
|
||||
or
|
||||
// Net::HTTP.new(..).get(..).body
|
||||
// Net::HTTP.start(..) do |http| http.get(..) end
|
||||
method in [
|
||||
"get", "get2", "request_get", "head", "head2", "request_head", "delete", "put", "patch",
|
||||
"post", "post2", "request_post", "request"
|
||||
] and
|
||||
requestNode = API::getTopLevelMember("Net").getMember("HTTP").getInstance().getReturn(method) and
|
||||
connectionNode =
|
||||
[
|
||||
API::getTopLevelMember("Net").getMember("HTTP").getInstance(),
|
||||
API::getTopLevelMember("Net")
|
||||
.getMember("HTTP")
|
||||
.getMethod("start")
|
||||
.getBlock()
|
||||
.getParameter(0)
|
||||
] and
|
||||
returnsResponseBody = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ private module MissingFullAnchorConfig implements DataFlow::ConfigSig {
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() {
|
||||
none() // can't be made diff-informed because the locations of Ruby RegExpTerms aren't correct when the regexp is parsed from a string arising from constant folding
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,8 @@ httpRequests
|
||||
| NetHttp.rb:16:6:16:19 | call to patch |
|
||||
| NetHttp.rb:24:3:24:33 | call to get |
|
||||
| NetHttp.rb:29:1:29:32 | call to post |
|
||||
| NetHttp.rb:33:1:33:22 | call to request |
|
||||
| NetHttp.rb:36:3:36:15 | call to get |
|
||||
| OpenURI.rb:3:9:3:41 | call to open |
|
||||
| OpenURI.rb:6:9:6:34 | call to open |
|
||||
| OpenURI.rb:9:9:9:38 | call to open |
|
||||
@@ -123,6 +125,8 @@ getFramework
|
||||
| NetHttp.rb:16:6:16:19 | call to patch | Net::HTTP |
|
||||
| NetHttp.rb:24:3:24:33 | call to get | Net::HTTP |
|
||||
| NetHttp.rb:29:1:29:32 | call to post | Net::HTTP |
|
||||
| NetHttp.rb:33:1:33:22 | call to request | Net::HTTP |
|
||||
| NetHttp.rb:36:3:36:15 | call to get | Net::HTTP |
|
||||
| OpenURI.rb:3:9:3:41 | call to open | OpenURI |
|
||||
| OpenURI.rb:6:9:6:34 | call to open | OpenURI |
|
||||
| OpenURI.rb:9:9:9:38 | call to open | OpenURI |
|
||||
@@ -292,6 +296,9 @@ getAUrlPart
|
||||
| NetHttp.rb:24:3:24:33 | call to get | NetHttp.rb:24:17:24:22 | domain |
|
||||
| NetHttp.rb:24:3:24:33 | call to get | NetHttp.rb:24:29:24:32 | path |
|
||||
| NetHttp.rb:29:1:29:32 | call to post | NetHttp.rb:29:16:29:18 | uri |
|
||||
| NetHttp.rb:33:1:33:22 | call to request | NetHttp.rb:31:22:31:42 | "https://example.com" |
|
||||
| NetHttp.rb:33:1:33:22 | call to request | NetHttp.rb:33:14:33:21 | root_get |
|
||||
| NetHttp.rb:36:3:36:15 | call to get | NetHttp.rb:36:12:36:14 | "/" |
|
||||
| OpenURI.rb:3:9:3:41 | call to open | OpenURI.rb:3:21:3:40 | "http://example.com" |
|
||||
| OpenURI.rb:6:9:6:34 | call to open | OpenURI.rb:6:14:6:33 | "http://example.com" |
|
||||
| OpenURI.rb:9:9:9:38 | call to open | OpenURI.rb:9:18:9:37 | "http://example.com" |
|
||||
|
||||
@@ -27,3 +27,11 @@ end
|
||||
get("example.com", "/").body
|
||||
|
||||
Net::HTTP.post(uri, "some_body") # note: response body not accessed
|
||||
|
||||
http = Net::HTTP.new("https://example.com")
|
||||
root_get = Net::HTTP::Get.new("/")
|
||||
http.request(root_get)
|
||||
|
||||
Net::HTTP.start("https://example.com") do |http|
|
||||
http.get("/")
|
||||
end
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# This file specifies the Rust version used to develop and test the
|
||||
# extractors written in rust. It is set to the lowest version of Rust
|
||||
# we want to support.
|
||||
# This file specifies the Rust version used to develop the extractors written
|
||||
# in rust. Notice that this is just used by using `cargo`-related tools in a
|
||||
# local dev environment. The actual version used to build the released packs
|
||||
# is specified in `MODULE.bazel` in the internal repository (typically
|
||||
# reflected by `MODULE.bazel` in this repository).
|
||||
|
||||
[toolchain]
|
||||
channel = "1.86"
|
||||
channel = "1.88"
|
||||
profile = "minimal"
|
||||
components = [ "clippy", "rustfmt" ]
|
||||
|
||||
@@ -23,7 +23,7 @@ fn class_name(type_name: &str) -> String {
|
||||
"AsmOptions" => "AsmOptionsList".to_owned(),
|
||||
"MacroStmts" => "MacroBlockExpr".to_owned(),
|
||||
_ if type_name.starts_with("Record") => type_name.replacen("Record", "Struct", 1),
|
||||
_ if type_name.ends_with("Type") => format!("{}Repr", type_name),
|
||||
_ if type_name.ends_with("Type") => format!("{type_name}Repr"),
|
||||
_ => type_name.to_owned(),
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@ fn get_trait_fields(trait_name: &str) -> Vec<FieldInfo> {
|
||||
],
|
||||
"HasArgList" => vec![FieldInfo::optional("arg_list", "ArgList")],
|
||||
"HasDocComments" => vec![],
|
||||
_ => panic!("Unknown trait {}", trait_name),
|
||||
_ => panic!("Unknown trait {trait_name}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ impl<'a> RustAnalyzer<'a> {
|
||||
config: &CargoConfig,
|
||||
load_config: &LoadCargoConfig,
|
||||
) -> Option<(RootDatabase, Vfs)> {
|
||||
let progress = |t| (trace!("progress: {}", t));
|
||||
let progress = |t| trace!("progress: {t}");
|
||||
let manifest = project.manifest_path();
|
||||
match load_workspace_at(manifest.as_ref(), config, load_config, &progress) {
|
||||
Ok((db, vfs, _macro_server)) => Some((db, vfs)),
|
||||
@@ -67,7 +67,7 @@ impl<'a> RustAnalyzer<'a> {
|
||||
fn get_file_data(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<(&Semantics<RootDatabase>, EditionedFileId, FileText), &str> {
|
||||
) -> Result<(&Semantics<'_, RootDatabase>, EditionedFileId, FileText), &str> {
|
||||
match self {
|
||||
RustAnalyzer::WithoutSemantics { reason } => Err(reason),
|
||||
RustAnalyzer::WithSemantics { vfs, semantics } => {
|
||||
@@ -82,7 +82,7 @@ impl<'a> RustAnalyzer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(&self, path: &Path) -> ParseResult {
|
||||
pub fn parse(&self, path: &Path) -> ParseResult<'_> {
|
||||
match self.get_file_data(path) {
|
||||
Ok((semantics, file_id, input)) => {
|
||||
let source_file = semantics.parse(file_id);
|
||||
|
||||
@@ -549,9 +549,7 @@ impl<'a> Translator<'a> {
|
||||
.map(|p| format!("[{p}; {size}]"));
|
||||
}
|
||||
if let Some(it) = ty.as_slice() {
|
||||
return self
|
||||
.canonical_path_from_type(it)
|
||||
.map(|p| format!("[{}]", p));
|
||||
return self.canonical_path_from_type(it).map(|p| format!("[{p}]"));
|
||||
}
|
||||
if let Some(it) = ty.as_builtin() {
|
||||
return Some(it.name().as_str().to_owned());
|
||||
@@ -651,7 +649,7 @@ impl<'a> Translator<'a> {
|
||||
// if we have a Hir entity, it means we have semantics
|
||||
let sema = self.semantics.as_ref().unwrap();
|
||||
match item.origin(sema.db) {
|
||||
CrateOrigin::Rustc { name } => format!("rustc:{}", name),
|
||||
CrateOrigin::Rustc { name } => format!("rustc:{name}"),
|
||||
CrateOrigin::Local { repo, name } => format!(
|
||||
"repo:{}:{}",
|
||||
repo.unwrap_or_default(),
|
||||
@@ -660,7 +658,7 @@ impl<'a> Translator<'a> {
|
||||
CrateOrigin::Library { repo, name } => {
|
||||
format!("repo:{}:{}", repo.unwrap_or_default(), name)
|
||||
}
|
||||
CrateOrigin::Lang(it) => format!("lang:{}", it),
|
||||
CrateOrigin::Lang(it) => format!("lang:{it}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ pub trait AsTrapKeyPart {
|
||||
|
||||
impl AsTrapKeyPart for UntypedLabel {
|
||||
fn as_key_part(&self) -> String {
|
||||
format!("{{{}}}", self)
|
||||
format!("{{{self}}}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ ql/rust/ql/src/queries/diagnostics/ExtractedFiles.ql
|
||||
ql/rust/ql/src/queries/diagnostics/ExtractionErrors.ql
|
||||
ql/rust/ql/src/queries/diagnostics/ExtractionWarnings.ql
|
||||
ql/rust/ql/src/queries/diagnostics/SsaConsistencyCounts.ql
|
||||
ql/rust/ql/src/queries/diagnostics/TypeInferenceConsistencyCounts.ql
|
||||
ql/rust/ql/src/queries/diagnostics/UnextractedElements.ql
|
||||
ql/rust/ql/src/queries/diagnostics/UnresolvedMacroCalls.ql
|
||||
ql/rust/ql/src/queries/security/CWE-020/RegexInjection.ql
|
||||
@@ -12,6 +13,7 @@ ql/rust/ql/src/queries/security/CWE-022/TaintedPath.ql
|
||||
ql/rust/ql/src/queries/security/CWE-089/SqlInjection.ql
|
||||
ql/rust/ql/src/queries/security/CWE-311/CleartextTransmission.ql
|
||||
ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql
|
||||
ql/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql
|
||||
ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql
|
||||
ql/rust/ql/src/queries/security/CWE-770/UncontrolledAllocationSize.ql
|
||||
|
||||
@@ -5,6 +5,7 @@ ql/rust/ql/src/queries/diagnostics/ExtractedFiles.ql
|
||||
ql/rust/ql/src/queries/diagnostics/ExtractionErrors.ql
|
||||
ql/rust/ql/src/queries/diagnostics/ExtractionWarnings.ql
|
||||
ql/rust/ql/src/queries/diagnostics/SsaConsistencyCounts.ql
|
||||
ql/rust/ql/src/queries/diagnostics/TypeInferenceConsistencyCounts.ql
|
||||
ql/rust/ql/src/queries/diagnostics/UnextractedElements.ql
|
||||
ql/rust/ql/src/queries/diagnostics/UnresolvedMacroCalls.ql
|
||||
ql/rust/ql/src/queries/security/CWE-020/RegexInjection.ql
|
||||
@@ -12,6 +13,7 @@ ql/rust/ql/src/queries/security/CWE-022/TaintedPath.ql
|
||||
ql/rust/ql/src/queries/security/CWE-089/SqlInjection.ql
|
||||
ql/rust/ql/src/queries/security/CWE-311/CleartextTransmission.ql
|
||||
ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql
|
||||
ql/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql
|
||||
ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql
|
||||
ql/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql
|
||||
|
||||
@@ -5,6 +5,7 @@ ql/rust/ql/src/queries/diagnostics/ExtractedFiles.ql
|
||||
ql/rust/ql/src/queries/diagnostics/ExtractionErrors.ql
|
||||
ql/rust/ql/src/queries/diagnostics/ExtractionWarnings.ql
|
||||
ql/rust/ql/src/queries/diagnostics/SsaConsistencyCounts.ql
|
||||
ql/rust/ql/src/queries/diagnostics/TypeInferenceConsistencyCounts.ql
|
||||
ql/rust/ql/src/queries/diagnostics/UnextractedElements.ql
|
||||
ql/rust/ql/src/queries/diagnostics/UnresolvedMacroCalls.ql
|
||||
ql/rust/ql/src/queries/security/CWE-020/RegexInjection.ql
|
||||
@@ -12,6 +13,7 @@ ql/rust/ql/src/queries/security/CWE-022/TaintedPath.ql
|
||||
ql/rust/ql/src/queries/security/CWE-089/SqlInjection.ql
|
||||
ql/rust/ql/src/queries/security/CWE-311/CleartextTransmission.ql
|
||||
ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql
|
||||
ql/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql
|
||||
ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql
|
||||
ql/rust/ql/src/queries/security/CWE-770/UncontrolledAllocationSize.ql
|
||||
|
||||
@@ -4,5 +4,4 @@
|
||||
|
||||
private import codeql.rust.frameworks.rustcrypto.RustCrypto
|
||||
private import codeql.rust.frameworks.Poem
|
||||
private import codeql.rust.frameworks.Sqlx
|
||||
private import codeql.rust.frameworks.stdlib.Stdlib
|
||||
|
||||
@@ -68,9 +68,8 @@ extensible predicate sourceModel(
|
||||
*
|
||||
* For example, `input = Argument[0]` means the first argument of the call.
|
||||
*
|
||||
* The following kinds are supported:
|
||||
*
|
||||
* - `sql-injection`: a flow sink for SQL injection.
|
||||
* The sink kinds supported by queries can be found by searching for uses of
|
||||
* the `sinkNode` predicate.
|
||||
*/
|
||||
extensible predicate sinkModel(
|
||||
string path, string input, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
|
||||
@@ -36,5 +36,36 @@ module Impl {
|
||||
not this.hasGenericParamList() and
|
||||
result = 0
|
||||
}
|
||||
|
||||
private int nrOfDirectTypeBounds() {
|
||||
result = this.getTypeBoundList().getNumberOfBounds()
|
||||
or
|
||||
not this.hasTypeBoundList() and
|
||||
result = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `index`th type bound of this trait, if any.
|
||||
*
|
||||
* This includes type bounds directly on the trait and bounds from any
|
||||
* `where` clauses for `Self`.
|
||||
*/
|
||||
TypeBound getTypeBound(int index) {
|
||||
result = this.getTypeBoundList().getBound(index)
|
||||
or
|
||||
exists(WherePred wp |
|
||||
wp = this.getWhereClause().getAPredicate() and
|
||||
wp.getTypeRepr().(PathTypeRepr).getPath().getText() = "Self" and
|
||||
result = wp.getTypeBoundList().getBound(index - this.nrOfDirectTypeBounds())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type bound of this trait.
|
||||
*
|
||||
* This includes type bounds directly on the trait and bounds from any
|
||||
* `where` clauses for `Self`.
|
||||
*/
|
||||
TypeBound getATypeBound() { result = this.getTypeBound(_) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ private import codeql.rust.elements.internal.generated.TypeParam
|
||||
*/
|
||||
module Impl {
|
||||
private import rust
|
||||
private import codeql.rust.internal.PathResolution
|
||||
|
||||
// the following QLdoc is generated: if you need to edit it, do it in the schema file
|
||||
/**
|
||||
@@ -27,6 +28,41 @@ module Impl {
|
||||
/** Gets the position of this type parameter. */
|
||||
int getPosition() { this = any(GenericParamList l).getTypeParam(result) }
|
||||
|
||||
private TypeBound getTypeBoundAt(int i, int j) {
|
||||
exists(TypeBoundList tbl | result = tbl.getBound(j) |
|
||||
tbl = this.getTypeBoundList() and i = 0
|
||||
or
|
||||
exists(WherePred wp |
|
||||
wp = this.(TypeParamItemNode).getAWherePred() and
|
||||
tbl = wp.getTypeBoundList() and
|
||||
wp = any(WhereClause wc).getPredicate(i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `index`th type bound of this type parameter, if any.
|
||||
*
|
||||
* This includes type bounds directly on this type parameter and bounds from
|
||||
* any `where` clauses for this type parameter.
|
||||
*/
|
||||
TypeBound getTypeBound(int index) {
|
||||
result = rank[index + 1](int i, int j | | this.getTypeBoundAt(i, j) order by i, j)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type bound of this type parameter.
|
||||
*
|
||||
* This includes type bounds directly on this type parameter and bounds from
|
||||
* any `where` clauses for this type parameter.
|
||||
*/
|
||||
TypeBound getATypeBound() {
|
||||
// NOTE: This predicate is used in path resolution, so it can not be
|
||||
// defined using `getTypeBound` as that would cause non-monotonic
|
||||
// recursion due to the `rank`.
|
||||
result = this.getTypeBoundAt(_, _)
|
||||
}
|
||||
|
||||
override string toAbbreviatedString() { result = this.getName().getText() }
|
||||
|
||||
override string toStringImpl() { result = this.getName().getText() }
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
* Provides modeling for the `SQLx` library.
|
||||
*/
|
||||
|
||||
private import rust
|
||||
private import codeql.rust.Concepts
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* A call to `sqlx::query` and variations.
|
||||
*/
|
||||
private class SqlxQuery extends SqlConstruction::Range {
|
||||
CallExpr call;
|
||||
|
||||
SqlxQuery() {
|
||||
this.asExpr().getExpr() = call and
|
||||
call.getFunction().(PathExpr).getResolvedPath() =
|
||||
[
|
||||
"crate::query::query", "crate::query_as::query_as", "crate::query_with::query_with",
|
||||
"crate::query_as_with::query_as_with", "crate::query_scalar::query_scalar",
|
||||
"crate::query_scalar_with::query_scalar_with", "crate::raw_sql::raw_sql"
|
||||
]
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result.asExpr().getExpr() = call.getArgList().getArg(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `sqlx::Executor::execute`.
|
||||
*/
|
||||
private class SqlxExecute extends SqlExecution::Range {
|
||||
MethodCallExpr call;
|
||||
|
||||
SqlxExecute() {
|
||||
this.asExpr().getExpr() = call and
|
||||
call.(Resolvable).getResolvedPath() = "crate::executor::Executor::execute"
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result.asExpr().getExpr() = call.getArgList().getArg(0) }
|
||||
}
|
||||
13
rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml
Normal file
13
rust/ql/lib/codeql/rust/frameworks/sqlx.model.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/rust-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["sqlx_core::query::query", "Argument[0]", "sql-injection", "manual"]
|
||||
- ["sqlx_core::query_as::query_as", "Argument[0]", "sql-injection", "manual"]
|
||||
- ["sqlx_core::query_with::query_with", "Argument[0]", "sql-injection", "manual"]
|
||||
- ["sqlx_core::query_as_with::query_as_with", "Argument[0]", "sql-injection", "manual"]
|
||||
- ["sqlx_core::query_scalar::query_scalar", "Argument[0]", "sql-injection", "manual"]
|
||||
- ["sqlx_core::query_scalar_with::query_scalar_with", "Argument[0]", "sql-injection", "manual"]
|
||||
- ["sqlx_core::raw_sql::raw_sql", "Argument[0]", "sql-injection", "manual"]
|
||||
- ["<_ as sqlx_core::executor::Executor>::execute", "Argument[0]", "sql-injection", "manual"]
|
||||
@@ -120,7 +120,7 @@ module Stages {
|
||||
or
|
||||
exists(resolvePath(_))
|
||||
or
|
||||
exists(any(ItemNode i).getASuccessorFull(_))
|
||||
exists(any(ItemNode i).getASuccessor(_))
|
||||
or
|
||||
exists(any(ItemNode i).getASuccessorRec(_))
|
||||
or
|
||||
|
||||
@@ -194,7 +194,7 @@ abstract class ItemNode extends Locatable {
|
||||
* both are included
|
||||
*/
|
||||
cached
|
||||
ItemNode getASuccessorFull(string name) {
|
||||
ItemNode getASuccessor(string name) {
|
||||
Stages::PathResolutionStage::ref() and
|
||||
result = this.getASuccessorRec(name)
|
||||
or
|
||||
@@ -261,27 +261,6 @@ abstract class ItemNode extends Locatable {
|
||||
item instanceof TypeParamItemNode
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSourceFunction(string name) {
|
||||
this.getASuccessorFull(name).(Function).fromSource()
|
||||
}
|
||||
|
||||
/** Gets a successor named `name` of this item, if any. */
|
||||
pragma[nomagic]
|
||||
ItemNode getASuccessor(string name) {
|
||||
result = this.getASuccessorFull(name) and
|
||||
(
|
||||
// when a function exists in both source code and in library code, it is because
|
||||
// we also extracted the source code as library code, and hence we only want
|
||||
// the function from source code
|
||||
result.fromSource()
|
||||
or
|
||||
not result instanceof Function
|
||||
or
|
||||
not this.hasSourceFunction(name)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this item has a canonical path belonging to the crate `c`. */
|
||||
abstract predicate hasCanonicalPath(Crate c);
|
||||
|
||||
@@ -345,7 +324,7 @@ abstract private class ModuleLikeNode extends ItemNode {
|
||||
private class SourceFileItemNode extends ModuleLikeNode, SourceFile {
|
||||
pragma[nomagic]
|
||||
ModuleLikeNode getSuper() {
|
||||
result = any(ModuleItemNode mod | fileImport(mod, this)).getASuccessorFull("super")
|
||||
result = any(ModuleItemNode mod | fileImport(mod, this)).getASuccessor("super")
|
||||
}
|
||||
|
||||
override string getName() { result = "(source file)" }
|
||||
@@ -393,7 +372,7 @@ class CrateItemNode extends ItemNode instanceof Crate {
|
||||
predicate isPotentialDollarCrateTarget() {
|
||||
exists(string name, RelevantPath p |
|
||||
p.isDollarCrateQualifiedPath(name) and
|
||||
exists(this.getASuccessorFull(name))
|
||||
exists(this.getASuccessor(name))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -791,9 +770,7 @@ private class StructItemNode extends TypeItemNode instanceof Struct {
|
||||
|
||||
class TraitItemNode extends ImplOrTraitItemNode, TypeItemNode instanceof Trait {
|
||||
pragma[nomagic]
|
||||
Path getABoundPath() {
|
||||
result = super.getTypeBoundList().getABound().getTypeRepr().(PathTypeRepr).getPath()
|
||||
}
|
||||
Path getABoundPath() { result = super.getATypeBound().getTypeRepr().(PathTypeRepr).getPath() }
|
||||
|
||||
pragma[nomagic]
|
||||
ItemNode resolveABound() { result = resolvePath(this.getABoundPath()) }
|
||||
@@ -924,7 +901,8 @@ private class BlockExprItemNode extends ItemNode instanceof BlockExpr {
|
||||
}
|
||||
|
||||
class TypeParamItemNode extends TypeItemNode instanceof TypeParam {
|
||||
private WherePred getAWherePred() {
|
||||
/** Gets a where predicate for this type parameter, if any */
|
||||
WherePred getAWherePred() {
|
||||
exists(ItemNode declaringItem |
|
||||
this = resolveTypeParamPathTypeRepr(result.getTypeRepr()) and
|
||||
result = declaringItem.getADescendant() and
|
||||
@@ -933,13 +911,7 @@ class TypeParamItemNode extends TypeItemNode instanceof TypeParam {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
Path getABoundPath() {
|
||||
exists(TypeBoundList tbl | result = tbl.getABound().getTypeRepr().(PathTypeRepr).getPath() |
|
||||
tbl = super.getTypeBoundList()
|
||||
or
|
||||
tbl = this.getAWherePred().getTypeBoundList()
|
||||
)
|
||||
}
|
||||
Path getABoundPath() { result = super.getATypeBound().getTypeRepr().(PathTypeRepr).getPath() }
|
||||
|
||||
pragma[nomagic]
|
||||
ItemNode resolveABound() { result = resolvePath(this.getABoundPath()) }
|
||||
@@ -956,12 +928,7 @@ class TypeParamItemNode extends TypeItemNode instanceof TypeParam {
|
||||
* ```
|
||||
*/
|
||||
cached
|
||||
predicate hasTraitBound() {
|
||||
Stages::PathResolutionStage::ref() and
|
||||
exists(this.getABoundPath())
|
||||
or
|
||||
exists(this.getAWherePred())
|
||||
}
|
||||
predicate hasTraitBound() { Stages::PathResolutionStage::ref() and exists(this.getABoundPath()) }
|
||||
|
||||
/**
|
||||
* Holds if this type parameter has no trait bound. Examples:
|
||||
@@ -1257,8 +1224,8 @@ private predicate unqualifiedPathLookup(ItemNode encl, string name, Namespace ns
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ItemNode getASuccessorFull(ItemNode pred, string name, Namespace ns) {
|
||||
result = pred.getASuccessorFull(name) and
|
||||
private ItemNode getASuccessor(ItemNode pred, string name, Namespace ns) {
|
||||
result = pred.getASuccessor(name) and
|
||||
ns = result.getNamespace()
|
||||
}
|
||||
|
||||
@@ -1293,7 +1260,7 @@ private predicate keywordLookup(ItemNode encl, string name, RelevantPath p) {
|
||||
pragma[nomagic]
|
||||
private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns) {
|
||||
exists(ItemNode encl, string name |
|
||||
result = getASuccessorFull(encl, name, ns) and not encl.excludedLocally(name, result)
|
||||
result = getASuccessor(encl, name, ns) and not encl.excludedLocally(name, result)
|
||||
|
|
||||
unqualifiedPathLookup(encl, name, ns, p)
|
||||
or
|
||||
@@ -1318,7 +1285,7 @@ private ItemNode resolvePath0(RelevantPath path, Namespace ns) {
|
||||
or
|
||||
exists(ItemNode q, string name |
|
||||
q = resolvePathQualifier(path, name) and
|
||||
result = getASuccessorFull(q, name, ns) and
|
||||
result = getASuccessor(q, name, ns) and
|
||||
not q.excludedExternally(name, result)
|
||||
)
|
||||
or
|
||||
@@ -1395,12 +1362,12 @@ private ItemNode resolveUseTreeListItem(Use use, UseTree tree, RelevantPath path
|
||||
mid = resolveUseTreeListItem(use, midTree) and
|
||||
tree = midTree.getUseTreeList().getAUseTree() and
|
||||
isUseTreeSubPathUnqualified(tree, path, pragma[only_bind_into](name)) and
|
||||
result = mid.getASuccessorFull(pragma[only_bind_into](name))
|
||||
result = mid.getASuccessor(pragma[only_bind_into](name))
|
||||
)
|
||||
or
|
||||
exists(ItemNode q, string name |
|
||||
q = resolveUseTreeListItemQualifier(use, tree, path, name) and
|
||||
result = q.getASuccessorFull(name)
|
||||
result = q.getASuccessor(name)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1430,7 +1397,7 @@ private predicate useImportEdge(Use use, string name, ItemNode item) {
|
||||
then
|
||||
exists(ItemNode encl, Namespace ns |
|
||||
encl.getADescendant() = use and
|
||||
item = getASuccessorFull(used, name, ns) and
|
||||
item = getASuccessor(used, name, ns) and
|
||||
// glob imports can be shadowed
|
||||
not declares(encl, ns, name) and
|
||||
not name = ["super", "self", "Self", "$crate", "crate"]
|
||||
|
||||
@@ -59,7 +59,7 @@ newtype TType =
|
||||
TSelfTypeParameter(Trait t) or
|
||||
TSliceTypeParameter()
|
||||
|
||||
predicate implTraitTypeParam(ImplTraitTypeRepr implTrait, int i, TypeParam tp) {
|
||||
private predicate implTraitTypeParam(ImplTraitTypeRepr implTrait, int i, TypeParam tp) {
|
||||
implTrait.isInReturnPos() and
|
||||
tp = implTrait.getFunction().getGenericParamList().getTypeParam(i) and
|
||||
// Only include type parameters of the function that occur inside the impl
|
||||
|
||||
@@ -15,6 +15,14 @@ private import codeql.rust.elements.internal.CallExprImpl::Impl as CallExprImpl
|
||||
|
||||
class Type = T::Type;
|
||||
|
||||
private newtype TTypeArgumentPosition =
|
||||
// method type parameters are matched by position instead of by type
|
||||
// parameter entity, to avoid extra recursion through method call resolution
|
||||
TMethodTypeArgumentPosition(int pos) {
|
||||
exists(any(MethodCallExpr mce).getGenericArgList().getTypeArg(pos))
|
||||
} or
|
||||
TTypeParamTypeArgumentPosition(TypeParam tp)
|
||||
|
||||
private module Input1 implements InputSig1<Location> {
|
||||
private import Type as T
|
||||
private import codeql.rust.elements.internal.generated.Raw
|
||||
@@ -26,14 +34,6 @@ private module Input1 implements InputSig1<Location> {
|
||||
|
||||
class TypeAbstraction = T::TypeAbstraction;
|
||||
|
||||
private newtype TTypeArgumentPosition =
|
||||
// method type parameters are matched by position instead of by type
|
||||
// parameter entity, to avoid extra recursion through method call resolution
|
||||
TMethodTypeArgumentPosition(int pos) {
|
||||
exists(any(MethodCallExpr mce).getGenericArgList().getTypeArg(pos))
|
||||
} or
|
||||
TTypeParamTypeArgumentPosition(TypeParam tp)
|
||||
|
||||
class TypeArgumentPosition extends TTypeArgumentPosition {
|
||||
int asMethodTypeArgumentPosition() { this = TMethodTypeArgumentPosition(result) }
|
||||
|
||||
@@ -151,7 +151,7 @@ private module Input2 implements InputSig2 {
|
||||
TypeMention getABaseTypeMention(Type t) { none() }
|
||||
|
||||
TypeMention getATypeParameterConstraint(TypeParameter tp) {
|
||||
result = tp.(TypeParamTypeParameter).getTypeParam().getTypeBoundList().getABound().getTypeRepr()
|
||||
result = tp.(TypeParamTypeParameter).getTypeParam().getATypeBound().getTypeRepr()
|
||||
or
|
||||
result = tp.(SelfTypeParameter).getTrait()
|
||||
or
|
||||
@@ -184,12 +184,12 @@ private module Input2 implements InputSig2 {
|
||||
exists(Trait trait |
|
||||
abs = trait and
|
||||
condition = trait and
|
||||
constraint = trait.getTypeBoundList().getABound().getTypeRepr()
|
||||
constraint = trait.getATypeBound().getTypeRepr()
|
||||
)
|
||||
or
|
||||
// trait bounds on type parameters
|
||||
exists(TypeParam param |
|
||||
abs = param.getTypeBoundList().getABound() and
|
||||
abs = param.getATypeBound() and
|
||||
condition = param and
|
||||
constraint = abs.(TypeBound).getTypeRepr()
|
||||
)
|
||||
@@ -221,7 +221,13 @@ private module M2 = Make2<Input2>;
|
||||
|
||||
private import M2
|
||||
|
||||
module Consistency = M2::Consistency;
|
||||
module Consistency {
|
||||
import M2::Consistency
|
||||
|
||||
query predicate nonUniqueCertainType(AstNode n, TypePath path) {
|
||||
strictcount(CertainTypeInference::inferCertainType(n, path)) > 1
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the type annotation that applies to `n`, if any. */
|
||||
private TypeMention getTypeAnnotation(AstNode n) {
|
||||
@@ -249,6 +255,164 @@ private Type inferAnnotatedType(AstNode n, TypePath path) {
|
||||
result = getTypeAnnotation(n).resolveTypeAt(path)
|
||||
}
|
||||
|
||||
/** Module for inferring certain type information. */
|
||||
private module CertainTypeInference {
|
||||
pragma[nomagic]
|
||||
private predicate callResolvesTo(CallExpr ce, Path p, Function f) {
|
||||
p = CallExprImpl::getFunctionPath(ce) and
|
||||
f = resolvePath(p)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type getCallExprType(
|
||||
CallExpr ce, Path p, CallExprBaseMatchingInput::FunctionDecl f, TypePath tp
|
||||
) {
|
||||
callResolvesTo(ce, p, f) and
|
||||
result = f.getReturnType(tp)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type getCertainCallExprType(CallExpr ce, Path p, TypePath tp) {
|
||||
forex(Function f | callResolvesTo(ce, p, f) | result = getCallExprType(ce, p, f, tp))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private TypePath getPathToImplSelfTypeParam(TypeParam tp) {
|
||||
exists(ImplItemNode impl |
|
||||
tp = impl.getTypeParam(_) and
|
||||
TTypeParamTypeParameter(tp) = impl.(Impl).getSelfTy().(TypeMention).resolveTypeAt(result)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
Type inferCertainCallExprType(CallExpr ce, TypePath path) {
|
||||
exists(Type ty, TypePath prefix, Path p | ty = getCertainCallExprType(ce, p, prefix) |
|
||||
exists(TypePath suffix, TypeParam tp |
|
||||
tp = ty.(TypeParamTypeParameter).getTypeParam() and
|
||||
path = prefix.append(suffix)
|
||||
|
|
||||
// For type parameters of the `impl` block we must resolve their
|
||||
// instantiation from the path. For instance, for `impl<A> for Foo<A>`
|
||||
// and the path `Foo<i64>::bar` we must resolve `A` to `i64`.
|
||||
exists(TypePath pathToTp |
|
||||
pathToTp = getPathToImplSelfTypeParam(tp) and
|
||||
result = p.getQualifier().(TypeMention).resolveTypeAt(pathToTp.appendInverse(suffix))
|
||||
)
|
||||
or
|
||||
// For type parameters of the function we must resolve their
|
||||
// instantiation from the path. For instance, for `fn bar<A>(a: A) -> A`
|
||||
// and the path `bar<i64>`, we must resolve `A` to `i64`.
|
||||
result =
|
||||
ce.(CallExprBaseMatchingInput::Access)
|
||||
.getTypeArgument(TTypeParamTypeArgumentPosition(tp), suffix)
|
||||
)
|
||||
or
|
||||
not ty instanceof TypeParameter and
|
||||
result = ty and
|
||||
path = prefix
|
||||
)
|
||||
}
|
||||
|
||||
predicate certainTypeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) {
|
||||
prefix1.isEmpty() and
|
||||
prefix2.isEmpty() and
|
||||
(
|
||||
exists(Variable v | n1 = v.getAnAccess() |
|
||||
n2 = v.getPat().getName() or n2 = v.getParameter().(SelfParam)
|
||||
)
|
||||
or
|
||||
// A `let` statement with a type annotation is a coercion site and hence
|
||||
// is not a certain type equality.
|
||||
exists(LetStmt let | not let.hasTypeRepr() |
|
||||
let.getPat() = n1 and
|
||||
let.getInitializer() = n2
|
||||
)
|
||||
or
|
||||
n1 = n2.(ParenExpr).getExpr()
|
||||
)
|
||||
or
|
||||
n1 =
|
||||
any(IdentPat ip |
|
||||
n2 = ip.getName() and
|
||||
prefix1.isEmpty() and
|
||||
if ip.isRef() then prefix2 = TypePath::singleton(TRefTypeParameter()) else prefix2.isEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type inferCertainTypeEquality(AstNode n, TypePath path) {
|
||||
exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix |
|
||||
result = inferCertainType(n2, prefix2.appendInverse(suffix)) and
|
||||
path = prefix1.append(suffix)
|
||||
|
|
||||
certainTypeEquality(n, prefix1, n2, prefix2)
|
||||
or
|
||||
certainTypeEquality(n2, prefix2, n, prefix1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` has complete and certain type information and if `n` has the
|
||||
* resulting type at `path`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Type inferCertainType(AstNode n, TypePath path) {
|
||||
exists(TypeMention tm |
|
||||
tm = getTypeAnnotation(n) and
|
||||
result = tm.resolveTypeAt(path)
|
||||
)
|
||||
or
|
||||
result = inferCertainCallExprType(n, path)
|
||||
or
|
||||
result = inferCertainTypeEquality(n, path)
|
||||
or
|
||||
result = inferLiteralType(n, path, true)
|
||||
or
|
||||
infersCertainTypeAt(n, path, result.getATypeParameter())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` has complete and certain type information at the type path
|
||||
* `prefix.tp`. This entails that the type at `prefix` must be the type
|
||||
* that declares `tp`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate infersCertainTypeAt(AstNode n, TypePath prefix, TypeParameter tp) {
|
||||
exists(TypePath path |
|
||||
exists(inferCertainType(n, path)) and
|
||||
path.isSnoc(prefix, tp)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` has complete and certain type information at _some_ type path.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate hasInferredCertainType(AstNode n) { exists(inferCertainType(n, _)) }
|
||||
|
||||
/**
|
||||
* Holds if `n` having type `t` at `path` conflicts with certain type information.
|
||||
*/
|
||||
bindingset[n, path, t]
|
||||
pragma[inline_late]
|
||||
predicate certainTypeConflict(AstNode n, TypePath path, Type t) {
|
||||
inferCertainType(n, path) != t
|
||||
or
|
||||
// If we infer that `n` has _some_ type at `T1.T2....Tn`, and we also
|
||||
// know that `n` certainly has type `certainType` at `T1.T2...Ti`, `i <=0 < n`,
|
||||
// then it must be the case that `T(i+1)` is a type parameter of `certainType`,
|
||||
// otherwise there is a conflict.
|
||||
//
|
||||
// Below, `prefix` is `T1.T2...Ti` and `tp` is `T(i+1)`.
|
||||
exists(TypePath prefix, TypePath suffix, TypeParameter tp, Type certainType |
|
||||
path = prefix.appendInverse(suffix) and
|
||||
tp = suffix.getHead() and
|
||||
inferCertainType(n, prefix) = certainType and
|
||||
not certainType.getATypeParameter() = tp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private Type inferLogicalOperationType(AstNode n, TypePath path) {
|
||||
exists(Builtins::BuiltinType t, BinaryLogicalOperation be |
|
||||
n = [be, be.getLhs(), be.getRhs()] and
|
||||
@@ -288,22 +452,16 @@ private Struct getRangeType(RangeExpr re) {
|
||||
* through the type equality.
|
||||
*/
|
||||
private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) {
|
||||
CertainTypeInference::certainTypeEquality(n1, prefix1, n2, prefix2)
|
||||
or
|
||||
prefix1.isEmpty() and
|
||||
prefix2.isEmpty() and
|
||||
(
|
||||
exists(Variable v | n1 = v.getAnAccess() |
|
||||
n2 = v.getPat().getName()
|
||||
or
|
||||
n2 = v.getParameter().(SelfParam)
|
||||
)
|
||||
or
|
||||
exists(LetStmt let |
|
||||
let.getPat() = n1 and
|
||||
let.getInitializer() = n2
|
||||
)
|
||||
or
|
||||
n1 = n2.(ParenExpr).getExpr()
|
||||
or
|
||||
n1 = n2.(IfExpr).getABranch()
|
||||
or
|
||||
n1 = n2.(MatchExpr).getAnArm().getExpr()
|
||||
@@ -339,13 +497,6 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
|
||||
n1 = n2.(MacroPat).getMacroCall().getMacroCallExpansion()
|
||||
)
|
||||
or
|
||||
n1 =
|
||||
any(IdentPat ip |
|
||||
n2 = ip.getName() and
|
||||
prefix1.isEmpty() and
|
||||
if ip.isRef() then prefix2 = TypePath::singleton(TRefTypeParameter()) else prefix2.isEmpty()
|
||||
)
|
||||
or
|
||||
(
|
||||
n1 = n2.(RefExpr).getExpr() or
|
||||
n1 = n2.(RefPat).getPat()
|
||||
@@ -716,7 +867,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig {
|
||||
}
|
||||
}
|
||||
|
||||
private class FunctionDecl extends Declaration, Function {
|
||||
additional class FunctionDecl extends Declaration, Function {
|
||||
override TypeParameter getTypeParameter(TypeParameterPosition ppos) {
|
||||
typeParamMatchPosition(this.getGenericParamList().getATypeParam(), result, ppos)
|
||||
or
|
||||
@@ -1180,17 +1331,22 @@ pragma[nomagic]
|
||||
private StructType getStrStruct() { result = TStruct(any(Builtins::Str s)) }
|
||||
|
||||
pragma[nomagic]
|
||||
private Type inferLiteralType(LiteralExpr le, TypePath path) {
|
||||
private Type inferLiteralType(LiteralExpr le, TypePath path, boolean certain) {
|
||||
path.isEmpty() and
|
||||
exists(Builtins::BuiltinType t | result = TStruct(t) |
|
||||
le instanceof CharLiteralExpr and
|
||||
t instanceof Builtins::Char
|
||||
t instanceof Builtins::Char and
|
||||
certain = true
|
||||
or
|
||||
le =
|
||||
any(NumberLiteralExpr ne |
|
||||
t.getName() = ne.getSuffix()
|
||||
t.getName() = ne.getSuffix() and
|
||||
certain = true
|
||||
or
|
||||
// When a number literal has no suffix, the type may depend on the context.
|
||||
// For simplicity, we assume either `i32` or `f64`.
|
||||
not exists(ne.getSuffix()) and
|
||||
certain = false and
|
||||
(
|
||||
ne instanceof IntegerLiteralExpr and
|
||||
t instanceof Builtins::I32
|
||||
@@ -1201,7 +1357,8 @@ private Type inferLiteralType(LiteralExpr le, TypePath path) {
|
||||
)
|
||||
or
|
||||
le instanceof BooleanLiteralExpr and
|
||||
t instanceof Builtins::Bool
|
||||
t instanceof Builtins::Bool and
|
||||
certain = true
|
||||
)
|
||||
or
|
||||
le instanceof StringLiteralExpr and
|
||||
@@ -1210,7 +1367,8 @@ private Type inferLiteralType(LiteralExpr le, TypePath path) {
|
||||
or
|
||||
path = TypePath::singleton(TRefTypeParameter()) and
|
||||
result = getStrStruct()
|
||||
)
|
||||
) and
|
||||
certain = true
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2152,6 +2310,16 @@ private module Cached {
|
||||
cached
|
||||
Type inferType(AstNode n, TypePath path) {
|
||||
Stages::TypeInferenceStage::ref() and
|
||||
result = CertainTypeInference::inferCertainType(n, path)
|
||||
or
|
||||
// Don't propagate type information into a node which conflicts with certain
|
||||
// type information.
|
||||
(
|
||||
if CertainTypeInference::hasInferredCertainType(n)
|
||||
then not CertainTypeInference::certainTypeConflict(n, path, result)
|
||||
else any()
|
||||
) and
|
||||
(
|
||||
result = inferAnnotatedType(n, path)
|
||||
or
|
||||
result = inferLogicalOperationType(n, path)
|
||||
@@ -2182,7 +2350,7 @@ private module Cached {
|
||||
or
|
||||
result = inferTryExprType(n, path)
|
||||
or
|
||||
result = inferLiteralType(n, path)
|
||||
result = inferLiteralType(n, path, false)
|
||||
or
|
||||
result = inferAsyncBlockExprRootType(n) and
|
||||
path.isEmpty()
|
||||
@@ -2208,6 +2376,7 @@ private module Cached {
|
||||
result = inferStructPatType(n, path)
|
||||
or
|
||||
result = inferTupleStructPatType(n, path)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2307,4 +2476,15 @@ private module Debug {
|
||||
c = countTypePaths(n, path, t) and
|
||||
c = max(countTypePaths(_, _, _))
|
||||
}
|
||||
|
||||
Type debugInferCertainType(AstNode n, TypePath path) {
|
||||
n = getRelevantLocatable() and
|
||||
result = CertainTypeInference::inferCertainType(n, path)
|
||||
}
|
||||
|
||||
Type debugInferCertainNonUniqueType(AstNode n, TypePath path) {
|
||||
n = getRelevantLocatable() and
|
||||
Consistency::nonUniqueCertainType(n, path) and
|
||||
result = CertainTypeInference::inferCertainType(n, path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
* Provides classes for recognizing type inference inconsistencies.
|
||||
*/
|
||||
|
||||
private import rust
|
||||
private import Type
|
||||
private import TypeMention
|
||||
private import TypeInference
|
||||
private import TypeInference::Consistency as Consistency
|
||||
import TypeInference::Consistency
|
||||
|
||||
@@ -27,4 +29,7 @@ int getTypeInferenceInconsistencyCounts(string type) {
|
||||
or
|
||||
type = "Ill-formed type mention" and
|
||||
result = count(TypeMention tm | illFormedTypeMention(tm) | tm)
|
||||
or
|
||||
type = "Non-unique certain type information" and
|
||||
result = count(AstNode n, TypePath path | nonUniqueCertainType(n, path) | n)
|
||||
}
|
||||
|
||||
@@ -75,21 +75,7 @@ class SliceTypeReprMention extends TypeMention instanceof SliceTypeRepr {
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `path` is used as a type mention during type inference. */
|
||||
predicate relevantPathTypeMention(Path path) {
|
||||
path =
|
||||
[
|
||||
any(PathTypeRepr r).getPath(),
|
||||
any(StructExpr s).getPath().getQualifier*(),
|
||||
any(CallExpr ce).getFunction().(PathExpr).getPath().getQualifier*(),
|
||||
any(StructPat p).getPath(),
|
||||
any(TupleStructPat p).getPath()
|
||||
]
|
||||
}
|
||||
|
||||
abstract class PathTypeMention extends TypeMention, Path {
|
||||
PathTypeMention() { relevantPathTypeMention(this) }
|
||||
}
|
||||
abstract class PathTypeMention extends TypeMention, Path { }
|
||||
|
||||
class AliasPathTypeMention extends PathTypeMention {
|
||||
TypeAlias resolved;
|
||||
@@ -241,7 +227,8 @@ class NonAliasPathTypeMention extends PathTypeMention {
|
||||
)
|
||||
}
|
||||
|
||||
Type resolveRootType() {
|
||||
pragma[nomagic]
|
||||
private Type resolveRootType() {
|
||||
result = TStruct(resolved)
|
||||
or
|
||||
result = TEnum(resolved)
|
||||
|
||||
@@ -40,6 +40,6 @@ module CleartextLogging {
|
||||
* A sink for logging from model data.
|
||||
*/
|
||||
private class ModelsAsDataSink extends Sink {
|
||||
ModelsAsDataSink() { exists(string s | sinkNode(this, s) and s.matches("log-injection%")) }
|
||||
ModelsAsDataSink() { sinkNode(this, "log-injection") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about cleartext storage
|
||||
* of sensitive information in a database.
|
||||
*/
|
||||
|
||||
import rust
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.dataflow.internal.DataFlowImpl
|
||||
private import codeql.rust.security.SensitiveData
|
||||
private import codeql.rust.Concepts
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and barriers for detecting cleartext storage
|
||||
* of sensitive information in a database, as well as extension points for
|
||||
* adding your own.
|
||||
*/
|
||||
module CleartextStorageDatabase {
|
||||
/**
|
||||
* A data flow source for cleartext storage vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for cleartext storage vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends QuerySink::Range {
|
||||
override string getSinkType() { result = "CleartextStorageDatabase" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A barrier for cleartext storage vulnerabilities.
|
||||
*/
|
||||
abstract class Barrier extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* Sensitive data, considered as a flow source.
|
||||
*/
|
||||
private class SensitiveDataAsSource extends Source instanceof SensitiveData { }
|
||||
|
||||
/**
|
||||
* A sink for cleartext storage vulnerabilities from model data.
|
||||
* - SQL commands
|
||||
* - other database storage operations
|
||||
*/
|
||||
private class ModelsAsDataSink extends Sink {
|
||||
ModelsAsDataSink() { sinkNode(this, ["sql-injection", "database-store"]) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `rust/cleartext-storage-database`, for detecting cases where sensitive information is stored non-encrypted in a database.
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name Type inference inconsistency counts
|
||||
* @description Counts the number of type inference inconsistencies of each type. This query is intended for internal use.
|
||||
* @kind diagnostic
|
||||
* @id rust/diagnostics/type-inference-consistency-counts
|
||||
*/
|
||||
|
||||
private import codeql.rust.internal.TypeInferenceConsistency as Consistency
|
||||
|
||||
// see also `rust/diagnostics/type-inference-consistency`, which lists the
|
||||
// individual inconsistency results.
|
||||
from string type, int num
|
||||
where num = Consistency::getTypeInferenceInconsistencyCounts(type)
|
||||
select type, num
|
||||
@@ -48,6 +48,6 @@ import CleartextTransmissionFlow::PathGraph
|
||||
from CleartextTransmissionFlow::PathNode sourceNode, CleartextTransmissionFlow::PathNode sinkNode
|
||||
where CleartextTransmissionFlow::flowPath(sourceNode, sinkNode)
|
||||
select sinkNode.getNode(), sourceNode, sinkNode,
|
||||
"The operation '" + sinkNode.getNode().toString() +
|
||||
"', transmits data which may contain unencrypted sensitive data from $@.", sourceNode,
|
||||
"This '" + sinkNode.getNode().toString() +
|
||||
"' operation transmits data which may contain unencrypted sensitive data from $@.", sourceNode,
|
||||
sourceNode.getNode().toString()
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Sensitive information that is stored unencrypted in a database is accessible to an attacker
|
||||
who gains access to that database. For example, the information could be accessed by any
|
||||
process or user in a rooted device, or exposed through another vulnerability.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Either encrypt the entire database, or ensure that each piece of sensitive information is
|
||||
encrypted before being stored. In general, decrypt sensitive information only at the point
|
||||
where it is necessary for it to be used in cleartext. Avoid storing sensitive information
|
||||
at all if you do not need to keep it.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example stores sensitive information into a database without encryption, using the
|
||||
SQLx library:
|
||||
</p>
|
||||
<sample src="CleartextStorageDatabaseBad.rs"/>
|
||||
<p>
|
||||
This is insecure because the sensitive data is stored in cleartext, making it accessible to anyone
|
||||
with access to the database.
|
||||
</p>
|
||||
<p>
|
||||
To fix this, we can either encrypt the entire database or encrypt just the sensitive data before it
|
||||
is stored. Take care to select a secure modern encryption algorithm and put suitable key management
|
||||
practices into place. In the following example, we have encrypted the sensitive data using 256-bit
|
||||
AES before storing it in the database:
|
||||
</p>
|
||||
<sample src="CleartextStorageDatabaseGood.rs"/>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
OWASP Top 10:2021:
|
||||
<a href="https://owasp.org/Top10/A02_2021-Cryptographic_Failures/">A02:2021 - Cryptographic Failures</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html">Key Management Cheat Sheet</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @name Cleartext storage of sensitive information in a database
|
||||
* @description Storing sensitive information in a non-encrypted
|
||||
* database can expose it to an attacker.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id rust/cleartext-storage-database
|
||||
* @tags security
|
||||
* external/cwe/cwe-312
|
||||
*/
|
||||
|
||||
import rust
|
||||
import codeql.rust.dataflow.DataFlow
|
||||
import codeql.rust.dataflow.TaintTracking
|
||||
import codeql.rust.security.CleartextStorageDatabaseExtensions
|
||||
|
||||
/**
|
||||
* A taint configuration from sensitive information to expressions that are
|
||||
* stored in a database.
|
||||
*/
|
||||
module CleartextStorageDatabaseConfig implements DataFlow::ConfigSig {
|
||||
import CleartextStorageDatabase
|
||||
|
||||
predicate isSource(DataFlow::Node node) { node instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node node) { node instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof Barrier }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) {
|
||||
// make sources barriers so that we only report the closest instance
|
||||
isSource(node)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// flow from `a` to `&a`
|
||||
node2.asExpr().getExpr().(RefExpr).getExpr() = node1.asExpr().getExpr()
|
||||
}
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
}
|
||||
|
||||
module CleartextStorageDatabaseFlow = TaintTracking::Global<CleartextStorageDatabaseConfig>;
|
||||
|
||||
import CleartextStorageDatabaseFlow::PathGraph
|
||||
|
||||
from
|
||||
CleartextStorageDatabaseFlow::PathNode sourceNode, CleartextStorageDatabaseFlow::PathNode sinkNode
|
||||
where CleartextStorageDatabaseFlow::flowPath(sourceNode, sinkNode)
|
||||
select sinkNode.getNode(), sourceNode, sinkNode,
|
||||
"This database operation may read or write unencrypted sensitive data from $@.", sourceNode,
|
||||
sourceNode.getNode().toString()
|
||||
@@ -0,0 +1,6 @@
|
||||
let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)";
|
||||
let result = sqlx::query(query)
|
||||
.bind(id)
|
||||
.bind(credit_card_number) // BAD: Cleartext storage of sensitive data in the database
|
||||
.execute(pool)
|
||||
.await?;
|
||||
@@ -0,0 +1,41 @@
|
||||
fn encrypt(text: String, encryption_key: &aes_gcm::Key<Aes256Gcm>) -> String {
|
||||
// encrypt text -> ciphertext
|
||||
let cipher = Aes256Gcm::new(&encryption_key);
|
||||
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
|
||||
let ciphertext = cipher.encrypt(&nonce, text.as_ref()).unwrap();
|
||||
|
||||
// append (nonce, ciphertext)
|
||||
let mut combined = nonce.to_vec();
|
||||
combined.extend(ciphertext);
|
||||
|
||||
// encode to base64 string
|
||||
BASE64_STANDARD.encode(combined)
|
||||
}
|
||||
|
||||
fn decrypt(data: String, encryption_key: &aes_gcm::Key<Aes256Gcm>) -> String {
|
||||
let cipher = Aes256Gcm::new(&encryption_key);
|
||||
|
||||
// decode base64 string
|
||||
let decoded = BASE64_STANDARD.decode(data).unwrap();
|
||||
|
||||
// split into (nonce, ciphertext)
|
||||
let nonce_size = <Aes256Gcm as AeadCore>::NonceSize::to_usize();
|
||||
let (nonce, ciphertext) = decoded.split_at(nonce_size);
|
||||
|
||||
// decrypt ciphertext -> plaintext
|
||||
let plaintext = cipher.decrypt(nonce.into(), ciphertext).unwrap();
|
||||
String::from_utf8(plaintext).unwrap()
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
let encryption_key = Aes256Gcm::generate_key(OsRng);
|
||||
|
||||
...
|
||||
|
||||
let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)";
|
||||
let result = sqlx::query(query)
|
||||
.bind(id)
|
||||
.bind(encrypt(credit_card_number, &encryption_key)) // GOOD: Encrypted storage of sensitive data in the database
|
||||
.execute(pool)
|
||||
.await?;
|
||||
@@ -32,8 +32,7 @@ class CtorAttr extends Attr {
|
||||
*/
|
||||
class StdCall extends Expr {
|
||||
StdCall() {
|
||||
this.(CallExpr).getFunction().(PathExpr).getResolvedCrateOrigin() = "lang:std" or
|
||||
this.(MethodCallExpr).getResolvedCrateOrigin() = "lang:std"
|
||||
this.(CallExprBase).getStaticTarget().getCanonicalPath().matches(["std::%", "<std::%"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ private import TaintReach
|
||||
private import codeql.rust.security.regex.RegexInjectionExtensions
|
||||
private import codeql.rust.security.AccessInvalidPointerExtensions
|
||||
private import codeql.rust.security.CleartextLoggingExtensions
|
||||
private import codeql.rust.security.CleartextStorageDatabaseExtensions
|
||||
private import codeql.rust.security.CleartextTransmissionExtensions
|
||||
private import codeql.rust.security.SqlInjectionExtensions
|
||||
private import codeql.rust.security.TaintedPathExtensions
|
||||
|
||||
2
rust/ql/test/.gitignore
vendored
2
rust/ql/test/.gitignore
vendored
@@ -2,7 +2,7 @@ target/
|
||||
|
||||
# these are all generated, see `rust/extractor/src/qltest.rs` for details
|
||||
Cargo.toml
|
||||
rust-toolchain.toml
|
||||
/*/**/rust-toolchain.toml
|
||||
lib.rs
|
||||
.proc_macro/
|
||||
.lib/
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
nonUniqueCertainType
|
||||
| web_frameworks.rs:139:30:139:39 | ...::get(...) | |
|
||||
| web_frameworks.rs:140:34:140:43 | ...::get(...) | |
|
||||
| web_frameworks.rs:141:30:141:39 | ...::get(...) | |
|
||||
@@ -2,6 +2,6 @@ multipleCallTargets
|
||||
| main.rs:118:9:118:11 | f(...) |
|
||||
| proc_macro.rs:9:5:11:5 | ...::new(...) |
|
||||
multiplePathResolutions
|
||||
| main.rs:626:3:626:12 | proc_macro |
|
||||
| main.rs:632:7:632:16 | proc_macro |
|
||||
| main.rs:635:7:635:16 | proc_macro |
|
||||
| main.rs:641:3:641:12 | proc_macro |
|
||||
| main.rs:647:7:647:16 | proc_macro |
|
||||
| main.rs:650:7:650:16 | proc_macro |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user