Merge branch 'main' into redsun82/cargo-upgrade-2

This commit is contained in:
Paolo Tranquilli
2025-08-12 16:42:23 +02:00
135 changed files with 8857 additions and 4556 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,7 +16,7 @@ pub trait AsTrapKeyPart {
impl AsTrapKeyPart for UntypedLabel {
fn as_key_part(&self) -> String {
format!("{{{}}}", self)
format!("{{{self}}}")
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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