mirror of
https://github.com/github/codeql.git
synced 2026-04-26 01:05:15 +02:00
Merge remote-tracking branch 'upstream/main' into wrongtypeformat
This commit is contained in:
10
MODULE.bazel
10
MODULE.bazel
@@ -20,18 +20,18 @@ bazel_dep(name = "rules_go", version = "0.59.0")
|
||||
bazel_dep(name = "rules_java", version = "9.0.3")
|
||||
bazel_dep(name = "rules_pkg", version = "1.0.1")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.7.3")
|
||||
bazel_dep(name = "rules_python", version = "0.40.0")
|
||||
bazel_dep(name = "rules_shell", version = "0.5.0")
|
||||
bazel_dep(name = "rules_python", version = "1.9.0")
|
||||
bazel_dep(name = "rules_shell", version = "0.6.1")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.8.1")
|
||||
bazel_dep(name = "abseil-cpp", version = "20240116.1", repo_name = "absl")
|
||||
bazel_dep(name = "abseil-cpp", version = "20260107.1", repo_name = "absl")
|
||||
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
|
||||
bazel_dep(name = "fmt", version = "12.1.0-codeql.1")
|
||||
bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1")
|
||||
bazel_dep(name = "gazelle", version = "0.47.0")
|
||||
bazel_dep(name = "rules_dotnet", version = "0.21.5-codeql.1")
|
||||
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
|
||||
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
|
||||
bazel_dep(name = "rules_rust", version = "0.68.1.codeql.1")
|
||||
bazel_dep(name = "zstd", version = "1.5.5.bcr.1")
|
||||
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")
|
||||
|
||||
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.4.29
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.28
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
actions/ql/lib/change-notes/released/0.4.29.md
Normal file
3
actions/ql/lib/change-notes/released/0.4.29.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.4.29
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.28
|
||||
lastReleaseVersion: 0.4.29
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-all
|
||||
version: 0.4.29-dev
|
||||
version: 0.4.30-dev
|
||||
library: true
|
||||
warnOnImplicitThis: true
|
||||
dependencies:
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.6.21
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.20
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
actions/ql/src/change-notes/released/0.6.21.md
Normal file
3
actions/ql/src/change-notes/released/0.6.21.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.21
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.20
|
||||
lastReleaseVersion: 0.6.21
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-queries
|
||||
version: 0.6.21-dev
|
||||
version: 0.6.22-dev
|
||||
library: false
|
||||
warnOnImplicitThis: true
|
||||
groups: [actions, queries]
|
||||
|
||||
@@ -199,6 +199,7 @@ def annotate_as_appropriate(filename, lines):
|
||||
# as overlay[local?]. It is not clear that these heuristics are exactly what we want,
|
||||
# but they seem to work well enough for now (as determined by speed and accuracy numbers).
|
||||
if (filename.endswith("Test.qll") or
|
||||
re.search(r"go/ql/lib/semmle/go/security/[^/]+[.]qll$", filename.replace(os.sep, "/")) or
|
||||
((filename.endswith("Query.qll") or filename.endswith("Config.qll")) and
|
||||
any("implements DataFlow::ConfigSig" in line for line in lines))):
|
||||
return None
|
||||
|
||||
@@ -172,10 +172,6 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"C# ControlFlowReachability": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
],
|
||||
"C++ ExternalAPIs": [
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
## 8.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL version 2.24.2 accidentally introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
|
||||
* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.
|
||||
|
||||
## 7.1.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* CodeQL version 2.24.2 accidentially introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
|
||||
14
cpp/ql/lib/change-notes/released/8.0.0.md
Normal file
14
cpp/ql/lib/change-notes/released/8.0.0.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 8.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL version 2.24.2 accidentally introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
|
||||
* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 7.1.1
|
||||
lastReleaseVersion: 8.0.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 7.1.2-dev
|
||||
version: 8.0.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -524,6 +524,12 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
not exists(NewOrNewArrayExpr new | e = new.getAllocatorCall().getArgument(0))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function has an ambiguous return type, meaning that zero or multiple return
|
||||
* types for this function are present in the database (this can occur in `build-mode: none`).
|
||||
*/
|
||||
predicate hasAmbiguousReturnType() { count(this.getType()) != 1 }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
|
||||
@@ -1663,7 +1663,7 @@ private module Cached {
|
||||
private predicate compares_ge(
|
||||
ValueNumber test, Operand left, Operand right, int k, boolean isGe, GuardValue value
|
||||
) {
|
||||
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, value))
|
||||
compares_lt(test, right, left, 1 - k, isGe, value)
|
||||
}
|
||||
|
||||
/** Rearrange various simple comparisons into `left < right + k` form. */
|
||||
|
||||
@@ -353,12 +353,26 @@ module CsvValidation {
|
||||
)
|
||||
}
|
||||
|
||||
private string getIncorrectConstructorSummaryOutput() {
|
||||
exists(string namespace, string type, string name, string output |
|
||||
type = name or
|
||||
type = name + "<" + any(string s)
|
||||
|
|
||||
summaryModel(namespace, type, _, name, _, _, _, output, _, _, _) and
|
||||
output.matches("ReturnValue%") and
|
||||
result =
|
||||
"Constructor model for " + namespace + "." + type +
|
||||
" should use `Argument[this]` in the output, not `ReturnValue`."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
msg =
|
||||
[
|
||||
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind()
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind(),
|
||||
getIncorrectConstructorSummaryOutput()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,117 +6,67 @@ private import OverlayXml
|
||||
|
||||
/**
|
||||
* Holds always for the overlay variant and never for the base variant.
|
||||
* This local predicate is used to define local predicates that behave
|
||||
* differently for the base and overlay variant.
|
||||
*/
|
||||
overlay[local]
|
||||
predicate isOverlay() { databaseMetadata("isOverlay", "true") }
|
||||
|
||||
overlay[local]
|
||||
private string getLocationFilePath(@location_default loc) {
|
||||
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file path for an element with a single location.
|
||||
* Holds if the TRAP file or tag `t` is reachable from source file `sourceFile`
|
||||
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
|
||||
*/
|
||||
overlay[local]
|
||||
private string getSingleLocationFilePath(@element e) {
|
||||
exists(@location_default loc |
|
||||
var_decls(e, _, _, _, loc)
|
||||
or
|
||||
fun_decls(e, _, _, _, loc)
|
||||
or
|
||||
type_decls(e, _, loc)
|
||||
or
|
||||
namespace_decls(e, _, loc, _)
|
||||
or
|
||||
macroinvocations(e, _, loc, _)
|
||||
or
|
||||
preprocdirects(e, _, loc)
|
||||
or
|
||||
diagnostics(e, _, _, _, _, loc)
|
||||
or
|
||||
usings(e, _, loc, _)
|
||||
or
|
||||
static_asserts(e, _, _, loc, _)
|
||||
or
|
||||
derivations(e, _, _, _, loc)
|
||||
or
|
||||
frienddecls(e, _, _, loc)
|
||||
or
|
||||
comments(e, _, loc)
|
||||
or
|
||||
exprs(e, _, loc)
|
||||
or
|
||||
stmts(e, _, loc)
|
||||
or
|
||||
initialisers(e, _, _, loc)
|
||||
or
|
||||
attributes(e, _, _, _, loc)
|
||||
or
|
||||
attribute_args(e, _, _, _, loc)
|
||||
or
|
||||
namequalifiers(e, _, _, loc)
|
||||
or
|
||||
enumconstants(e, _, _, _, _, loc)
|
||||
or
|
||||
type_mentions(e, _, loc, _)
|
||||
or
|
||||
lambda_capture(e, _, _, _, _, _, loc)
|
||||
or
|
||||
concept_templates(e, _, loc)
|
||||
|
|
||||
result = getLocationFilePath(loc)
|
||||
private predicate locallyReachableTrapOrTag(
|
||||
boolean isOverlayVariant, string sourceFile, @trap_or_tag t
|
||||
) {
|
||||
exists(@source_file sf, @trap trap |
|
||||
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
|
||||
source_file_uses_trap(sf, trap) and
|
||||
source_file_name(sf, sourceFile) and
|
||||
(t = trap or trap_uses_tag(trap, t))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file path for an element with potentially multiple locations.
|
||||
* Holds if element `e` is in TRAP file or tag `t`
|
||||
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
|
||||
*/
|
||||
overlay[local]
|
||||
private string getMultiLocationFilePath(@element e) {
|
||||
exists(@location_default loc |
|
||||
var_decls(_, e, _, _, loc)
|
||||
or
|
||||
fun_decls(_, e, _, _, loc)
|
||||
or
|
||||
type_decls(_, e, loc)
|
||||
or
|
||||
namespace_decls(_, e, loc, _)
|
||||
|
|
||||
result = getLocationFilePath(loc)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A local helper predicate that holds in the base variant and never in the
|
||||
* overlay variant.
|
||||
*/
|
||||
overlay[local]
|
||||
private predicate isBase() { not isOverlay() }
|
||||
|
||||
/**
|
||||
* Holds if `path` was extracted in the overlay database.
|
||||
*/
|
||||
overlay[local]
|
||||
private predicate overlayHasFile(string path) {
|
||||
isOverlay() and
|
||||
files(_, path) and
|
||||
path != ""
|
||||
private predicate locallyInTrapOrTag(boolean isOverlayVariant, @element e, @trap_or_tag t) {
|
||||
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
|
||||
in_trap_or_tag(e, t)
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards an element from the base variant if:
|
||||
* - It has a single location in a file extracted in the overlay, or
|
||||
* - All of its locations are in files extracted in the overlay.
|
||||
* - We have knowledge about what TRAP file or tag it is in (in the base).
|
||||
* - It is not in any overlay TRAP file or tag that is reachable from an overlay source file.
|
||||
* - For every base TRAP file or tag that contains it and is reachable from a base source file,
|
||||
* either the source file has changed, or the overlay has redefined the TRAP file or tag,
|
||||
* or the overlay runner has re-extracted the same source file.
|
||||
*/
|
||||
overlay[discard_entity]
|
||||
private predicate discardElement(@element e) {
|
||||
isBase() and
|
||||
(
|
||||
overlayHasFile(getSingleLocationFilePath(e))
|
||||
or
|
||||
forex(string path | path = getMultiLocationFilePath(e) | overlayHasFile(path))
|
||||
// If we don't have any knowledge about what TRAP file something
|
||||
// is in, then we don't want to discard it, so we only consider
|
||||
// entities that are known to be in a base TRAP file or tag.
|
||||
locallyInTrapOrTag(false, e, _) and
|
||||
// Anything that is reachable from an overlay source file should
|
||||
// not be discarded.
|
||||
not exists(@trap_or_tag t | locallyInTrapOrTag(true, e, t) |
|
||||
locallyReachableTrapOrTag(true, _, t)
|
||||
) and
|
||||
// Finally, we have to make sure the base variant does not retain it.
|
||||
// If it is reachable from a base source file, then that is
|
||||
// sufficient unless either the base source file has changed (in
|
||||
// particular, been deleted), or the overlay has redefined the TRAP
|
||||
// file or tag it is in, or the overlay runner has re-extracted the same
|
||||
// source file (e.g. because a header it includes has changed).
|
||||
forall(@trap_or_tag t, string sourceFile |
|
||||
locallyInTrapOrTag(false, e, t) and
|
||||
locallyReachableTrapOrTag(false, sourceFile, t)
|
||||
|
|
||||
overlayChangedFiles(sourceFile) or
|
||||
locallyReachableTrapOrTag(true, _, t) or
|
||||
locallyReachableTrapOrTag(true, sourceFile, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.5.12
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.11
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -218,7 +218,9 @@ where
|
||||
// only report if we cannot prove that the result of the
|
||||
// multiplication will be less (resp. greater) than the
|
||||
// maximum (resp. minimum) number we can compute.
|
||||
overflows(me, t1)
|
||||
overflows(me, t1) and
|
||||
// exclude cases where the expression type may not have been extracted accurately
|
||||
not me.getParent().(Call).getTarget().hasAmbiguousReturnType()
|
||||
select me,
|
||||
"Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '"
|
||||
+ me.getFullyConverted().getType().toString() + "'."
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Fixed an issue with the "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query causing false positive results in `build-mode: none` databases.
|
||||
3
cpp/ql/src/change-notes/released/1.5.12.md
Normal file
3
cpp/ql/src/change-notes/released/1.5.12.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.5.12
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.11
|
||||
lastReleaseVersion: 1.5.12
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 1.5.12-dev
|
||||
version: 1.5.13-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
{
|
||||
C *c = new C();
|
||||
B *b = B::make(c);
|
||||
sink(b->c); // $ast,ir
|
||||
sink(b->c); // $ ast,ir
|
||||
}
|
||||
|
||||
void f2()
|
||||
|
||||
@@ -26,9 +26,9 @@ public:
|
||||
|
||||
void func()
|
||||
{
|
||||
sink(s1); // $ast,ir
|
||||
sink(s1); // $ ast,ir
|
||||
sink(s2); // $ MISSING: ast,ir
|
||||
sink(s3); // $ast,ir
|
||||
sink(s3); // $ ast,ir
|
||||
sink(s4); // $ MISSING: ast,ir
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
};
|
||||
|
||||
static void sinkWrap(Box2* b2) {
|
||||
sink(b2->getBox1()->getElem()); // $ast,ir=28:15 ast,ir=35:15 ast,ir=42:15 ast,ir=49:15
|
||||
sink(b2->getBox1()->getElem()); // $ ast,ir=28:15 ast,ir=35:15 ast,ir=42:15 ast,ir=49:15
|
||||
}
|
||||
|
||||
Box2* boxfield;
|
||||
|
||||
@@ -48,25 +48,25 @@ struct S {
|
||||
void test_setDirectly() {
|
||||
S s;
|
||||
s.setDirectly(user_input());
|
||||
sink(s.getDirectly()); // $ast ir
|
||||
sink(s.getDirectly()); // $ ast ir
|
||||
}
|
||||
|
||||
void test_setIndirectly() {
|
||||
S s;
|
||||
s.setIndirectly(user_input());
|
||||
sink(s.getIndirectly()); // $ast ir
|
||||
sink(s.getIndirectly()); // $ ast ir
|
||||
}
|
||||
|
||||
void test_setThroughNonMember() {
|
||||
S s;
|
||||
s.setThroughNonMember(user_input());
|
||||
sink(s.getThroughNonMember()); // $ast ir
|
||||
sink(s.getThroughNonMember()); // $ ast ir
|
||||
}
|
||||
|
||||
void test_nonMemberSetA() {
|
||||
S s;
|
||||
nonMemberSetA(&s, user_input());
|
||||
sink(nonMemberGetA(&s)); // $ast,ir
|
||||
sink(nonMemberGetA(&s)); // $ ast,ir
|
||||
}
|
||||
|
||||
////////////////////
|
||||
@@ -112,7 +112,7 @@ void test_outer_with_ptr(Outer *pouter) {
|
||||
sink(outer.a); // $ ast,ir
|
||||
|
||||
sink(pouter->inner_nested.a); // $ ast,ir
|
||||
sink(pouter->inner_ptr->a); // $ast,ir
|
||||
sink(pouter->inner_ptr->a); // $ ast,ir
|
||||
sink(pouter->a); // $ ast,ir
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ void single_field_test()
|
||||
A a;
|
||||
a.i = user_input();
|
||||
A a2 = a;
|
||||
sink(a2.i); //$ ast,ir
|
||||
sink(a2.i); // $ ast,ir
|
||||
}
|
||||
|
||||
struct C {
|
||||
@@ -81,7 +81,7 @@ struct C2
|
||||
|
||||
void m() {
|
||||
f2.f1 = user_input();
|
||||
sink(getf2f1()); //$ ast,ir
|
||||
sink(getf2f1()); // $ ast,ir
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ void single_field_test_typedef(A_typedef a)
|
||||
{
|
||||
a.i = user_input();
|
||||
A_typedef a2 = a;
|
||||
sink(a2.i); //$ ast,ir
|
||||
sink(a2.i); // $ ast,ir
|
||||
}
|
||||
|
||||
namespace TestAdditionalCallTargets {
|
||||
@@ -168,4 +168,4 @@ void test_union_with_two_instantiations_of_different_sizes() {
|
||||
sink(u_int.y); // $ MISSING: ir
|
||||
}
|
||||
|
||||
} // namespace Simple
|
||||
} // namespace Simple
|
||||
|
||||
@@ -12,14 +12,14 @@ struct Outer {
|
||||
};
|
||||
|
||||
void absink(struct AB *ab) {
|
||||
sink(ab->a); //$ ast,ir=20:20 ast,ir=27:7 ast,ir=40:20
|
||||
sink(ab->a); // $ ast,ir=20:20 ast,ir=27:7 ast,ir=40:20
|
||||
sink(ab->b); // no flow
|
||||
}
|
||||
|
||||
int struct_init(void) {
|
||||
struct AB ab = { user_input(), 0 };
|
||||
|
||||
sink(ab.a); //$ ast,ir
|
||||
sink(ab.a); // $ ast,ir
|
||||
sink(ab.b); // no flow
|
||||
absink(&ab);
|
||||
|
||||
@@ -28,9 +28,9 @@ int struct_init(void) {
|
||||
&ab,
|
||||
};
|
||||
|
||||
sink(outer.nestedAB.a); //$ ast,ir
|
||||
sink(outer.nestedAB.a); // $ ast,ir
|
||||
sink(outer.nestedAB.b); // no flow
|
||||
sink(outer.pointerAB->a); //$ ast,ir
|
||||
sink(outer.pointerAB->a); // $ ast,ir
|
||||
sink(outer.pointerAB->b); // no flow
|
||||
|
||||
absink(&outer.nestedAB);
|
||||
|
||||
@@ -75,7 +75,7 @@ void test_sources() {
|
||||
int e = localMadSource();
|
||||
sink(e); // $ ir
|
||||
|
||||
sink(MyNamespace::namespaceLocalMadSource()); // $: ir
|
||||
sink(MyNamespace::namespaceLocalMadSource()); // $ ir
|
||||
sink(MyNamespace::namespaceLocalMadSourceVar); // $ ir
|
||||
sink(MyNamespace::MyNamespace2::namespace2LocalMadSource()); // $ ir
|
||||
sink(MyNamespace::localMadSource()); // $ (the MyNamespace version of this function is not a source)
|
||||
@@ -475,4 +475,4 @@ void test_receive_array() {
|
||||
int array[10] = {x};
|
||||
int y = receive_array(array);
|
||||
sink(y); // $ ir
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ void test_qualifiers()
|
||||
b.member = source();
|
||||
sink(b); // $ ir MISSING: ast
|
||||
sink(b.member); // $ ast,ir
|
||||
sink(b.getMember()); // $ MISSING: ir ast
|
||||
sink(b.getMember()); // $ MISSING: ir ast
|
||||
|
||||
c = new MyClass2(0);
|
||||
|
||||
@@ -865,4 +865,4 @@ void test_iconv(size_t size) {
|
||||
size_t size_out;
|
||||
iconv(0, &s, &size, &p, &size_out);
|
||||
sink(*p); // $ ast,ir
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,64 +24,64 @@ struct DerivedVI : virtual Base1 {
|
||||
};
|
||||
|
||||
void Locals() {
|
||||
Point pt = { //$ussa=pt
|
||||
1, //$ussa=pt[0..4)<int>
|
||||
2 //$ussa=pt[4..8)<int>
|
||||
Point pt = { // $ ussa=pt
|
||||
1, // $ ussa=pt[0..4)<int>
|
||||
2 // $ ussa=pt[4..8)<int>
|
||||
};
|
||||
int i = pt.x; //$ussa=pt[0..4)<int>
|
||||
i = pt.y; //$ussa=pt[4..8)<int>
|
||||
int i = pt.x; // $ ussa=pt[0..4)<int>
|
||||
i = pt.y; // $ ussa=pt[4..8)<int>
|
||||
int* p = &pt.x;
|
||||
i = *p; //$ussa=pt[0..4)<int>
|
||||
i = *p; // $ ussa=pt[0..4)<int>
|
||||
p = &pt.y;
|
||||
i = *p; //$ussa=pt[4..8)<int>
|
||||
i = *p; // $ ussa=pt[4..8)<int>
|
||||
}
|
||||
|
||||
void PointsTo(
|
||||
int a, //$raw=a
|
||||
Point& b, //$raw=b ussa=*b
|
||||
Point* c, //$raw=c ussa=*c
|
||||
int* d, //$raw=d ussa=*d
|
||||
DerivedSI* e, //$raw=e ussa=*e
|
||||
DerivedMI* f, //$raw=f ussa=*f
|
||||
DerivedVI* g //$raw=g ussa=*g
|
||||
int a, // $ raw=a
|
||||
Point& b, // $ raw=b ussa=*b
|
||||
Point* c, // $ raw=c ussa=*c
|
||||
int* d, // $ raw=d ussa=*d
|
||||
DerivedSI* e, // $ raw=e ussa=*e
|
||||
DerivedMI* f, // $ raw=f ussa=*f
|
||||
DerivedVI* g // $ raw=g ussa=*g
|
||||
) {
|
||||
|
||||
int i = a; //$raw=a
|
||||
i = *&a; //$raw=a
|
||||
i = *(&a + 0); //$raw=a
|
||||
i = b.x; //$raw=b ussa=*b[0..4)<int>
|
||||
i = b.y; //$raw=b ussa=*b[4..8)<int>
|
||||
i = c->x; //$raw=c ussa=*c[0..4)<int>
|
||||
i = c->y; //$raw=c ussa=*c[4..8)<int>
|
||||
i = *d; //$raw=d ussa=*d[0..4)<int>
|
||||
i = *(d + 0); //$raw=d ussa=*d[0..4)<int>
|
||||
i = d[5]; //$raw=d ussa=*d[20..24)<int>
|
||||
i = 5[d]; //$raw=d ussa=*d[20..24)<int>
|
||||
i = d[a]; //$raw=d raw=a ussa=*d[?..?)<int>
|
||||
i = a[d]; //$raw=d raw=a ussa=*d[?..?)<int>
|
||||
int i = a; // $ raw=a
|
||||
i = *&a; // $ raw=a
|
||||
i = *(&a + 0); // $ raw=a
|
||||
i = b.x; // $ raw=b ussa=*b[0..4)<int>
|
||||
i = b.y; // $ raw=b ussa=*b[4..8)<int>
|
||||
i = c->x; // $ raw=c ussa=*c[0..4)<int>
|
||||
i = c->y; // $ raw=c ussa=*c[4..8)<int>
|
||||
i = *d; // $ raw=d ussa=*d[0..4)<int>
|
||||
i = *(d + 0); // $ raw=d ussa=*d[0..4)<int>
|
||||
i = d[5]; // $ raw=d ussa=*d[20..24)<int>
|
||||
i = 5[d]; // $ raw=d ussa=*d[20..24)<int>
|
||||
i = d[a]; // $ raw=d raw=a ussa=*d[?..?)<int>
|
||||
i = a[d]; // $ raw=d raw=a ussa=*d[?..?)<int>
|
||||
|
||||
int* p = &b.x; //$raw=b
|
||||
i = *p; //$ussa=*b[0..4)<int>
|
||||
p = &b.y; //$raw=b
|
||||
i = *p; //$ussa=*b[4..8)<int>
|
||||
p = &c->x; //$raw=c
|
||||
i = *p; //$ussa=*c[0..4)<int>
|
||||
p = &c->y; //$raw=c
|
||||
i = *p; //$ussa=*c[4..8)<int>
|
||||
p = &d[5]; //$raw=d
|
||||
i = *p; //$ussa=*d[20..24)<int>
|
||||
p = &d[a]; //$raw=d raw=a
|
||||
i = *p; //$ussa=*d[?..?)<int>
|
||||
int* p = &b.x; // $ raw=b
|
||||
i = *p; // $ ussa=*b[0..4)<int>
|
||||
p = &b.y; // $ raw=b
|
||||
i = *p; // $ ussa=*b[4..8)<int>
|
||||
p = &c->x; // $ raw=c
|
||||
i = *p; // $ ussa=*c[0..4)<int>
|
||||
p = &c->y; // $ raw=c
|
||||
i = *p; // $ ussa=*c[4..8)<int>
|
||||
p = &d[5]; // $ raw=d
|
||||
i = *p; // $ ussa=*d[20..24)<int>
|
||||
p = &d[a]; // $ raw=d raw=a
|
||||
i = *p; // $ ussa=*d[?..?)<int>
|
||||
|
||||
Point* q = &c[a]; //$raw=c raw=a
|
||||
i = q->x; //$ussa=*c[?..?)<int>
|
||||
i = q->y; //$ussa=*c[?..?)<int>
|
||||
Point* q = &c[a]; // $ raw=c raw=a
|
||||
i = q->x; // $ ussa=*c[?..?)<int>
|
||||
i = q->y; // $ ussa=*c[?..?)<int>
|
||||
|
||||
i = e->b1; //$raw=e ussa=*e[0..4)<int>
|
||||
i = e->dsi; //$raw=e ussa=*e[4..8)<int>
|
||||
i = f->b1; //$raw=f ussa=*f[0..4)<int>
|
||||
i = f->b2; //$raw=f ussa=*f[4..8)<int>
|
||||
i = f->dmi; //$raw=f ussa=*f[8..12)<int>
|
||||
i = g->b1; //$raw=g ussa=*g[?..?)<int>
|
||||
i = g->dvi; //$raw=g ussa=*g[8..12)<int>
|
||||
}
|
||||
i = e->b1; // $ raw=e ussa=*e[0..4)<int>
|
||||
i = e->dsi; // $ raw=e ussa=*e[4..8)<int>
|
||||
i = f->b1; // $ raw=f ussa=*f[0..4)<int>
|
||||
i = f->b2; // $ raw=f ussa=*f[4..8)<int>
|
||||
i = f->dmi; // $ raw=f ussa=*f[8..12)<int>
|
||||
i = g->b1; // $ raw=g ussa=*g[?..?)<int>
|
||||
i = g->dvi; // $ raw=g ussa=*g[8..12)<int>
|
||||
}
|
||||
|
||||
@@ -10,24 +10,24 @@ struct S {
|
||||
|
||||
void unique_ptr_init(S s) {
|
||||
unique_ptr<S> p(new S); // MISSING: $ussa=dynamic{1}
|
||||
int i = (*p).x; //$ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*p = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
int i = (*p).x; // $ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*p = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
unique_ptr<S> q = std::move(p);
|
||||
*(q.get()) = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(q.get()) = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
shared_ptr<S> t(std::move(q));
|
||||
t->x = 5; //$ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*t = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(t.get()) = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
t->x = 5; // $ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*t = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(t.get()) = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
}
|
||||
|
||||
void shared_ptr_init(S s) {
|
||||
shared_ptr<S> p(new S); //$ MISSING: ussa=dynamic{1}
|
||||
int i = (*p).x; //$ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*p = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
shared_ptr<S> p(new S); // $ MISSING: ussa=dynamic{1}
|
||||
int i = (*p).x; // $ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*p = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
shared_ptr<S> q = std::move(p);
|
||||
*(q.get()) = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(q.get()) = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
shared_ptr<S> t(q);
|
||||
t->x = 5; //$ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*t = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(t.get()) = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
t->x = 5; // $ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*t = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(t.get()) = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ int test4() {
|
||||
}
|
||||
range(total); // $ MISSING: range=>=0
|
||||
range(i); // $ range===2
|
||||
range(total + i); // $ range="<=Phi: i+2" MISSING: range===i+2 range=>=2 range=>=i+0
|
||||
range(total + i); // $ range="<=Phi: i+2" MISSING: range===i+2 range=>=2 range=>=i+0
|
||||
return total + i;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ int test14(int x) {
|
||||
int x3 = (int)(unsigned int)x;
|
||||
range(x3);
|
||||
char c0 = x;
|
||||
range(c0);
|
||||
range(c0);
|
||||
unsigned short s0 = x;
|
||||
range(s0);
|
||||
range(x0 + x1 + x2 + x3 + c0 + s0); // $ overflow=+ overflow=+-
|
||||
@@ -218,7 +218,7 @@ int test14(int x) {
|
||||
}
|
||||
|
||||
long long test15(long long x) {
|
||||
return (x > 0 && (range(x), x == (int)x)) ? // $ range=>=1
|
||||
return (x > 0 && (range(x), x == (int)x)) ? // $ range=>=1
|
||||
(range(x), x) : // $ range=>=1
|
||||
(range(x), -1);
|
||||
}
|
||||
@@ -228,7 +228,7 @@ int test_unary(int a) {
|
||||
int total = 0;
|
||||
|
||||
if (3 <= a && a <= 11) {
|
||||
range(a); // $ range=<=11 range=>=3
|
||||
range(a); // $ range=<=11 range=>=3
|
||||
int b = +a;
|
||||
range(b); // $ range=<=11 range=>=3
|
||||
int c = -a;
|
||||
@@ -384,7 +384,7 @@ int test_mult02(int a, int b) {
|
||||
total += r;
|
||||
range(total); // $ range=">=Phi: 0-143" range=">=Phi: 0-286"
|
||||
}
|
||||
range(total); // $range=">=Phi: 0-143" range=">=Phi: 0-286"
|
||||
range(total); // $ range=">=Phi: 0-143" range=">=Phi: 0-286"
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -467,7 +467,7 @@ int test_mult04(int a, int b) {
|
||||
range(a); // $ range=<=0 range=>=-17
|
||||
range(b); // $ range=<=0 range=>=-13
|
||||
int r = a*b; // 0 .. 221
|
||||
range(r); // $ range=<=221 range=>=0
|
||||
range(r); // $ range=<=221 range=>=0
|
||||
total += r;
|
||||
range(total); // $ range="<=Phi: - ...+221"
|
||||
}
|
||||
@@ -1030,7 +1030,7 @@ void test_negate_signed(int s) {
|
||||
}
|
||||
}
|
||||
|
||||
// By setting the guard after the use in another guard we
|
||||
// By setting the guard after the use in another guard we
|
||||
// don't get the useful information
|
||||
void test_guard_after_use(int pos, int size, int offset) {
|
||||
if (pos + offset >= size) { // $ overflow=+-
|
||||
@@ -1040,12 +1040,12 @@ void test_guard_after_use(int pos, int size, int offset) {
|
||||
return;
|
||||
}
|
||||
range(pos + 1); // $ overflow=+ range="==InitializeParameter: pos+1" MISSING: range="<=InitializeParameter: size-1"
|
||||
}
|
||||
}
|
||||
|
||||
int cond();
|
||||
|
||||
|
||||
// This is basically what we get when we have a loop that calls
|
||||
// This is basically what we get when we have a loop that calls
|
||||
// realloc in some iterations
|
||||
void alloc_in_loop(int origLen) {
|
||||
if (origLen <= 10) {
|
||||
@@ -1066,12 +1066,12 @@ void alloc_in_loop(int origLen) {
|
||||
}
|
||||
}
|
||||
|
||||
// This came from a case where it handled the leftovers before an unrolled loop
|
||||
// This came from a case where it handled the leftovers before an unrolled loop
|
||||
void mask_at_start(int len) {
|
||||
if (len < 0) {
|
||||
return;
|
||||
}
|
||||
int leftOver = len & 63;
|
||||
int leftOver = len & 63;
|
||||
for (int i = 0; i < leftOver; i++) {
|
||||
range(i); // $ range=<=62 range=>=0 range="<=Store: ... & ... | Store: leftOver-1" range="<=InitializeParameter: len-1"
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
void Complex(void) {
|
||||
_Complex float cf; //$irtype=cfloat8
|
||||
_Complex double cd; //$irtype=cfloat16
|
||||
_Complex long double cld; //$irtype=cfloat32
|
||||
_Complex float cf; // $ irtype=cfloat8
|
||||
_Complex double cd; // $ irtype=cfloat16
|
||||
_Complex long double cld; // $ irtype=cfloat32
|
||||
// _Complex __float128 cf128;
|
||||
}
|
||||
|
||||
void Imaginary(void) {
|
||||
_Imaginary float jf; //$irtype=ifloat4
|
||||
_Imaginary double jd; //$irtype=ifloat8
|
||||
_Imaginary long double jld; //$irtype=ifloat16
|
||||
_Imaginary float jf; // $ irtype=ifloat4
|
||||
_Imaginary double jd; // $ irtype=ifloat8
|
||||
_Imaginary long double jld; // $ irtype=ifloat16
|
||||
// _Imaginary __float128 jf128;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,44 +22,44 @@ enum class ScopedE {
|
||||
};
|
||||
|
||||
void IRTypes() {
|
||||
char c; //$irtype=int1
|
||||
signed char sc; //$irtype=int1
|
||||
unsigned char uc; //$irtype=uint1
|
||||
short s; //$irtype=int2
|
||||
signed short ss; //$irtype=int2
|
||||
unsigned short us; //$irtype=uint2
|
||||
int i; //$irtype=int4
|
||||
signed int si; //$irtype=int4
|
||||
unsigned int ui; //$irtype=uint4
|
||||
long l; //$irtype=int8
|
||||
signed long sl; //$irtype=int8
|
||||
unsigned long ul; //$irtype=uint8
|
||||
long long ll; //$irtype=int8
|
||||
signed long long sll; //$irtype=int8
|
||||
unsigned long long ull; //$irtype=uint8
|
||||
bool b; //$irtype=bool1
|
||||
float f; //$irtype=float4
|
||||
double d; //$irtype=float8
|
||||
long double ld; //$irtype=float16
|
||||
__float128 f128; //$irtype=float16
|
||||
char c; // $ irtype=int1
|
||||
signed char sc; // $ irtype=int1
|
||||
unsigned char uc; // $ irtype=uint1
|
||||
short s; // $ irtype=int2
|
||||
signed short ss; // $ irtype=int2
|
||||
unsigned short us; // $ irtype=uint2
|
||||
int i; // $ irtype=int4
|
||||
signed int si; // $ irtype=int4
|
||||
unsigned int ui; // $ irtype=uint4
|
||||
long l; // $ irtype=int8
|
||||
signed long sl; // $ irtype=int8
|
||||
unsigned long ul; // $ irtype=uint8
|
||||
long long ll; // $ irtype=int8
|
||||
signed long long sll; // $ irtype=int8
|
||||
unsigned long long ull; // $ irtype=uint8
|
||||
bool b; // $ irtype=bool1
|
||||
float f; // $ irtype=float4
|
||||
double d; // $ irtype=float8
|
||||
long double ld; // $ irtype=float16
|
||||
__float128 f128; // $ irtype=float16
|
||||
|
||||
wchar_t wc; //$irtype=uint4
|
||||
// char8_t c8; //$irtype=uint1
|
||||
char16_t c16; //$irtype=uint2
|
||||
char32_t c32; //$irtype=uint4
|
||||
wchar_t wc; // $ irtype=uint4
|
||||
// char8_t c8; // $ irtype=uint1
|
||||
char16_t c16; // $ irtype=uint2
|
||||
char32_t c32; // $ irtype=uint4
|
||||
|
||||
int* pi; //$irtype=addr8
|
||||
int& ri = i; //$irtype=addr8
|
||||
void (*pfn)() = nullptr; //$irtype=func8
|
||||
void (&rfn)() = IRTypes; //$irtype=func8
|
||||
int* pi; // $ irtype=addr8
|
||||
int& ri = i; // $ irtype=addr8
|
||||
void (*pfn)() = nullptr; // $ irtype=func8
|
||||
void (&rfn)() = IRTypes; // $ irtype=func8
|
||||
|
||||
A s_a; //$irtype=opaque4{A}
|
||||
B s_b; //$irtype=opaque16{B}
|
||||
A s_a; // $ irtype=opaque4{A}
|
||||
B s_b; // $ irtype=opaque16{B}
|
||||
|
||||
E e; //$irtype=uint4
|
||||
ScopedE se; //$irtype=uint4
|
||||
E e; // $ irtype=uint4
|
||||
ScopedE se; // $ irtype=uint4
|
||||
|
||||
B a_b[10]; //$irtype=opaque160{B[10]}
|
||||
B a_b[10]; // $ irtype=opaque160{B[10]}
|
||||
}
|
||||
|
||||
// semmle-extractor-options: -std=c++17 --clang
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// semmle-extractor-options: --expect_errors
|
||||
|
||||
void test_float_double1(float f, double d) {
|
||||
float r1 = f * f; // GOOD
|
||||
float r2 = f * d; // GOOD
|
||||
double r3 = f * f; // BAD
|
||||
double r4 = f * d; // GOOD
|
||||
|
||||
float f1 = fabsf(f * f); // GOOD
|
||||
float f2 = fabsf(f * d); // GOOD
|
||||
double f3 = fabs(f * f); // BAD [NOT DETECTED]
|
||||
double f4 = fabs(f * d); // GOOD
|
||||
}
|
||||
|
||||
double fabs(double f);
|
||||
float fabsf(float f);
|
||||
|
||||
void test_float_double2(float f, double d) {
|
||||
float r1 = f * f; // GOOD
|
||||
float r2 = f * d; // GOOD
|
||||
double r3 = f * f; // BAD
|
||||
double r4 = f * d; // GOOD
|
||||
|
||||
float f1 = fabsf(f * f); // GOOD
|
||||
float f2 = fabsf(f * d); // GOOD
|
||||
double f3 = fabs(f * f); // BAD [NOT DETECTED]
|
||||
double f4 = fabs(f * d); // GOOD
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
| Buildless.c:6:17:6:21 | ... * ... | Multiplication result may overflow 'float' before it is converted to 'double'. |
|
||||
| Buildless.c:21:17:21:21 | ... * ... | Multiplication result may overflow 'float' before it is converted to 'double'. |
|
||||
| IntMultToLong.c:4:10:4:14 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
|
||||
| IntMultToLong.c:7:16:7:20 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
|
||||
| IntMultToLong.c:18:19:18:23 | ... * ... | Multiplication result may overflow 'float' before it is converted to 'double'. |
|
||||
|
||||
@@ -1338,7 +1338,7 @@ void indirect_time_conversion_check(WORD year, WORD offset){
|
||||
void set_time(WORD year, WORD month, WORD day){
|
||||
SYSTEMTIME tmp;
|
||||
|
||||
tmp.wYear = year; //$ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
|
||||
tmp.wYear = year; // $ Alert[cpp/leap-year/unchecked-after-arithmetic-year-modification]
|
||||
tmp.wMonth = month;
|
||||
tmp.wDay = day;
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@ NHibernate,3,,,,,,,,,,,,3,,,,,,,,,,
|
||||
Newtonsoft.Json,,,91,,,,,,,,,,,,,,,,,,,73,18
|
||||
ServiceStack,194,,7,27,,,,,75,,,,92,,,,,,,,,7,
|
||||
SourceGenerators,,,5,,,,,,,,,,,,,,,,,,,,5
|
||||
System,59,47,12495,,6,5,12,,,4,1,,31,2,,6,15,17,4,3,,6382,6113
|
||||
System,59,48,12495,,6,5,12,,,4,1,,31,2,,6,15,17,5,3,,6382,6113
|
||||
Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,,,,,,,,,,
|
||||
|
||||
|
@@ -8,7 +8,7 @@ C# framework & library support
|
||||
|
||||
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
|
||||
`ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
|
||||
System,"``System.*``, ``System``",47,12495,59,5
|
||||
System,"``System.*``, ``System``",48,12495,59,5
|
||||
Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Http``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Data.SqlClient``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``NHibernate``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",60,2406,162,4
|
||||
Totals,,107,14908,415,9
|
||||
Totals,,108,14908,415,9
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Remove inclusion of @assign_expr in @bin_op
|
||||
compatibility: full
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.7.60
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.7.59
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.7.60
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.7.59
|
||||
lastReleaseVersion: 1.7.60
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.7.60-dev
|
||||
version: 1.7.61-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.7.60
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.7.59
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.7.60
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.7.59
|
||||
lastReleaseVersion: 1.7.60
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.7.60-dev
|
||||
version: 1.7.61-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,63 +1,5 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.controlflow.internal.Completion
|
||||
import semmle.code.csharp.controlflow.internal.PreBasicBlocks
|
||||
import ControlFlow
|
||||
import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl::Consistency
|
||||
import semmle.code.csharp.controlflow.internal.Splitting
|
||||
|
||||
private predicate splitBB(ControlFlow::BasicBlock bb) {
|
||||
exists(ControlFlow::Node first |
|
||||
first = bb.getFirstNode() and
|
||||
first.isJoin() and
|
||||
strictcount(first.getAPredecessor().getAstNode()) = 1
|
||||
)
|
||||
}
|
||||
|
||||
private class RelevantBasicBlock extends ControlFlow::BasicBlock {
|
||||
RelevantBasicBlock() { not splitBB(this) }
|
||||
}
|
||||
|
||||
predicate bbStartInconsistency(ControlFlowElement cfe) {
|
||||
exists(RelevantBasicBlock bb | bb.getFirstNode() = cfe.getAControlFlowNode()) and
|
||||
not cfe = any(PreBasicBlock bb).getFirstElement()
|
||||
}
|
||||
|
||||
predicate bbSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) {
|
||||
exists(RelevantBasicBlock predBB, RelevantBasicBlock succBB |
|
||||
predBB.getLastNode() = pred.getAControlFlowNode() and
|
||||
succBB = predBB.getASuccessor() and
|
||||
succBB.getFirstNode() = succ.getAControlFlowNode()
|
||||
) and
|
||||
not exists(PreBasicBlock predBB, PreBasicBlock succBB |
|
||||
predBB.getLastNode() = pred and
|
||||
succBB = predBB.getASuccessor() and
|
||||
succBB.getFirstElement() = succ
|
||||
)
|
||||
}
|
||||
|
||||
predicate bbIntraSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) {
|
||||
exists(ControlFlow::BasicBlock bb, int i |
|
||||
pred.getAControlFlowNode() = bb.getNode(i) and
|
||||
succ.getAControlFlowNode() = bb.getNode(i + 1)
|
||||
) and
|
||||
not exists(PreBasicBlock bb |
|
||||
bb.getLastNode() = pred and
|
||||
bb.getASuccessor().getFirstElement() = succ
|
||||
) and
|
||||
not exists(PreBasicBlock bb, int i |
|
||||
bb.getNode(i) = pred and
|
||||
bb.getNode(i + 1) = succ
|
||||
)
|
||||
}
|
||||
|
||||
query predicate preBasicBlockConsistency(ControlFlowElement cfe1, ControlFlowElement cfe2, string s) {
|
||||
bbStartInconsistency(cfe1) and
|
||||
cfe2 = cfe1 and
|
||||
s = "start inconsistency"
|
||||
or
|
||||
bbSuccInconsistency(cfe1, cfe2) and
|
||||
s = "succ inconsistency"
|
||||
or
|
||||
bbIntraSuccInconsistency(cfe1, cfe2) and
|
||||
s = "intra succ inconsistency"
|
||||
}
|
||||
|
||||
@@ -35,9 +35,7 @@ private module Input implements InputSig<Location, CsharpDataFlow> {
|
||||
or
|
||||
n.asExpr().(ObjectCreation).hasInitializer()
|
||||
or
|
||||
exists(
|
||||
n.(PostUpdateNode).getPreUpdateNode().asExprAtNode(LocalFlow::getPostUpdateReverseStep(_))
|
||||
)
|
||||
n.(PostUpdateNode).getPreUpdateNode().asExpr() = LocalFlow::getPostUpdateReverseStep(_)
|
||||
}
|
||||
|
||||
predicate argHasPostUpdateExclude(ArgumentNode n) {
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
## 5.4.8
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* C# 14: Added support for partial events.
|
||||
* C# 14: Added support for the `field` keyword in properties.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed an issue where the body of a partial member could be extracted twice. When both a *defining* and an *implementing* declaration exist, only the *implementing* declaration is now extracted.
|
||||
|
||||
## 5.4.7
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* C# 14: Added support for the `field` keyword in properties.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* C# 14: Added support for partial events.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added reverse taint flow from implicit conversion operator calls to their arguments.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added `System.Net.WebSockets::ReceiveAsync` as a remote flow source.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
|
||||
@@ -1,4 +1,10 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
## 5.4.8
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* C# 14: Added support for partial events.
|
||||
* C# 14: Added support for the `field` keyword in properties.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed an issue where the body of a partial member could be extracted twice. When both a *defining* and an *implementing* declaration exist, only the *implementing* declaration is now extracted.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 5.4.7
|
||||
lastReleaseVersion: 5.4.8
|
||||
|
||||
6
csharp/ql/lib/ext/System.Net.WebSockets.model.yml
Normal file
6
csharp/ql/lib/ext/System.Net.WebSockets.model.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/csharp-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["System.Net.WebSockets", "WebSocket", True, "ReceiveAsync", "", "", "Argument[0]", "remote", "manual"]
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 5.4.8-dev
|
||||
version: 5.4.9-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -183,9 +183,10 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt {
|
||||
* return 3;
|
||||
* }
|
||||
* ```
|
||||
* Note that this reorders the `default` case to always be at the end.
|
||||
*/
|
||||
override CaseStmt getCase(int i) { result = SwithStmtInternal::getCase(this, i) }
|
||||
override CaseStmt getCase(int i) {
|
||||
result = rank[i + 1](CaseStmt cs, int idx | cs = this.getChildStmt(idx) | cs order by idx)
|
||||
}
|
||||
|
||||
/** Gets a case of this `switch` statement. */
|
||||
override CaseStmt getACase() { result = this.getCase(_) }
|
||||
@@ -208,87 +209,29 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt {
|
||||
* ```csharp
|
||||
* switch (x) {
|
||||
* case "abc": // i = 0
|
||||
* return 0;
|
||||
* case int i when i > 0: // i = 1
|
||||
* return 1;
|
||||
* case string s: // i = 2
|
||||
* Console.WriteLine(s);
|
||||
* return 2; // i = 3
|
||||
* default: // i = 4
|
||||
* return 3; // i = 5
|
||||
* return 0; // i = 1
|
||||
* case int i when i > 0: // i = 2
|
||||
* return 1; // i = 3
|
||||
* case string s: // i = 4
|
||||
* Console.WriteLine(s); // i = 5
|
||||
* return 2; // i = 6
|
||||
* default: // i = 7
|
||||
* return 3; // i = 8
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note that each non-`default` case is a labeled statement, so the statement
|
||||
* that follows is a child of the labeled statement, and not the `switch` block.
|
||||
*/
|
||||
Stmt getStmt(int i) { result = SwithStmtInternal::getStmt(this, i) }
|
||||
Stmt getStmt(int i) { result = this.getChildStmt(i) }
|
||||
|
||||
/** Gets a statement in the body of this `switch` statement. */
|
||||
Stmt getAStmt() { result = this.getStmt(_) }
|
||||
}
|
||||
|
||||
cached
|
||||
private module SwithStmtInternal {
|
||||
cached
|
||||
CaseStmt getCase(SwitchStmt ss, int i) {
|
||||
exists(int index, int rankIndex |
|
||||
caseIndex(ss, result, index) and
|
||||
rankIndex = i + 1 and
|
||||
index = rank[rankIndex](int j, CaseStmt cs | caseIndex(ss, cs, j) | j)
|
||||
)
|
||||
}
|
||||
|
||||
/** Implicitly reorder case statements to put the default case last if needed. */
|
||||
private predicate caseIndex(SwitchStmt ss, CaseStmt case, int index) {
|
||||
exists(int i | case = ss.getChildStmt(i) |
|
||||
if case instanceof DefaultCase
|
||||
then index = max(int j | exists(ss.getChildStmt(j))) + 1
|
||||
else index = i
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Stmt getStmt(SwitchStmt ss, int i) {
|
||||
exists(int index, int rankIndex |
|
||||
result = ss.getChildStmt(index) and
|
||||
rankIndex = i + 1 and
|
||||
index =
|
||||
rank[rankIndex](int j, Stmt s |
|
||||
// `getChild` includes both labeled statements and the targeted
|
||||
// statements of labeled statement as separate children, but we
|
||||
// only want the labeled statement
|
||||
s = getLabeledStmt(ss, j)
|
||||
|
|
||||
j
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private Stmt getLabeledStmt(SwitchStmt ss, int i) {
|
||||
result = ss.getChildStmt(i) and
|
||||
not result = any(CaseStmt cs).getBody()
|
||||
}
|
||||
}
|
||||
|
||||
/** A `case` statement. */
|
||||
class CaseStmt extends Case, @case_stmt {
|
||||
override Expr getExpr() { result = any(SwitchStmt ss | ss.getACase() = this).getExpr() }
|
||||
|
||||
override PatternExpr getPattern() { result = this.getChild(0) }
|
||||
|
||||
override Stmt getBody() {
|
||||
exists(int i, Stmt next |
|
||||
this = this.getParent().getChild(i) and
|
||||
next = this.getParent().getChild(i + 1)
|
||||
|
|
||||
result = next and
|
||||
not result instanceof CaseStmt
|
||||
or
|
||||
result = next.(CaseStmt).getBody()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the condition on this case, if any. For example, the type case on line 3
|
||||
* has no condition, and the type case on line 4 has condition `s.Length > 0`, in
|
||||
|
||||
@@ -142,6 +142,7 @@ private module GuardsInput implements
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate equalityTest(Expr eqtest, Expr left, Expr right, boolean polarity) {
|
||||
exists(ComparisonTest ct |
|
||||
ct.getExpr() = eqtest and
|
||||
@@ -410,6 +411,22 @@ private predicate typePattern(PatternMatch pm, TypePatternExpr tpe, Type t) {
|
||||
t = pm.getExpr().getType()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate dereferenceableExpr(Expr e, boolean isNullableType) {
|
||||
exists(Type t | t = e.getType() |
|
||||
t instanceof NullableType and
|
||||
isNullableType = true
|
||||
or
|
||||
t instanceof RefType and
|
||||
isNullableType = false
|
||||
)
|
||||
or
|
||||
exists(Expr parent |
|
||||
dereferenceableExpr(parent, isNullableType) and
|
||||
e = getNullEquivParent(parent)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that evaluates to a value that can be dereferenced. That is,
|
||||
* an expression that may evaluate to `null`.
|
||||
@@ -418,21 +435,12 @@ class DereferenceableExpr extends Expr {
|
||||
private boolean isNullableType;
|
||||
|
||||
DereferenceableExpr() {
|
||||
exists(Expr e, Type t |
|
||||
// There is currently a bug in the extractor: the type of `x?.Length` is
|
||||
// incorrectly `int`, while it should have been `int?`. We apply
|
||||
// `getNullEquivParent()` as a workaround
|
||||
this = getNullEquivParent*(e) and
|
||||
t = e.getType() and
|
||||
not this instanceof SwitchCaseExpr and
|
||||
not this instanceof PatternExpr
|
||||
|
|
||||
t instanceof NullableType and
|
||||
isNullableType = true
|
||||
or
|
||||
t instanceof RefType and
|
||||
isNullableType = false
|
||||
)
|
||||
// There is currently a bug in the extractor: the type of `x?.Length` is
|
||||
// incorrectly `int`, while it should have been `int?`. We apply
|
||||
// `getNullEquivParent()` as a workaround
|
||||
dereferenceableExpr(this, isNullableType) and
|
||||
not this instanceof SwitchCaseExpr and
|
||||
not this instanceof PatternExpr
|
||||
}
|
||||
|
||||
/** Holds if this expression has a nullable type `T?`. */
|
||||
|
||||
@@ -94,9 +94,19 @@ private Element getAChild(Element p) {
|
||||
result = p.(AssignOperation).getExpandedAssignment()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate astNode(Element e) {
|
||||
e = any(@top_level_exprorstmt_parent p | not p instanceof Attribute)
|
||||
or
|
||||
exists(Element parent |
|
||||
astNode(parent) and
|
||||
e = getAChild(parent)
|
||||
)
|
||||
}
|
||||
|
||||
/** An AST node. */
|
||||
class AstNode extends Element, TAstNode {
|
||||
AstNode() { this = getAChild*(any(@top_level_exprorstmt_parent p | not p instanceof Attribute)) }
|
||||
AstNode() { astNode(this) }
|
||||
|
||||
int getId() { idOf(this, result) }
|
||||
}
|
||||
@@ -298,6 +308,93 @@ private class ConstructorTree extends ControlFlowTree instanceof Constructor {
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
private module SwithStmtInternal {
|
||||
// Reorders default to be last if needed
|
||||
cached
|
||||
CaseStmt getCase(SwitchStmt ss, int i) {
|
||||
exists(int index, int rankIndex |
|
||||
caseIndex(ss, result, index) and
|
||||
rankIndex = i + 1 and
|
||||
index = rank[rankIndex](int j, CaseStmt cs | caseIndex(ss, cs, j) | j)
|
||||
)
|
||||
}
|
||||
|
||||
/** Implicitly reorder case statements to put the default case last if needed. */
|
||||
private predicate caseIndex(SwitchStmt ss, CaseStmt case, int index) {
|
||||
exists(int i | case = ss.getChildStmt(i) |
|
||||
if case instanceof DefaultCase
|
||||
then index = max(int j | exists(ss.getChildStmt(j))) + 1
|
||||
else index = i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th statement in the body of this `switch` statement.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```csharp
|
||||
* switch (x) {
|
||||
* case "abc": // i = 0
|
||||
* return 0;
|
||||
* case int i when i > 0: // i = 1
|
||||
* return 1;
|
||||
* case string s: // i = 2
|
||||
* Console.WriteLine(s);
|
||||
* return 2; // i = 3
|
||||
* default: // i = 4
|
||||
* return 3; // i = 5
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note that each non-`default` case is a labeled statement, so the statement
|
||||
* that follows is a child of the labeled statement, and not the `switch` block.
|
||||
*/
|
||||
cached
|
||||
Stmt getStmt(SwitchStmt ss, int i) {
|
||||
exists(int index, int rankIndex |
|
||||
result = ss.getChildStmt(index) and
|
||||
rankIndex = i + 1 and
|
||||
index =
|
||||
rank[rankIndex](int j, Stmt s |
|
||||
// `getChild` includes both labeled statements and the targeted
|
||||
// statements of labeled statement as separate children, but we
|
||||
// only want the labeled statement
|
||||
s = getLabeledStmt(ss, j)
|
||||
|
|
||||
j
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private Stmt getLabeledStmt(SwitchStmt ss, int i) {
|
||||
result = ss.getChildStmt(i) and
|
||||
not result = caseStmtGetBody(_)
|
||||
}
|
||||
}
|
||||
|
||||
private ControlFlowElement caseGetBody(Case c) {
|
||||
result = c.getBody() or result = caseStmtGetBody(c)
|
||||
}
|
||||
|
||||
private ControlFlowElement caseStmtGetBody(CaseStmt c) {
|
||||
exists(int i, Stmt next |
|
||||
c = c.getParent().getChild(i) and
|
||||
next = c.getParent().getChild(i + 1)
|
||||
|
|
||||
result = next and
|
||||
not result instanceof CaseStmt
|
||||
or
|
||||
result = caseStmtGetBody(next)
|
||||
)
|
||||
}
|
||||
|
||||
// Reorders default to be last if needed
|
||||
private Case switchGetCase(Switch s, int i) {
|
||||
result = s.(SwitchExpr).getCase(i) or result = SwithStmtInternal::getCase(s, i)
|
||||
}
|
||||
|
||||
abstract private class SwitchTree extends ControlFlowTree instanceof Switch {
|
||||
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
|
||||
|
||||
@@ -305,27 +402,27 @@ abstract private class SwitchTree extends ControlFlowTree instanceof Switch {
|
||||
// Flow from last element of switch expression to first element of first case
|
||||
last(super.getExpr(), pred, c) and
|
||||
c instanceof NormalCompletion and
|
||||
first(super.getCase(0), succ)
|
||||
first(switchGetCase(this, 0), succ)
|
||||
or
|
||||
// Flow from last element of case pattern to next case
|
||||
exists(Case case, int i | case = super.getCase(i) |
|
||||
exists(Case case, int i | case = switchGetCase(this, i) |
|
||||
last(case.getPattern(), pred, c) and
|
||||
c.(MatchingCompletion).isNonMatch() and
|
||||
first(super.getCase(i + 1), succ)
|
||||
first(switchGetCase(this, i + 1), succ)
|
||||
)
|
||||
or
|
||||
// Flow from last element of condition to next case
|
||||
exists(Case case, int i | case = super.getCase(i) |
|
||||
exists(Case case, int i | case = switchGetCase(this, i) |
|
||||
last(case.getCondition(), pred, c) and
|
||||
c instanceof FalseCompletion and
|
||||
first(super.getCase(i + 1), succ)
|
||||
first(switchGetCase(this, i + 1), succ)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract private class CaseTree extends ControlFlowTree instanceof Case {
|
||||
final override predicate propagatesAbnormal(AstNode child) {
|
||||
child in [super.getPattern().(ControlFlowElement), super.getCondition(), super.getBody()]
|
||||
child in [super.getPattern().(ControlFlowElement), super.getCondition(), caseGetBody(this)]
|
||||
}
|
||||
|
||||
override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
@@ -338,13 +435,13 @@ abstract private class CaseTree extends ControlFlowTree instanceof Case {
|
||||
first(super.getCondition(), succ)
|
||||
else
|
||||
// Flow from last element of pattern to first element of body
|
||||
first(super.getBody(), succ)
|
||||
first(caseGetBody(this), succ)
|
||||
)
|
||||
or
|
||||
// Flow from last element of condition to first element of body
|
||||
last(super.getCondition(), pred, c) and
|
||||
c instanceof TrueCompletion and
|
||||
first(super.getBody(), succ)
|
||||
first(caseGetBody(this), succ)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1216,10 +1313,11 @@ module Statements {
|
||||
c instanceof NormalCompletion
|
||||
or
|
||||
// A statement exits with a `break` completion
|
||||
last(super.getStmt(_), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
|
||||
last(SwithStmtInternal::getStmt(this, _), last,
|
||||
c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
|
||||
or
|
||||
// A statement exits abnormally
|
||||
last(super.getStmt(_), last, c) and
|
||||
last(SwithStmtInternal::getStmt(this, _), last, c) and
|
||||
not c instanceof BreakCompletion and
|
||||
not c instanceof NormalCompletion and
|
||||
not any(LabeledStmtTree t |
|
||||
@@ -1228,8 +1326,8 @@ module Statements {
|
||||
or
|
||||
// Last case exits with a non-match
|
||||
exists(CaseStmt cs, int last_ |
|
||||
last_ = max(int i | exists(super.getCase(i))) and
|
||||
cs = super.getCase(last_)
|
||||
last_ = max(int i | exists(SwithStmtInternal::getCase(this, i))) and
|
||||
cs = SwithStmtInternal::getCase(this, last_)
|
||||
|
|
||||
last(cs.getPattern(), last, c) and
|
||||
not c.(MatchingCompletion).isMatch()
|
||||
@@ -1248,22 +1346,22 @@ module Statements {
|
||||
c instanceof SimpleCompletion
|
||||
or
|
||||
// Flow from last element of non-`case` statement `i` to first element of statement `i+1`
|
||||
exists(int i | last(super.getStmt(i), pred, c) |
|
||||
not super.getStmt(i) instanceof CaseStmt and
|
||||
exists(int i | last(SwithStmtInternal::getStmt(this, i), pred, c) |
|
||||
not SwithStmtInternal::getStmt(this, i) instanceof CaseStmt and
|
||||
c instanceof NormalCompletion and
|
||||
first(super.getStmt(i + 1), succ)
|
||||
first(SwithStmtInternal::getStmt(this, i + 1), succ)
|
||||
)
|
||||
or
|
||||
// Flow from last element of `case` statement `i` to first element of statement `i+1`
|
||||
exists(int i, Stmt body |
|
||||
body = super.getStmt(i).(CaseStmt).getBody() and
|
||||
body = caseStmtGetBody(SwithStmtInternal::getStmt(this, i)) and
|
||||
// in case of fall-through cases, make sure to not jump from their shared body back
|
||||
// to one of the fall-through cases
|
||||
not body = super.getStmt(i + 1).(CaseStmt).getBody() and
|
||||
not body = caseStmtGetBody(SwithStmtInternal::getStmt(this, i + 1)) and
|
||||
last(body, pred, c)
|
||||
|
|
||||
c instanceof NormalCompletion and
|
||||
first(super.getStmt(i + 1), succ)
|
||||
first(SwithStmtInternal::getStmt(this, i + 1), succ)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1279,7 +1377,7 @@ module Statements {
|
||||
not c.(MatchingCompletion).isMatch()
|
||||
or
|
||||
// Case body exits with any completion
|
||||
last(super.getBody(), last, c)
|
||||
last(caseStmtGetBody(this), last, c)
|
||||
}
|
||||
|
||||
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides a basic block implementation on control flow elements. That is,
|
||||
* a "pre-CFG" where the nodes are (unsplit) control flow elements and the
|
||||
* successor relation is `succ = succ(pred, _)`.
|
||||
*
|
||||
* The logic is duplicated from the implementation in `BasicBlocks.qll`, and
|
||||
* being an internal class, all predicate documentation has been removed.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import Completion
|
||||
private import ControlFlowGraphImpl
|
||||
private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow as Cfg
|
||||
private import codeql.controlflow.BasicBlock as BB
|
||||
|
||||
private predicate startsBB(ControlFlowElement cfe) {
|
||||
not succ(_, cfe, _) and
|
||||
(
|
||||
succ(cfe, _, _)
|
||||
or
|
||||
scopeLast(_, cfe, _)
|
||||
)
|
||||
or
|
||||
strictcount(ControlFlowElement pred, Completion c | succ(pred, cfe, c)) > 1
|
||||
or
|
||||
succ(_, cfe, any(ConditionalCompletion c))
|
||||
or
|
||||
exists(ControlFlowElement pred, int i |
|
||||
succ(pred, cfe, _) and
|
||||
i = count(ControlFlowElement succ, Completion c | succ(pred, succ, c))
|
||||
|
|
||||
i > 1
|
||||
or
|
||||
i = 1 and
|
||||
scopeLast(_, pred, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate intraBBSucc(ControlFlowElement pred, ControlFlowElement succ) {
|
||||
succ(pred, succ, _) and
|
||||
not startsBB(succ)
|
||||
}
|
||||
|
||||
private predicate bbIndex(ControlFlowElement bbStart, ControlFlowElement cfe, int i) =
|
||||
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfe, i)
|
||||
|
||||
private predicate succBB(PreBasicBlock pred, PreBasicBlock succ) { succ = pred.getASuccessor() }
|
||||
|
||||
private predicate entryBB(PreBasicBlock bb) { scopeFirst(_, bb) }
|
||||
|
||||
private predicate bbIDominates(PreBasicBlock dom, PreBasicBlock bb) =
|
||||
idominance(entryBB/1, succBB/2)(_, dom, bb)
|
||||
|
||||
class PreBasicBlock extends ControlFlowElement {
|
||||
PreBasicBlock() { startsBB(this) }
|
||||
|
||||
PreBasicBlock getASuccessor(Cfg::SuccessorType t) {
|
||||
succ(this.getLastNode(), result, any(Completion c | t = c.getAMatchingSuccessorType()))
|
||||
}
|
||||
|
||||
deprecated PreBasicBlock getASuccessorByType(Cfg::SuccessorType t) {
|
||||
result = this.getASuccessor(t)
|
||||
}
|
||||
|
||||
PreBasicBlock getASuccessor() { result = this.getASuccessor(_) }
|
||||
|
||||
PreBasicBlock getAPredecessor() { result.getASuccessor() = this }
|
||||
|
||||
ControlFlowElement getNode(int pos) { bbIndex(this, result, pos) }
|
||||
|
||||
deprecated ControlFlowElement getElement(int pos) { result = this.getNode(pos) }
|
||||
|
||||
ControlFlowElement getAnElement() { result = this.getNode(_) }
|
||||
|
||||
ControlFlowElement getFirstElement() { result = this }
|
||||
|
||||
ControlFlowElement getLastNode() { result = this.getNode(this.length() - 1) }
|
||||
|
||||
deprecated ControlFlowElement getLastElement() { result = this.getLastNode() }
|
||||
|
||||
int length() { result = strictcount(this.getAnElement()) }
|
||||
|
||||
PreBasicBlock getImmediateDominator() { bbIDominates(result, this) }
|
||||
|
||||
predicate immediatelyDominates(PreBasicBlock bb) { bbIDominates(this, bb) }
|
||||
|
||||
pragma[inline]
|
||||
predicate strictlyDominates(PreBasicBlock bb) { this.immediatelyDominates+(bb) }
|
||||
|
||||
pragma[inline]
|
||||
predicate dominates(PreBasicBlock bb) {
|
||||
bb = this
|
||||
or
|
||||
this.strictlyDominates(bb)
|
||||
}
|
||||
|
||||
predicate inDominanceFrontier(PreBasicBlock df) {
|
||||
this = df.getAPredecessor() and not bbIDominates(this, df)
|
||||
or
|
||||
exists(PreBasicBlock prev | prev.inDominanceFrontier(df) |
|
||||
bbIDominates(this, prev) and
|
||||
not bbIDominates(this, df)
|
||||
)
|
||||
}
|
||||
|
||||
/** Unsupported. Do not use. */
|
||||
predicate strictlyPostDominates(PreBasicBlock bb) { none() }
|
||||
|
||||
/** Unsupported. Do not use. */
|
||||
predicate postDominates(PreBasicBlock bb) {
|
||||
this.strictlyPostDominates(bb) or
|
||||
this = bb
|
||||
}
|
||||
}
|
||||
|
||||
private Completion getConditionalCompletion(ConditionalCompletion cc) {
|
||||
result.getInnerCompletion() = cc
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate conditionBlockImmediatelyControls(
|
||||
ConditionBlock cond, PreBasicBlock succ, ConditionalCompletion cc
|
||||
) {
|
||||
exists(ControlFlowElement last, Completion c |
|
||||
last = cond.getLastNode() and
|
||||
c = getConditionalCompletion(cc) and
|
||||
succ(last, succ, c) and
|
||||
// In the pre-CFG, we need to account for case where one predecessor node has
|
||||
// two edges to the same successor node. Assertion expressions are examples of
|
||||
// such nodes.
|
||||
not exists(Completion other |
|
||||
succ(last, succ, other) and
|
||||
other != c
|
||||
) and
|
||||
forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != cond |
|
||||
succ.dominates(pred)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
class ConditionBlock extends PreBasicBlock {
|
||||
ConditionBlock() {
|
||||
exists(Completion c | c = getConditionalCompletion(_) |
|
||||
succ(this.getLastNode(), _, c)
|
||||
or
|
||||
scopeLast(_, this.getLastNode(), c)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate controls(PreBasicBlock controlled, Cfg::ConditionalSuccessor s) {
|
||||
exists(PreBasicBlock succ, ConditionalCompletion c |
|
||||
conditionBlockImmediatelyControls(this, succ, c)
|
||||
|
|
||||
succ.dominates(controlled) and
|
||||
s = c.getAMatchingSuccessorType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module PreCfg implements BB::CfgSig<Location> {
|
||||
class ControlFlowNode = ControlFlowElement;
|
||||
|
||||
class BasicBlock = PreBasicBlock;
|
||||
|
||||
class EntryBasicBlock extends BasicBlock {
|
||||
EntryBasicBlock() { entryBB(this) }
|
||||
}
|
||||
|
||||
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) {
|
||||
conditionBlockImmediatelyControls(bb1, bb2, _)
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
import csharp
|
||||
|
||||
private class ControlFlowScope extends ControlFlowElement {
|
||||
private boolean exactScope;
|
||||
|
||||
ControlFlowScope() {
|
||||
exists(ControlFlowReachabilityConfiguration c |
|
||||
c.candidate(_, _, this, exactScope, _) or
|
||||
c.candidateDef(_, _, this, exactScope, _)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExact() { exactScope = true }
|
||||
|
||||
predicate isNonExact() { exactScope = false }
|
||||
}
|
||||
|
||||
private ControlFlowElement getANonExactScopeChild(ControlFlowScope scope) {
|
||||
scope.isNonExact() and
|
||||
result = scope
|
||||
or
|
||||
result = getANonExactScopeChild(scope).getAChild()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) {
|
||||
result.getANode().getAstNode() = getANonExactScopeChild(scope) and
|
||||
exactScope = false
|
||||
or
|
||||
scope.isExact() and
|
||||
result.getANode().getAstNode() = scope and
|
||||
exactScope = true
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class for determining control-flow reachability for pairs of
|
||||
* elements.
|
||||
*
|
||||
* This is useful when defining for example expression-based data-flow steps in
|
||||
* the presence of control-flow splitting, where a data-flow step should make
|
||||
* sure to stay in the same split.
|
||||
*
|
||||
* For example, in
|
||||
*
|
||||
* ```csharp
|
||||
* if (b)
|
||||
* ....
|
||||
* var x = "foo";
|
||||
* if (b)
|
||||
* ....
|
||||
* ```
|
||||
*
|
||||
* there should only be steps from `[b = true] "foo"` to `[b = true] SSA def(x)`
|
||||
* and `[b = false] "foo"` to `[b = false] SSA def(x)`, and for example not from
|
||||
* `[b = true] "foo"` to `[b = false] SSA def(x)`
|
||||
*/
|
||||
abstract class ControlFlowReachabilityConfiguration extends string {
|
||||
bindingset[this]
|
||||
ControlFlowReachabilityConfiguration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `e1` and `e2` are expressions for which we want to find a
|
||||
* control-flow path that follows control flow successors (resp.
|
||||
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
|
||||
* `scope`. The Boolean `exactScope` indicates whether a transitive child
|
||||
* of `scope` is allowed (`exactScope = false`).
|
||||
*/
|
||||
predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` and `def` are elements for which we want to find a
|
||||
* control-flow path that follows control flow successors (resp.
|
||||
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
|
||||
* `scope`. The Boolean `exactScope` indicates whether a transitive child
|
||||
* of `scope` is allowed (`exactScope = false`).
|
||||
*/
|
||||
predicate candidateDef(
|
||||
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
|
||||
boolean isSuccessor
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockExprBase(
|
||||
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1, int i,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.candidate(e1, e2, _, _, isSuccessor) and
|
||||
cfn1 = e1.getAControlFlowNode() and
|
||||
bb.getNode(i) = cfn1
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockExprRec(
|
||||
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
exists(ControlFlow::BasicBlock mid |
|
||||
this.reachesBasicBlockExpr(e1, e2, isSuccessor, cfn1, mid)
|
||||
|
|
||||
isSuccessor = true and
|
||||
bb = mid.getASuccessor()
|
||||
or
|
||||
isSuccessor = false and
|
||||
bb = mid.getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockExpr(
|
||||
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, _, bb)
|
||||
or
|
||||
exists(ControlFlowElement scope, boolean exactScope |
|
||||
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
|
||||
this.reachesBasicBlockExprRec(e1, e2, isSuccessor, cfn1, bb) and
|
||||
bb = getABasicBlockInScope(scope, exactScope)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockDefinitionBase(
|
||||
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
|
||||
int i, ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.candidateDef(e, def, _, _, isSuccessor) and
|
||||
cfn = e.getAControlFlowNode() and
|
||||
bb.getNode(i) = cfn
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockDefinitionRec(
|
||||
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
exists(ControlFlow::BasicBlock mid |
|
||||
this.reachesBasicBlockDefinition(e, def, isSuccessor, cfn, mid)
|
||||
|
|
||||
isSuccessor = true and
|
||||
bb = mid.getASuccessor()
|
||||
or
|
||||
isSuccessor = false and
|
||||
bb = mid.getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockDefinition(
|
||||
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, _, bb)
|
||||
or
|
||||
exists(ControlFlowElement scope, boolean exactScope |
|
||||
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
|
||||
this.reachesBasicBlockDefinitionRec(e, def, isSuccessor, cfn, bb) and
|
||||
bb = getABasicBlockInScope(scope, exactScope)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control-flow path from `cfn1` to `cfn2`, where `cfn1` is a
|
||||
* control-flow node for `e1` and `cfn2` is a control-flow node for `e2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate hasExprPath(Expr e1, ControlFlow::Node cfn1, Expr e2, ControlFlow::Node cfn2) {
|
||||
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
|
||||
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, i, bb) and
|
||||
cfn2 = bb.getNode(j) and
|
||||
cfn2 = e2.getAControlFlowNode()
|
||||
|
|
||||
isSuccessor = true and j >= i
|
||||
or
|
||||
isSuccessor = false and i >= j
|
||||
)
|
||||
or
|
||||
exists(ControlFlow::BasicBlock bb |
|
||||
this.reachesBasicBlockExprRec(e1, e2, _, cfn1, bb) and
|
||||
cfn2 = bb.getANode() and
|
||||
cfn2 = e2.getAControlFlowNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control-flow path from `cfn` to `cfnDef`, where `cfn` is a
|
||||
* control-flow node for `e` and `cfnDef` is a control-flow node for `def`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate hasDefPath(
|
||||
Expr e, ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef
|
||||
) {
|
||||
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
|
||||
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, i, bb) and
|
||||
cfnDef = bb.getNode(j) and
|
||||
def.getExpr().getAControlFlowNode() = cfnDef
|
||||
|
|
||||
isSuccessor = true and j >= i
|
||||
or
|
||||
isSuccessor = false and i >= j
|
||||
)
|
||||
or
|
||||
exists(ControlFlow::BasicBlock bb |
|
||||
this.reachesBasicBlockDefinitionRec(e, def, _, cfn, bb) and
|
||||
def.getExpr().getAControlFlowNode() = cfnDef and
|
||||
cfnDef = bb.getANode()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,12 @@ private import csharp
|
||||
private import DataFlowPublic
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplCommon
|
||||
private import ControlFlowReachability
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.dataflow.FlowSummary as FlowSummary
|
||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||
private import semmle.code.csharp.commons.Collections
|
||||
private import semmle.code.csharp.Conversion
|
||||
private import semmle.code.csharp.exprs.internal.Expr
|
||||
private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
private import semmle.code.csharp.Unification
|
||||
@@ -258,34 +258,16 @@ private module ThisFlow {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control-flow path from `n1` to `n2`. `n2` is either an
|
||||
* expression node or an SSA definition node.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate hasNodePath(ControlFlowReachabilityConfiguration conf, ExprNode n1, Node n2) {
|
||||
exists(ControlFlow::Node cfn1, ControlFlow::Node cfn2 | conf.hasExprPath(_, cfn1, _, cfn2) |
|
||||
cfn1 = n1.getControlFlowNode() and
|
||||
cfn2 = n2.(ExprNode).getControlFlowNode()
|
||||
)
|
||||
or
|
||||
exists(ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef |
|
||||
conf.hasDefPath(_, cfn, def, cfnDef) and
|
||||
cfn = n1.getControlFlowNode() and
|
||||
n2 = TAssignableDefinitionNode(def, cfnDef)
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides logic related to captured variables. */
|
||||
module VariableCapture {
|
||||
private import codeql.dataflow.VariableCapture as Shared
|
||||
private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks
|
||||
|
||||
private predicate closureFlowStep(ControlFlow::Nodes::ExprNode e1, ControlFlow::Nodes::ExprNode e2) {
|
||||
e1 = LocalFlow::getALastEvalNode(e2)
|
||||
e1.getExpr() = LocalFlow::getALastEvalNode(e2.getExpr())
|
||||
or
|
||||
exists(Ssa::Definition def, AssignableDefinition adef |
|
||||
LocalFlow::defAssigns(adef, _, e1) and
|
||||
LocalFlow::defAssigns(adef, _, _, e1) and
|
||||
def.getAnUltimateDefinition().(Ssa::ExplicitDefinition).getADefinition() = adef and
|
||||
exists(def.getAReadAtNode(e2))
|
||||
)
|
||||
@@ -378,7 +360,7 @@ module VariableCapture {
|
||||
this = def.getExpr().getAControlFlowNode()
|
||||
}
|
||||
|
||||
ControlFlow::Node getRhs() { LocalFlow::defAssigns(def, this, result) }
|
||||
ControlFlow::Node getRhs() { LocalFlow::defAssigns(def, this, _, result) }
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
}
|
||||
@@ -527,127 +509,74 @@ module SsaFlow {
|
||||
|
||||
/** Provides predicates related to local data flow. */
|
||||
module LocalFlow {
|
||||
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
LocalExprStepConfiguration() { this = "LocalExprStepConfiguration" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
exactScope = false and
|
||||
(
|
||||
e1 = e2.(ParenthesizedExpr).getExpr() and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e1 = e2.(NullCoalescingExpr).getAnOperand() and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e1 = e2.(SuppressNullableWarningExpr).getExpr() and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e2 =
|
||||
any(ConditionalExpr ce |
|
||||
e1 = ce.getThen() or
|
||||
e1 = ce.getElse()
|
||||
) and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e1 = e2.(Cast).getExpr() and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
// An `=` expression, where the result of the expression is used
|
||||
e2 =
|
||||
any(AssignExpr ae |
|
||||
ae.getParent() = any(ControlFlowElement cfe | not cfe instanceof ExprStmt) and
|
||||
e1 = ae.getRValue()
|
||||
) and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e1 = e2.(ObjectCreation).getInitializer() and
|
||||
scope = e2 and
|
||||
isSuccessor = false
|
||||
or
|
||||
e1 = e2.(ArrayCreation).getInitializer() and
|
||||
scope = e2 and
|
||||
isSuccessor = false
|
||||
or
|
||||
e1 = e2.(SwitchExpr).getACase().getBody() and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e1 = e2.(CheckedExpr).getExpr() and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e1 = e2.(UncheckedExpr).getExpr() and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e1 = e2.(CollectionExpression).getAnElement() and
|
||||
e1 instanceof SpreadElementExpr and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e1 = e2.(SpreadElementExpr).getExpr() and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
exists(WithExpr we |
|
||||
scope = we and
|
||||
isSuccessor = true
|
||||
|
|
||||
e1 = we.getExpr() and
|
||||
e2 = we.getInitializer()
|
||||
or
|
||||
e1 = we.getInitializer() and
|
||||
e2 = we
|
||||
)
|
||||
or
|
||||
scope = any(AssignExpr ae | ae.getLValue().(TupleExpr) = e2 and ae.getRValue() = e1) and
|
||||
isSuccessor = false
|
||||
or
|
||||
isSuccessor = true and
|
||||
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
|
||||
cfe.(IsExpr).getExpr() = e1 and scope = cfe
|
||||
or
|
||||
exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1 and scope = sw)
|
||||
)
|
||||
predicate localExprStep(Expr e1, Expr e2) {
|
||||
e1 = e2.(ParenthesizedExpr).getExpr()
|
||||
or
|
||||
e1 = e2.(NullCoalescingExpr).getAnOperand()
|
||||
or
|
||||
e1 = e2.(SuppressNullableWarningExpr).getExpr()
|
||||
or
|
||||
e2 =
|
||||
any(ConditionalExpr ce |
|
||||
e1 = ce.getThen() or
|
||||
e1 = ce.getElse()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate candidateDef(
|
||||
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
|
||||
boolean isSuccessor
|
||||
) {
|
||||
// Flow from source to definition
|
||||
exactScope = false and
|
||||
def.getSource() = e and
|
||||
(
|
||||
scope = def.getExpr() and
|
||||
isSuccessor = true
|
||||
or
|
||||
scope = def.(AssignableDefinitions::PatternDefinition).getMatch().(IsExpr) and
|
||||
isSuccessor = false
|
||||
or
|
||||
exists(Switch s |
|
||||
s.getACase() = def.(AssignableDefinitions::PatternDefinition).getMatch() and
|
||||
isSuccessor = true
|
||||
|
|
||||
scope = s.getExpr()
|
||||
or
|
||||
scope = s.getACase()
|
||||
)
|
||||
or
|
||||
e1 = e2.(Cast).getExpr()
|
||||
or
|
||||
// An `=` expression, where the result of the expression is used
|
||||
e2 =
|
||||
any(AssignExpr ae |
|
||||
ae.getParent() = any(ControlFlowElement cfe | not cfe instanceof ExprStmt) and
|
||||
e1 = ae.getRValue()
|
||||
)
|
||||
}
|
||||
or
|
||||
e1 = e2.(ObjectCreation).getInitializer()
|
||||
or
|
||||
e1 = e2.(ArrayCreation).getInitializer()
|
||||
or
|
||||
e1 = e2.(SwitchExpr).getACase().getBody()
|
||||
or
|
||||
e1 = e2.(CheckedExpr).getExpr()
|
||||
or
|
||||
e1 = e2.(UncheckedExpr).getExpr()
|
||||
or
|
||||
e1 = e2.(CollectionExpression).getAnElement() and
|
||||
e1 instanceof SpreadElementExpr
|
||||
or
|
||||
e1 = e2.(SpreadElementExpr).getExpr()
|
||||
or
|
||||
exists(WithExpr we |
|
||||
e1 = we.getExpr() and
|
||||
e2 = we.getInitializer()
|
||||
or
|
||||
e1 = we.getInitializer() and
|
||||
e2 = we
|
||||
)
|
||||
or
|
||||
exists(AssignExpr ae | ae.getLValue().(TupleExpr) = e2 and ae.getRValue() = e1)
|
||||
or
|
||||
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
|
||||
cfe.(IsExpr).getExpr() = e1
|
||||
or
|
||||
exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1)
|
||||
)
|
||||
}
|
||||
|
||||
predicate defAssigns(AssignableDefinition def, ControlFlow::Node cfnDef, ControlFlow::Node value) {
|
||||
any(LocalExprStepConfiguration x).hasDefPath(_, value, def, cfnDef)
|
||||
predicate defAssigns(
|
||||
AssignableDefinition def, ControlFlow::Node cfnDef, Expr value, ControlFlow::Node valueCfn
|
||||
) {
|
||||
def.getSource() = value and
|
||||
valueCfn = value.getControlFlowNode() and
|
||||
cfnDef = def.getExpr().getAControlFlowNode()
|
||||
}
|
||||
|
||||
private predicate defAssigns(ExprNode value, AssignableDefinitionNode defNode) {
|
||||
exists(ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef |
|
||||
defAssigns(def, cfnDef, value.getExpr(), _) and
|
||||
cfn = value.getControlFlowNode() and
|
||||
defNode = TAssignableDefinitionNode(def, cfnDef)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -658,7 +587,9 @@ module LocalFlow {
|
||||
}
|
||||
|
||||
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
|
||||
hasNodePath(any(LocalExprStepConfiguration x), nodeFrom, nodeTo)
|
||||
localExprStep(nodeFrom.asExpr(), nodeTo.asExpr())
|
||||
or
|
||||
defAssigns(nodeFrom, nodeTo)
|
||||
or
|
||||
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo) and
|
||||
nodeFrom != nodeTo
|
||||
@@ -684,11 +615,12 @@ module LocalFlow {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that may execute last in `n`, and which, when it executes last,
|
||||
* will be the value of `n`.
|
||||
* Gets a node that may execute last in `e`, and which, when it executes last,
|
||||
* will be the value of `e`.
|
||||
*/
|
||||
ControlFlow::Nodes::ExprNode getALastEvalNode(ControlFlow::Nodes::ExprNode cfn) {
|
||||
exists(Expr e | any(LocalExprStepConfiguration x).hasExprPath(_, result, e, cfn) |
|
||||
Expr getALastEvalNode(Expr e) {
|
||||
localExprStep(result, e) and
|
||||
(
|
||||
e instanceof ConditionalExpr or
|
||||
e instanceof Cast or
|
||||
e instanceof NullCoalescingExpr or
|
||||
@@ -712,9 +644,7 @@ module LocalFlow {
|
||||
* we add a reverse flow step from `[post] b ? x : y` to `[post] x` and to
|
||||
* `[post] y`, in order for the side-effect of `m` to reach both `x` and `y`.
|
||||
*/
|
||||
ControlFlow::Nodes::ExprNode getPostUpdateReverseStep(ControlFlow::Nodes::ExprNode e) {
|
||||
result = getALastEvalNode(e)
|
||||
}
|
||||
Expr getPostUpdateReverseStep(Expr e) { result = getALastEvalNode(e) }
|
||||
|
||||
/**
|
||||
* Holds if the value of `node2` is given by `node1`.
|
||||
@@ -728,9 +658,10 @@ module LocalFlow {
|
||||
e instanceof ThisAccess or e instanceof BaseAccess
|
||||
)
|
||||
or
|
||||
hasNodePath(any(LocalExprStepConfiguration x), node1, node2) and
|
||||
defAssigns(node1, node2)
|
||||
or
|
||||
localExprStep(node1.asExpr(), node2.asExpr()) and
|
||||
(
|
||||
node2 instanceof AssignableDefinitionNode or
|
||||
node2.asExpr() instanceof Cast or
|
||||
node2.asExpr() instanceof AssignExpr
|
||||
)
|
||||
@@ -774,12 +705,8 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
or
|
||||
nodeTo = nodeFrom.(LocalFunctionCreationNode).getAnAccess(true)
|
||||
or
|
||||
nodeTo.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode() =
|
||||
LocalFlow::getPostUpdateReverseStep(nodeFrom
|
||||
.(PostUpdateNode)
|
||||
.getPreUpdateNode()
|
||||
.(ExprNode)
|
||||
.getControlFlowNode())
|
||||
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() =
|
||||
LocalFlow::getPostUpdateReverseStep(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr())
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
@@ -833,11 +760,11 @@ private class Argument extends Expr {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is an assignment of `src` to field or property `c` of `q`.
|
||||
* Holds if there is an assignment of `src` to field or property `c` of `q`.
|
||||
*
|
||||
* `postUpdate` indicates whether the store targets a post-update node.
|
||||
*/
|
||||
private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, boolean postUpdate) {
|
||||
private predicate fieldOrPropertyStore(ContentSet c, Expr src, Expr q, boolean postUpdate) {
|
||||
exists(FieldOrProperty f |
|
||||
c = f.getContentSet() and
|
||||
(
|
||||
@@ -860,25 +787,20 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
|
||||
f = fa.getTarget() and
|
||||
src = def.getSource() and
|
||||
q = fa.getQualifier() and
|
||||
e = def.getExpr() and
|
||||
postUpdate = true
|
||||
)
|
||||
or
|
||||
// `with` expression initializer, `x with { f = src }`
|
||||
e =
|
||||
any(WithExpr we |
|
||||
exists(MemberInitializer mi |
|
||||
q = we and
|
||||
mi = we.getInitializer().getAMemberInitializer() and
|
||||
f = mi.getInitializedMember() and
|
||||
src = mi.getRValue() and
|
||||
postUpdate = false
|
||||
)
|
||||
)
|
||||
exists(WithExpr we, MemberInitializer mi |
|
||||
q = we and
|
||||
mi = we.getInitializer().getAMemberInitializer() and
|
||||
f = mi.getInitializedMember() and
|
||||
src = mi.getRValue() and
|
||||
postUpdate = false
|
||||
)
|
||||
or
|
||||
// Object initializer, `new C() { f = src }`
|
||||
exists(MemberInitializer mi |
|
||||
e = q and
|
||||
mi = q.(ObjectInitializer).getAMemberInitializer() and
|
||||
q.getParent() instanceof ObjectCreation and
|
||||
f = mi.getInitializedMember() and
|
||||
@@ -887,16 +809,13 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
|
||||
)
|
||||
or
|
||||
// Tuple element, `(..., src, ...)` `f` is `ItemX` of tuple `q`
|
||||
e =
|
||||
any(TupleExpr te |
|
||||
exists(int i |
|
||||
e = q and
|
||||
src = te.getArgument(i) and
|
||||
te.isConstruction() and
|
||||
f = q.getType().(TupleType).getElement(i) and
|
||||
postUpdate = false
|
||||
)
|
||||
)
|
||||
exists(TupleExpr te, int i |
|
||||
te = q and
|
||||
src = te.getArgument(i) and
|
||||
te.isConstruction() and
|
||||
f = q.getType().(TupleType).getElement(i) and
|
||||
postUpdate = false
|
||||
)
|
||||
)
|
||||
or
|
||||
// A write to a dynamic property
|
||||
@@ -906,7 +825,6 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
|
||||
c.isDynamicProperty(dp) and
|
||||
src = def.getSource() and
|
||||
q = dma.getQualifier() and
|
||||
e = def.getExpr() and
|
||||
postUpdate = true
|
||||
)
|
||||
}
|
||||
@@ -942,22 +860,20 @@ private predicate collectionStore(Expr src, CollectionExpression ce) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is an expression that adds `src` to array `a`.
|
||||
* Holds if there is an expression that adds `src` to array `a`.
|
||||
*
|
||||
* `postUpdate` indicates whether the store targets a post-update node.
|
||||
*/
|
||||
private predicate arrayStore(Expr e, Expr src, Expr a, boolean postUpdate) {
|
||||
private predicate arrayStore(Expr src, Expr a, boolean postUpdate) {
|
||||
// Direct assignment, `a[i] = src`
|
||||
exists(AssignableDefinition def |
|
||||
a = def.getTargetAccess().(ArrayWrite).getQualifier() and
|
||||
src = def.getSource() and
|
||||
e = def.getExpr() and
|
||||
postUpdate = true
|
||||
)
|
||||
or
|
||||
// Array initializer, `new [] { src }`
|
||||
src = a.(ArrayInitializer).getAnElement() and
|
||||
e = a and
|
||||
postUpdate = false
|
||||
or
|
||||
// Member initializer, `new C { Array = { [i] = src } }`
|
||||
@@ -965,7 +881,6 @@ private predicate arrayStore(Expr e, Expr src, Expr a, boolean postUpdate) {
|
||||
mi = a.(ObjectInitializer).getAMemberInitializer() and
|
||||
mi.getLValue() instanceof ArrayAccess and
|
||||
mi.getRValue() = src and
|
||||
e = a and
|
||||
postUpdate = false
|
||||
)
|
||||
}
|
||||
@@ -1140,17 +1055,17 @@ private module Cached {
|
||||
(
|
||||
cfn.getExpr() instanceof Argument
|
||||
or
|
||||
cfn =
|
||||
LocalFlow::getPostUpdateReverseStep(any(ControlFlow::Nodes::ExprNode e |
|
||||
exists(any(SourcePostUpdateNode p).getPreUpdateNode().asExprAtNode(e))
|
||||
))
|
||||
cfn.getExpr() =
|
||||
LocalFlow::getPostUpdateReverseStep(any(SourcePostUpdateNode p)
|
||||
.getPreUpdateNode()
|
||||
.asExpr())
|
||||
) and
|
||||
exprMayHavePostUpdateNode(cfn.getExpr())
|
||||
or
|
||||
exists(Expr e | e = cfn.getExpr() |
|
||||
fieldOrPropertyStore(_, _, _, e, true)
|
||||
fieldOrPropertyStore(_, _, e, true)
|
||||
or
|
||||
arrayStore(_, _, e, true)
|
||||
arrayStore(_, e, true)
|
||||
or
|
||||
// needed for reverse stores; e.g. `x.f1.f2 = y` induces
|
||||
// a store step of `f1` into `x`
|
||||
@@ -1165,7 +1080,7 @@ private module Cached {
|
||||
)
|
||||
)
|
||||
or
|
||||
lambdaCallExpr(_, cfn)
|
||||
lambdaCallExpr(_, _, cfn)
|
||||
} or
|
||||
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) {
|
||||
sn.getSummarizedCallable() instanceof CallableUsedInSource
|
||||
@@ -1562,35 +1477,15 @@ abstract private class ArgumentNodeImpl extends Node {
|
||||
}
|
||||
|
||||
private module ArgumentNodes {
|
||||
private class ArgumentConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
ArgumentConfiguration() { this = "ArgumentConfiguration" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
e1.(Argument).isArgumentOf(e2, _) and
|
||||
exactScope = false and
|
||||
isSuccessor = true and
|
||||
if e2 instanceof PropertyWrite
|
||||
then
|
||||
exists(AssignableDefinition def |
|
||||
def.getTargetAccess() = e2 and
|
||||
scope = def.getExpr()
|
||||
)
|
||||
else scope = e2
|
||||
}
|
||||
}
|
||||
|
||||
/** A data-flow node that represents an explicit call argument. */
|
||||
class ExplicitArgumentNode extends ArgumentNodeImpl {
|
||||
ExplicitArgumentNode() { this.asExpr() instanceof Argument }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
exists(ArgumentConfiguration x, Expr c, Argument arg |
|
||||
exists(Expr c, Argument arg |
|
||||
arg = this.asExpr() and
|
||||
c = call.getExpr() and
|
||||
arg.isArgumentOf(c, pos) and
|
||||
x.hasExprPath(_, this.getControlFlowNode(), _, call.getControlFlowNode())
|
||||
arg.isArgumentOf(c, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1599,7 +1494,7 @@ private module ArgumentNodes {
|
||||
class DelegateSelfArgumentNode extends ArgumentNodeImpl, ExprNode {
|
||||
private DataFlowCall call_;
|
||||
|
||||
DelegateSelfArgumentNode() { lambdaCallExpr(call_, this.getControlFlowNode()) }
|
||||
DelegateSelfArgumentNode() { lambdaCallExpr(call_, this.getExpr(), _) }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
call = call_ and
|
||||
@@ -1856,27 +1751,6 @@ private module OutNodes {
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectOrCollectionInitializerConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
ObjectOrCollectionInitializerConfiguration() {
|
||||
this = "ObjectOrCollectionInitializerConfiguration"
|
||||
}
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
exactScope = false and
|
||||
scope = e1 and
|
||||
isSuccessor = true and
|
||||
exists(ObjectOrCollectionInitializer init | init = e1.(ObjectCreation).getInitializer() |
|
||||
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
|
||||
e2 = init.(CollectionInitializer).getAnElementInitializer()
|
||||
or
|
||||
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
|
||||
e2 = init.(ObjectInitializer).getAMemberInitializer().getLValue()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that reads a value returned by a callable using an
|
||||
* `out` or `ref` parameter.
|
||||
@@ -2235,30 +2109,6 @@ predicate jumpStep(Node pred, Node succ) {
|
||||
succ = pred.(LocalFunctionCreationNode).getAnAccess(false)
|
||||
}
|
||||
|
||||
private class StoreStepConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
StoreStepConfiguration() { this = "StoreStepConfiguration" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
exactScope = false and
|
||||
fieldOrPropertyStore(scope, _, e1, e2, isSuccessor.booleanNot())
|
||||
or
|
||||
exactScope = false and
|
||||
arrayStore(scope, e1, e2, isSuccessor.booleanNot())
|
||||
or
|
||||
exactScope = false and
|
||||
isSuccessor = true and
|
||||
collectionStore(e1, e2) and
|
||||
scope = e2
|
||||
or
|
||||
exactScope = false and
|
||||
isSuccessor = true and
|
||||
isParamsArg(e2, e1, _) and
|
||||
scope = e2
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ContentSet getResultContent() {
|
||||
result.isProperty(any(SystemThreadingTasksTaskTClass c_).getResultProperty())
|
||||
@@ -2281,21 +2131,17 @@ private predicate recordParameter(RecordType t, Parameter p, string name) {
|
||||
}
|
||||
|
||||
private predicate storeContentStep(Node node1, Content c, Node node2) {
|
||||
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
|
||||
hasNodePath(x, node1, node) and
|
||||
exists(ExprNode node, boolean postUpdate |
|
||||
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
|
||||
|
|
||||
arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
|
||||
arrayStore(node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
|
||||
)
|
||||
or
|
||||
exists(StoreStepConfiguration x | hasNodePath(x, node1, node2) |
|
||||
collectionStore(node1.asExpr(), node2.asExpr()) and c instanceof ElementContent
|
||||
)
|
||||
collectionStore(node1.asExpr(), node2.asExpr()) and c instanceof ElementContent
|
||||
or
|
||||
exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn |
|
||||
x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and
|
||||
node2 = TParamsArgumentNode(callCfn) and
|
||||
isParamsArg(_, arg, _) and
|
||||
exists(Call call |
|
||||
node2 = TParamsArgumentNode(call.getControlFlowNode()) and
|
||||
isParamsArg(call, node1.asExpr(), _) and
|
||||
c instanceof ElementContent
|
||||
)
|
||||
or
|
||||
@@ -2351,11 +2197,10 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
|
||||
c.isSingleton(cont)
|
||||
)
|
||||
or
|
||||
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
|
||||
hasNodePath(x, node1, node) and
|
||||
exists(ExprNode node, boolean postUpdate |
|
||||
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
|
||||
|
|
||||
fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate)
|
||||
fieldOrPropertyStore(c, node1.asExpr(), node.getExpr(), postUpdate)
|
||||
)
|
||||
or
|
||||
exists(Expr e |
|
||||
@@ -2377,123 +2222,51 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
|
||||
storeStepDelegateCall(node1, c, node2)
|
||||
}
|
||||
|
||||
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
ReadStepConfiguration() { this = "ReadStepConfiguration" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
exactScope = false and
|
||||
isSuccessor = true and
|
||||
fieldOrPropertyRead(e1, _, e2) and
|
||||
scope = e2
|
||||
or
|
||||
exactScope = false and
|
||||
isSuccessor = true and
|
||||
dynamicPropertyRead(e1, _, e2) and
|
||||
scope = e2
|
||||
or
|
||||
exactScope = false and
|
||||
isSuccessor = true and
|
||||
arrayRead(e1, e2) and
|
||||
scope = e2
|
||||
or
|
||||
exactScope = false and
|
||||
e1 = e2.(AwaitExpr).getExpr() and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
exactScope = false and
|
||||
e2 = e1.(TupleExpr).getAnArgument() and
|
||||
scope = e1 and
|
||||
isSuccessor = false
|
||||
}
|
||||
|
||||
override predicate candidateDef(
|
||||
Expr e, AssignableDefinition defTo, ControlFlowElement scope, boolean exactScope,
|
||||
boolean isSuccessor
|
||||
) {
|
||||
exists(ForeachStmt fs |
|
||||
e = fs.getIterableExpr() and
|
||||
defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() =
|
||||
fs.getVariableDeclExpr() and
|
||||
isSuccessor = true
|
||||
|
|
||||
scope = fs and
|
||||
exactScope = true
|
||||
or
|
||||
scope = fs.getIterableExpr() and
|
||||
exactScope = false
|
||||
or
|
||||
scope = fs.getVariableDeclExpr() and
|
||||
exactScope = false
|
||||
)
|
||||
or
|
||||
scope =
|
||||
any(AssignExpr ae |
|
||||
ae = defTo.(AssignableDefinitions::TupleAssignmentDefinition).getAssignment() and
|
||||
e = ae.getLValue().getAChildExpr*().(TupleExpr) and
|
||||
exactScope = false and
|
||||
isSuccessor = true
|
||||
)
|
||||
or
|
||||
scope =
|
||||
any(TupleExpr te |
|
||||
te.getAnArgument() = defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() and
|
||||
e = te and
|
||||
exactScope = false and
|
||||
isSuccessor = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate readContentStep(Node node1, Content c, Node node2) {
|
||||
exists(ReadStepConfiguration x |
|
||||
hasNodePath(x, node1, node2) and
|
||||
arrayRead(node1.asExpr(), node2.asExpr()) and
|
||||
arrayRead(node1.asExpr(), node2.asExpr()) and
|
||||
c instanceof ElementContent
|
||||
or
|
||||
exists(
|
||||
ForeachStmt fs, Ssa::ExplicitDefinition def,
|
||||
AssignableDefinitions::LocalVariableDefinition defTo
|
||||
|
|
||||
node1.asExpr() = fs.getIterableExpr() and
|
||||
defTo.getDeclaration() = fs.getVariableDeclExpr() and
|
||||
def.getADefinition() = defTo and
|
||||
node2.(SsaDefinitionNode).getDefinition() = def and
|
||||
c instanceof ElementContent
|
||||
)
|
||||
or
|
||||
node1 =
|
||||
any(InstanceParameterAccessPreNode n |
|
||||
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
|
||||
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter()
|
||||
) and
|
||||
node2.asExpr() instanceof ParameterRead
|
||||
or
|
||||
// node1 = (..., node2, ...)
|
||||
// node1.ItemX flows to node2
|
||||
exists(TupleExpr te, int i, Expr item |
|
||||
te = node1.asExpr() and
|
||||
not te.isConstruction() and
|
||||
c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and
|
||||
// node1 = (..., item, ...)
|
||||
te.getArgument(i) = item
|
||||
|
|
||||
// item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...)
|
||||
node2.asExpr().(TupleExpr) = item
|
||||
or
|
||||
exists(ForeachStmt fs, Ssa::ExplicitDefinition def |
|
||||
x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(),
|
||||
def.getControlFlowNode()) and
|
||||
node2.(SsaDefinitionNode).getDefinition() = def and
|
||||
c instanceof ElementContent
|
||||
// item = variable in node1 = (..., variable, ...)
|
||||
exists(AssignableDefinitions::TupleAssignmentDefinition tad |
|
||||
node2.(AssignableDefinitionNode).getDefinition() = tad and
|
||||
tad.getLeaf() = item
|
||||
)
|
||||
or
|
||||
node1 =
|
||||
any(InstanceParameterAccessPreNode n |
|
||||
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
|
||||
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter()
|
||||
) and
|
||||
node2.asExpr() instanceof ParameterRead
|
||||
or
|
||||
// node1 = (..., node2, ...)
|
||||
// node1.ItemX flows to node2
|
||||
exists(TupleExpr te, int i, Expr item |
|
||||
te = node1.asExpr() and
|
||||
not te.isConstruction() and
|
||||
c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and
|
||||
// node1 = (..., item, ...)
|
||||
te.getArgument(i) = item
|
||||
|
|
||||
// item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...)
|
||||
node2.asExpr().(TupleExpr) = item and
|
||||
hasNodePath(x, node1, node2)
|
||||
or
|
||||
// item = variable in node1 = (..., variable, ...)
|
||||
exists(AssignableDefinitions::TupleAssignmentDefinition tad |
|
||||
node2.(AssignableDefinitionNode).getDefinition() = tad and
|
||||
tad.getLeaf() = item and
|
||||
hasNodePath(x, node1, node2)
|
||||
)
|
||||
or
|
||||
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
|
||||
te = any(PatternExpr pe).getAChildExpr*() and
|
||||
exists(AssignableDefinitions::LocalVariableDefinition lvd |
|
||||
node2.(AssignableDefinitionNode).getDefinition() = lvd and
|
||||
lvd.getDeclaration() = item and
|
||||
hasNodePath(x, node1, node2)
|
||||
)
|
||||
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
|
||||
isPatternExprDescendant(te) and
|
||||
exists(AssignableDefinitions::LocalVariableDefinition lvd |
|
||||
node2.(AssignableDefinitionNode).getDefinition() = lvd and
|
||||
lvd.getDeclaration() = item
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -2524,14 +2297,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
c.isSingleton(cont)
|
||||
)
|
||||
or
|
||||
exists(ReadStepConfiguration x | hasNodePath(x, node1, node2) |
|
||||
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
|
||||
or
|
||||
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
|
||||
or
|
||||
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
|
||||
c = getResultContent()
|
||||
)
|
||||
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
|
||||
or
|
||||
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
|
||||
or
|
||||
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
|
||||
c = getResultContent()
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode())
|
||||
@@ -2565,9 +2336,9 @@ predicate clearsContent(Node n, ContentSet c) {
|
||||
c.isSingleton(cont)
|
||||
)
|
||||
or
|
||||
fieldOrPropertyStore(_, c, _, n.asExpr(), true)
|
||||
fieldOrPropertyStore(c, _, n.asExpr(), true)
|
||||
or
|
||||
fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false)
|
||||
fieldOrPropertyStore(c, _, n.(ObjectInitializerNode).getInitializer(), false)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
|
||||
or
|
||||
@@ -2806,8 +2577,13 @@ module PostUpdateNodes {
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
pos.isQualifier() and
|
||||
any(ObjectOrCollectionInitializerConfiguration x)
|
||||
.hasExprPath(_, cfn, _, call.getControlFlowNode())
|
||||
exists(ObjectOrCollectionInitializer init | init = oc.getInitializer() |
|
||||
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
|
||||
call.getExpr() = init.(CollectionInitializer).getAnElementInitializer()
|
||||
or
|
||||
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
|
||||
call.getExpr() = init.(ObjectInitializer).getAMemberInitializer().getLValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlowCallable getEnclosingCallableImpl() {
|
||||
@@ -2969,45 +2745,26 @@ private predicate isLocalFunctionCallReceiver(
|
||||
f = receiver.getTarget().getUnboundDeclaration()
|
||||
}
|
||||
|
||||
private class LambdaConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
LambdaConfiguration() { this = "LambdaConfiguration" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
e1 = e2.(DelegateLikeCall).getExpr() and
|
||||
exactScope = false and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
e1 = e2.(DelegateCreation).getArgument() and
|
||||
exactScope = false and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
or
|
||||
isLocalFunctionCallReceiver(e2, e1, _) and
|
||||
exactScope = false and
|
||||
scope = e2 and
|
||||
isSuccessor = true
|
||||
}
|
||||
}
|
||||
|
||||
private predicate lambdaCallExpr(DataFlowCall call, ControlFlow::Node receiver) {
|
||||
exists(LambdaConfiguration x, DelegateLikeCall dc |
|
||||
x.hasExprPath(dc.getExpr(), receiver, dc, call.getControlFlowNode())
|
||||
private predicate lambdaCallExpr(DataFlowCall call, Expr receiver, ControlFlow::Node receiverCfn) {
|
||||
exists(DelegateLikeCall dc |
|
||||
call.(ExplicitDelegateLikeDataFlowCall).getCall() = dc and
|
||||
receiver = dc.getExpr() and
|
||||
receiverCfn = receiver.getControlFlowNode()
|
||||
)
|
||||
or
|
||||
// In local function calls, `F()`, we use the local function access `F`
|
||||
// to represent the receiver. Only needed for flow through captured variables.
|
||||
exists(LambdaConfiguration x, LocalFunctionCall fc |
|
||||
x.hasExprPath(fc.getAChild(), receiver, fc, call.getControlFlowNode())
|
||||
exists(LocalFunctionCall fc |
|
||||
receiver = fc.getAChild() and
|
||||
receiverCfn = receiver.getControlFlowNode() and
|
||||
fc.getControlFlowNode() = call.getControlFlowNode()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `call` is a lambda call where `receiver` is the lambda expression. */
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
|
||||
(
|
||||
lambdaCallExpr(call, receiver.(ExprNode).getControlFlowNode()) and
|
||||
lambdaCallExpr(call, receiver.asExpr(), _) and
|
||||
// local function calls can be resolved directly without a flow analysis
|
||||
not call.getControlFlowNode().getAstNode() instanceof LocalFunctionCall
|
||||
or
|
||||
@@ -3017,9 +2774,9 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
|
||||
}
|
||||
|
||||
private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(LambdaConfiguration x, DelegateCreation dc |
|
||||
x.hasExprPath(dc.getArgument(), nodeFrom.(ExprNode).getControlFlowNode(), dc,
|
||||
nodeTo.(ExprNode).getControlFlowNode())
|
||||
exists(DelegateCreation dc |
|
||||
dc.getArgument() = nodeFrom.asExpr() and
|
||||
dc = nodeTo.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -239,12 +239,25 @@ module ModelValidation {
|
||||
)
|
||||
}
|
||||
|
||||
private string getIncorrectConstructorSummaryOutput() {
|
||||
exists(string namespace, string type, string name, string output |
|
||||
type = name or
|
||||
type = name + "<" + any(string s)
|
||||
|
|
||||
summaryModel(namespace, type, _, name, _, _, _, output, _, _, _) and
|
||||
output.matches("ReturnValue%") and
|
||||
result =
|
||||
"Constructor model for " + namespace + "." + type +
|
||||
" should use `Argument[this]` in the output, not `ReturnValue`."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a MaD flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
msg =
|
||||
[
|
||||
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
|
||||
KindVal::getInvalidModelKind()
|
||||
getIncorrectConstructorSummaryOutput(), KindVal::getInvalidModelKind()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.Caching
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.csharp.dataflow.internal.ControlFlowReachability
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import semmle.code.csharp.commons.ComparisonTest
|
||||
// import `TaintedMember` definitions from other files to avoid potential reevaluation
|
||||
@@ -45,72 +44,58 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c)
|
||||
)
|
||||
}
|
||||
|
||||
private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityConfiguration {
|
||||
LocalTaintExprStepConfiguration() { this = "LocalTaintExprStepConfiguration" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
exactScope = false and
|
||||
isSuccessor = true and
|
||||
(
|
||||
e1 = e2.(ElementAccess).getQualifier() and
|
||||
scope = e2
|
||||
or
|
||||
e1 = e2.(AddExpr).getAnOperand() and
|
||||
scope = e2
|
||||
or
|
||||
// A comparison expression where taint can flow from one of the
|
||||
// operands if the other operand is a constant value.
|
||||
exists(ComparisonTest ct, Expr other |
|
||||
ct.getExpr() = e2 and
|
||||
e1 = ct.getAnArgument() and
|
||||
other = ct.getAnArgument() and
|
||||
other.stripCasts().hasValue() and
|
||||
e1 != other and
|
||||
scope = e2
|
||||
)
|
||||
or
|
||||
e1 = e2.(UnaryLogicalOperation).getAnOperand() and
|
||||
scope = e2
|
||||
or
|
||||
e1 = e2.(BinaryLogicalOperation).getAnOperand() and
|
||||
scope = e2
|
||||
or
|
||||
e1 = e2.(InterpolatedStringExpr).getAChild() and
|
||||
scope = e2
|
||||
or
|
||||
e1 = e2.(InterpolatedStringInsertExpr).getInsert() and
|
||||
scope = e2
|
||||
or
|
||||
e2 =
|
||||
any(OperatorCall oc |
|
||||
oc.getTarget().(ConversionOperator).fromLibrary() and
|
||||
e1 = oc.getAnArgument() and
|
||||
scope = e2
|
||||
)
|
||||
or
|
||||
e1 = e2.(AwaitExpr).getExpr() and
|
||||
scope = e2
|
||||
or
|
||||
// Taint flows from the operand of a cast to the cast expression if the cast is to an interpolated string handler.
|
||||
e2 =
|
||||
any(CastExpr ce |
|
||||
e1 = ce.getExpr() and
|
||||
scope = ce and
|
||||
ce.getTargetType()
|
||||
.(Attributable)
|
||||
.getAnAttribute()
|
||||
.getType()
|
||||
.hasFullyQualifiedName("System.Runtime.CompilerServices",
|
||||
"InterpolatedStringHandlerAttribute")
|
||||
)
|
||||
private predicate localTaintExprStep(Expr e1, Expr e2) {
|
||||
e1 = e2.(ElementAccess).getQualifier()
|
||||
or
|
||||
e1 = e2.(AddExpr).getAnOperand()
|
||||
or
|
||||
// A comparison expression where taint can flow from one of the
|
||||
// operands if the other operand is a constant value.
|
||||
exists(ComparisonTest ct, Expr other |
|
||||
ct.getExpr() = e2 and
|
||||
e1 = ct.getAnArgument() and
|
||||
other = ct.getAnArgument() and
|
||||
other.stripCasts().hasValue() and
|
||||
e1 != other
|
||||
)
|
||||
or
|
||||
e1 = e2.(UnaryLogicalOperation).getAnOperand()
|
||||
or
|
||||
e1 = e2.(BinaryLogicalOperation).getAnOperand()
|
||||
or
|
||||
e1 = e2.(InterpolatedStringExpr).getAChild()
|
||||
or
|
||||
e1 = e2.(InterpolatedStringInsertExpr).getInsert()
|
||||
or
|
||||
e2 =
|
||||
any(OperatorCall oc |
|
||||
oc.getTarget().(ConversionOperator).fromLibrary() and
|
||||
e1 = oc.getAnArgument()
|
||||
)
|
||||
or
|
||||
e1 = e2.(AwaitExpr).getExpr()
|
||||
or
|
||||
// Taint flows from the operand of a cast to the cast expression if the cast is to an interpolated string handler.
|
||||
e2 =
|
||||
any(CastExpr ce |
|
||||
e1 = ce.getExpr() and
|
||||
ce.getTargetType()
|
||||
.(Attributable)
|
||||
.getAnAttribute()
|
||||
.getType()
|
||||
.hasFullyQualifiedName("System.Runtime.CompilerServices",
|
||||
"InterpolatedStringHandlerAttribute")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private Expr getALastEvalNode(OperatorCall oc) {
|
||||
localTaintExprStep(result, oc) and oc.getTarget() instanceof ImplicitConversionOperator
|
||||
}
|
||||
|
||||
private Expr getPostUpdateReverseStep(Expr e) { result = getALastEvalNode(e) }
|
||||
|
||||
private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
hasNodePath(any(LocalTaintExprStepConfiguration x), nodeFrom, nodeTo)
|
||||
localTaintExprStep(nodeFrom.asExpr(), nodeTo.asExpr())
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -177,6 +162,12 @@ private module Cached {
|
||||
readStep(nodeFrom, any(DataFlow::ContentSet c | c.isElement()), nodeTo)
|
||||
or
|
||||
nodeTo = nodeFrom.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
|
||||
or
|
||||
// Allow reverse update flow for implicit conversion operator calls.
|
||||
// This is needed to support flow out of method call arguments, where an implicit conversion is applied
|
||||
// to a call argument.
|
||||
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() =
|
||||
getPostUpdateReverseStep(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr())
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
import csharp
|
||||
|
||||
private class ControlFlowScope extends ControlFlowElement {
|
||||
private boolean exactScope;
|
||||
|
||||
ControlFlowScope() {
|
||||
exists(ControlFlowReachabilityConfiguration c |
|
||||
c.candidate(_, _, this, exactScope, _) or
|
||||
c.candidateDef(_, _, this, exactScope, _)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExact() { exactScope = true }
|
||||
|
||||
predicate isNonExact() { exactScope = false }
|
||||
}
|
||||
|
||||
private ControlFlowElement getANonExactScopeChild(ControlFlowScope scope) {
|
||||
scope.isNonExact() and
|
||||
result = scope
|
||||
or
|
||||
result = getANonExactScopeChild(scope).getAChild()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) {
|
||||
result.getANode().getAstNode() = getANonExactScopeChild(scope) and
|
||||
exactScope = false
|
||||
or
|
||||
scope.isExact() and
|
||||
result.getANode().getAstNode() = scope and
|
||||
exactScope = true
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class for determining control-flow reachability for pairs of
|
||||
* elements.
|
||||
*
|
||||
* This is useful when defining for example expression-based data-flow steps in
|
||||
* the presence of control-flow splitting, where a data-flow step should make
|
||||
* sure to stay in the same split.
|
||||
*
|
||||
* For example, in
|
||||
*
|
||||
* ```csharp
|
||||
* if (b)
|
||||
* ....
|
||||
* var x = "foo";
|
||||
* if (b)
|
||||
* ....
|
||||
* ```
|
||||
*
|
||||
* there should only be steps from `[b = true] "foo"` to `[b = true] SSA def(x)`
|
||||
* and `[b = false] "foo"` to `[b = false] SSA def(x)`, and for example not from
|
||||
* `[b = true] "foo"` to `[b = false] SSA def(x)`
|
||||
*/
|
||||
abstract class ControlFlowReachabilityConfiguration extends string {
|
||||
bindingset[this]
|
||||
ControlFlowReachabilityConfiguration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `e1` and `e2` are expressions for which we want to find a
|
||||
* control-flow path that follows control flow successors (resp.
|
||||
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
|
||||
* `scope`. The Boolean `exactScope` indicates whether a transitive child
|
||||
* of `scope` is allowed (`exactScope = false`).
|
||||
*/
|
||||
predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` and `def` are elements for which we want to find a
|
||||
* control-flow path that follows control flow successors (resp.
|
||||
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
|
||||
* `scope`. The Boolean `exactScope` indicates whether a transitive child
|
||||
* of `scope` is allowed (`exactScope = false`).
|
||||
*/
|
||||
predicate candidateDef(
|
||||
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
|
||||
boolean isSuccessor
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockExprBase(
|
||||
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1, int i,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.candidate(e1, e2, _, _, isSuccessor) and
|
||||
cfn1 = e1.getAControlFlowNode() and
|
||||
bb.getNode(i) = cfn1
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockExprRec(
|
||||
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
exists(ControlFlow::BasicBlock mid |
|
||||
this.reachesBasicBlockExpr(e1, e2, isSuccessor, cfn1, mid)
|
||||
|
|
||||
isSuccessor = true and
|
||||
bb = mid.getASuccessor()
|
||||
or
|
||||
isSuccessor = false and
|
||||
bb = mid.getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockExpr(
|
||||
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, _, bb)
|
||||
or
|
||||
exists(ControlFlowElement scope, boolean exactScope |
|
||||
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
|
||||
this.reachesBasicBlockExprRec(e1, e2, isSuccessor, cfn1, bb) and
|
||||
bb = getABasicBlockInScope(scope, exactScope)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockDefinitionBase(
|
||||
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
|
||||
int i, ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.candidateDef(e, def, _, _, isSuccessor) and
|
||||
cfn = e.getAControlFlowNode() and
|
||||
bb.getNode(i) = cfn
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockDefinitionRec(
|
||||
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
exists(ControlFlow::BasicBlock mid |
|
||||
this.reachesBasicBlockDefinition(e, def, isSuccessor, cfn, mid)
|
||||
|
|
||||
isSuccessor = true and
|
||||
bb = mid.getASuccessor()
|
||||
or
|
||||
isSuccessor = false and
|
||||
bb = mid.getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reachesBasicBlockDefinition(
|
||||
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
|
||||
ControlFlow::BasicBlock bb
|
||||
) {
|
||||
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, _, bb)
|
||||
or
|
||||
exists(ControlFlowElement scope, boolean exactScope |
|
||||
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
|
||||
this.reachesBasicBlockDefinitionRec(e, def, isSuccessor, cfn, bb) and
|
||||
bb = getABasicBlockInScope(scope, exactScope)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control-flow path from `cfn1` to `cfn2`, where `cfn1` is a
|
||||
* control-flow node for `e1` and `cfn2` is a control-flow node for `e2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate hasExprPath(Expr e1, ControlFlow::Node cfn1, Expr e2, ControlFlow::Node cfn2) {
|
||||
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
|
||||
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, i, bb) and
|
||||
cfn2 = bb.getNode(j) and
|
||||
cfn2 = e2.getAControlFlowNode()
|
||||
|
|
||||
isSuccessor = true and j >= i
|
||||
or
|
||||
isSuccessor = false and i >= j
|
||||
)
|
||||
or
|
||||
exists(ControlFlow::BasicBlock bb |
|
||||
this.reachesBasicBlockExprRec(e1, e2, _, cfn1, bb) and
|
||||
cfn2 = bb.getANode() and
|
||||
cfn2 = e2.getAControlFlowNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control-flow path from `cfn` to `cfnDef`, where `cfn` is a
|
||||
* control-flow node for `e` and `cfnDef` is a control-flow node for `def`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate hasDefPath(
|
||||
Expr e, ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef
|
||||
) {
|
||||
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
|
||||
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, i, bb) and
|
||||
cfnDef = bb.getNode(j) and
|
||||
def.getExpr().getAControlFlowNode() = cfnDef
|
||||
|
|
||||
isSuccessor = true and j >= i
|
||||
or
|
||||
isSuccessor = false and i >= j
|
||||
)
|
||||
or
|
||||
exists(ControlFlow::BasicBlock bb |
|
||||
this.reachesBasicBlockDefinitionRec(e, def, _, cfn, bb) and
|
||||
def.getExpr().getAControlFlowNode() = cfnDef and
|
||||
cfnDef = bb.getANode()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -8,26 +8,14 @@ private module Impl {
|
||||
private import ConstantUtils
|
||||
private import SsaReadPositionCommon
|
||||
private import semmle.code.csharp.controlflow.Guards as G
|
||||
private import ControlFlowReachability
|
||||
|
||||
private class ExprNode = ControlFlow::Nodes::ExprNode;
|
||||
|
||||
private class ExprChildReachability extends ControlFlowReachabilityConfiguration {
|
||||
ExprChildReachability() { this = "ExprChildReachability" }
|
||||
|
||||
override predicate candidate(
|
||||
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
|
||||
) {
|
||||
e2 = e1.getAChild() and
|
||||
scope = e1 and
|
||||
exactScope = false and
|
||||
isSuccessor in [false, true]
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `parent` having child `child` implies `parentNode` having child `childNode`. */
|
||||
predicate hasChild(Expr parent, Expr child, ExprNode parentNode, ExprNode childNode) {
|
||||
any(ExprChildReachability x).hasExprPath(parent, parentNode, child, childNode)
|
||||
parent.getAChild() = child and
|
||||
parentNode = parent.getControlFlowNode() and
|
||||
childNode = child.getControlFlowNode()
|
||||
}
|
||||
|
||||
/** Holds if SSA definition `def` equals `e + delta`. */
|
||||
|
||||
@@ -11,7 +11,7 @@ import Expr
|
||||
* (`LocalVariableDeclAndInitExpr`), a simple assignment (`AssignExpr`), or
|
||||
* an assignment operation (`AssignOperation`).
|
||||
*/
|
||||
class Assignment extends Operation, @assign_expr {
|
||||
class Assignment extends BinaryOperation, @assign_expr {
|
||||
Assignment() {
|
||||
this instanceof LocalVariableDeclExpr
|
||||
implies
|
||||
@@ -20,6 +20,10 @@ class Assignment extends Operation, @assign_expr {
|
||||
expr_parent(_, 0, this)
|
||||
}
|
||||
|
||||
override Expr getLeftOperand() { result = this.getChild(1) }
|
||||
|
||||
override Expr getRightOperand() { result = this.getChild(0) }
|
||||
|
||||
/** Gets the left operand of this assignment. */
|
||||
Expr getLValue() { result = this.getChild(1) }
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import semmle.code.csharp.Type
|
||||
private import semmle.code.csharp.ExprOrStmtParent
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.TypeRef
|
||||
private import internal.Expr
|
||||
|
||||
/**
|
||||
* An expression. Either an access (`Access`), a call (`Call`), an object or
|
||||
@@ -64,14 +65,24 @@ class Expr extends ControlFlowElement, @expr {
|
||||
/** Gets the enclosing callable of this expression, if any. */
|
||||
override Callable getEnclosingCallable() { enclosingCallable(this, result) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isExpandedAssignmentRValueDescendant() {
|
||||
this =
|
||||
any(AssignOperation op).getExpandedAssignment().getRValue().getChildExpr(0).getAChildExpr()
|
||||
or
|
||||
exists(Expr parent |
|
||||
parent.isExpandedAssignmentRValueDescendant() and
|
||||
this = parent.getAChildExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this expression is generated by the compiler and does not appear
|
||||
* explicitly in the source code.
|
||||
*/
|
||||
final predicate isImplicit() {
|
||||
compiler_generated(this) or
|
||||
this =
|
||||
any(AssignOperation op).getExpandedAssignment().getRValue().getChildExpr(0).getAChildExpr+()
|
||||
this.isExpandedAssignmentRValueDescendant()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,7 +244,8 @@ class UnaryOperation extends Operation, @un_op {
|
||||
* A binary operation. Either a binary arithmetic operation
|
||||
* (`BinaryArithmeticOperation`), a binary bitwise operation
|
||||
* (`BinaryBitwiseOperation`), a comparison operation (`ComparisonOperation`),
|
||||
* or a binary logical operation (`BinaryLogicalOperation`).
|
||||
* a binary logical operation (`BinaryLogicalOperation`), or an
|
||||
* assignment (`Assignment`).
|
||||
*/
|
||||
class BinaryOperation extends Operation, @bin_op {
|
||||
/** Gets the left operand of this binary operation. */
|
||||
@@ -1133,7 +1145,7 @@ class TupleExpr extends Expr, @tuple_expr {
|
||||
/** Holds if this expression is a tuple construction. */
|
||||
predicate isConstruction() {
|
||||
not this = getAnAssignOrForeachChild() and
|
||||
not this = any(PatternExpr pe).getAChildExpr*()
|
||||
not isPatternExprDescendant(this)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TupleExpr" }
|
||||
|
||||
11
csharp/ql/lib/semmle/code/csharp/exprs/internal/Expr.qll
Normal file
11
csharp/ql/lib/semmle/code/csharp/exprs/internal/Expr.qll
Normal file
@@ -0,0 +1,11 @@
|
||||
private import csharp
|
||||
|
||||
pragma[nomagic]
|
||||
predicate isPatternExprDescendant(Expr e) {
|
||||
e instanceof PatternExpr
|
||||
or
|
||||
exists(Expr parent |
|
||||
isPatternExprDescendant(parent) and
|
||||
e = parent.getAChildExpr()
|
||||
)
|
||||
}
|
||||
@@ -1261,10 +1261,10 @@ case @expr.kind of
|
||||
@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr;
|
||||
@comp_expr = @equality_op_expr | @rel_op_expr;
|
||||
|
||||
@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op;
|
||||
@op_expr = @un_op | @bin_op | @ternary_op;
|
||||
|
||||
@ternary_op = @ternary_log_op_expr;
|
||||
@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr;
|
||||
@bin_op = @assign_expr | @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr;
|
||||
@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr
|
||||
| @pointer_indirection_expr | @address_of_expr;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Expand @bin_op union to include @assign_expr
|
||||
compatibility: full
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.6.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.6.2
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -15,23 +15,6 @@
|
||||
|
||||
import csharp
|
||||
|
||||
/** An expression containing a qualified member access, a method call, or an array access. */
|
||||
class DangerousExpression extends Expr {
|
||||
DangerousExpression() {
|
||||
exists(Expr e | this = e.getParent*() |
|
||||
exists(Expr q | q = e.(MemberAccess).getQualifier() |
|
||||
not q instanceof ThisAccess and
|
||||
not q instanceof BaseAccess
|
||||
)
|
||||
or
|
||||
e instanceof MethodCall
|
||||
or
|
||||
e instanceof ArrayAccess
|
||||
) and
|
||||
not exists(Expr e | this = e.getParent*() | e.(Call).getTarget().getAParameter().isOutOrRef())
|
||||
}
|
||||
}
|
||||
|
||||
/** A use of `&` or `|` on operands of type boolean. */
|
||||
class NonShortCircuit extends BinaryBitwiseOperation {
|
||||
NonShortCircuit() {
|
||||
@@ -42,10 +25,40 @@ class NonShortCircuit extends BinaryBitwiseOperation {
|
||||
) and
|
||||
not exists(AssignBitwiseOperation abo | abo.getExpandedAssignment().getRValue() = this) and
|
||||
this.getLeftOperand().getType() instanceof BoolType and
|
||||
this.getRightOperand().getType() instanceof BoolType and
|
||||
this.getRightOperand() instanceof DangerousExpression
|
||||
this.getRightOperand().getType() instanceof BoolType
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasRightOperandDescendant(Expr e) {
|
||||
e = this.getRightOperand()
|
||||
or
|
||||
exists(Expr parent |
|
||||
this.hasRightOperandDescendant(parent) and
|
||||
e.getParent() = parent
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this non-short-circuit expression contains a qualified member access,
|
||||
* a method call, or an array access inside the right operand.
|
||||
*/
|
||||
predicate isDangerous() {
|
||||
exists(Expr e | this.hasRightOperandDescendant(e) |
|
||||
exists(Expr q | q = e.(MemberAccess).getQualifier() |
|
||||
not q instanceof ThisAccess and
|
||||
not q instanceof BaseAccess
|
||||
)
|
||||
or
|
||||
e instanceof MethodCall
|
||||
or
|
||||
e instanceof ArrayAccess
|
||||
) and
|
||||
not exists(Expr e | this.hasRightOperandDescendant(e) |
|
||||
e.(Call).getTarget().getAParameter().isOutOrRef()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from NonShortCircuit e
|
||||
where e.isDangerous()
|
||||
select e, "Potentially dangerous use of non-short circuit logic."
|
||||
|
||||
3
csharp/ql/src/change-notes/released/1.6.3.md
Normal file
3
csharp/ql/src/change-notes/released/1.6.3.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.6.3
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.6.2
|
||||
lastReleaseVersion: 1.6.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 1.6.3-dev
|
||||
version: 1.6.4-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
| CSharp7.cs:31:16:31:16 | access to parameter i | CSharp7.cs:31:16:31:20 | ... > ... |
|
||||
| CSharp7.cs:31:16:31:16 | access to parameter i | CSharp7.cs:31:24:31:24 | access to parameter i |
|
||||
| CSharp7.cs:31:24:31:24 | access to parameter i | CSharp7.cs:31:16:31:59 | ... ? ... : ... |
|
||||
| CSharp7.cs:31:28:31:59 | throw ... | CSharp7.cs:31:16:31:59 | ... ? ... : ... |
|
||||
| CSharp7.cs:35:7:35:18 | this | CSharp7.cs:35:7:35:18 | this access |
|
||||
| CSharp7.cs:39:9:39:9 | access to parameter x | CSharp7.cs:39:9:39:21 | SSA def(x) |
|
||||
| CSharp7.cs:39:13:39:21 | "tainted" | CSharp7.cs:39:9:39:9 | access to parameter x |
|
||||
@@ -253,6 +254,7 @@
|
||||
| CSharp7.cs:233:13:233:13 | access to local variable o | CSharp7.cs:235:13:235:42 | [input] SSA phi read(o) |
|
||||
| CSharp7.cs:233:13:233:13 | access to local variable o | CSharp7.cs:237:18:237:18 | access to local variable o |
|
||||
| CSharp7.cs:233:13:233:23 | [false] ... is ... | CSharp7.cs:233:13:233:33 | [false] ... && ... |
|
||||
| CSharp7.cs:233:13:233:23 | [false] ... is ... | CSharp7.cs:233:13:233:33 | [true] ... && ... |
|
||||
| CSharp7.cs:233:13:233:23 | [true] ... is ... | CSharp7.cs:233:13:233:33 | [false] ... && ... |
|
||||
| CSharp7.cs:233:13:233:23 | [true] ... is ... | CSharp7.cs:233:13:233:33 | [true] ... && ... |
|
||||
| CSharp7.cs:233:18:233:23 | Int32 i1 | CSharp7.cs:233:18:233:23 | SSA def(i1) |
|
||||
@@ -338,6 +340,8 @@
|
||||
| CSharp7.cs:297:35:297:35 | access to local variable x | CSharp7.cs:297:40:297:44 | Int32 y |
|
||||
| CSharp7.cs:297:35:297:35 | access to local variable x | CSharp7.cs:297:49:297:49 | access to local variable x |
|
||||
| CSharp7.cs:297:35:297:44 | [false] ... is ... | CSharp7.cs:297:25:297:44 | [false] ... && ... |
|
||||
| CSharp7.cs:297:35:297:44 | [false] ... is ... | CSharp7.cs:297:25:297:44 | [true] ... && ... |
|
||||
| CSharp7.cs:297:35:297:44 | [true] ... is ... | CSharp7.cs:297:25:297:44 | [false] ... && ... |
|
||||
| CSharp7.cs:297:35:297:44 | [true] ... is ... | CSharp7.cs:297:25:297:44 | [true] ... && ... |
|
||||
| CSharp7.cs:297:40:297:44 | Int32 y | CSharp7.cs:297:40:297:44 | SSA def(y) |
|
||||
| CSharp7.cs:297:40:297:44 | SSA def(y) | CSharp7.cs:299:31:299:31 | access to local variable y |
|
||||
|
||||
@@ -44,5 +44,13 @@ namespace RemoteFlowSource
|
||||
{
|
||||
Use(request.Unvalidated.RawUrl);
|
||||
}
|
||||
|
||||
public static async void M3(System.Net.WebSockets.WebSocket webSocket)
|
||||
{
|
||||
var buffer = new byte[1024];
|
||||
var segment = new ArraySegment<byte>(buffer);
|
||||
var result = await webSocket.ReceiveAsync(segment, System.Threading.CancellationToken.None);
|
||||
Use(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,3 +9,4 @@
|
||||
| RemoteFlowSource.cs:40:17:40:23 | access to parameter request | ASP.NET query string |
|
||||
| RemoteFlowSource.cs:45:17:45:23 | access to parameter request | ASP.NET query string |
|
||||
| RemoteFlowSource.cs:45:17:45:42 | access to property RawUrl | ASP.NET unvalidated request data |
|
||||
| RemoteFlowSource.cs:52:55:52:61 | [post] access to local variable segment | external |
|
||||
|
||||
@@ -185,6 +185,10 @@ source
|
||||
| System.IO;StreamWriter;StreamWriter;(System.String,System.IO.FileStreamOptions);Argument[this];file-write;manual |
|
||||
| System.IO;StreamWriter;StreamWriter;(System.String,System.Text.Encoding,System.IO.FileStreamOptions);Argument[this];file-write;manual |
|
||||
| System.Net.Sockets;TcpClient;GetStream;();ReturnValue;remote;manual |
|
||||
| System.Net.WebSockets;ClientWebSocket;ReceiveAsync;(System.ArraySegment<System.Byte>,System.Threading.CancellationToken);Argument[0];remote;manual |
|
||||
| System.Net.WebSockets;ClientWebSocket;ReceiveAsync;(System.Memory<System.Byte>,System.Threading.CancellationToken);Argument[0];remote;manual |
|
||||
| System.Net.WebSockets;WebSocket;ReceiveAsync;(System.ArraySegment<System.Byte>,System.Threading.CancellationToken);Argument[0];remote;manual |
|
||||
| System.Net.WebSockets;WebSocket;ReceiveAsync;(System.Memory<System.Byte>,System.Threading.CancellationToken);Argument[0];remote;manual |
|
||||
| System;Console;Read;();ReturnValue;stdin;manual |
|
||||
| System;Console;ReadKey;();ReturnValue;stdin;manual |
|
||||
| System;Console;ReadKey;(System.Boolean);ReturnValue;stdin;manual |
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
public class TestImplicitConversionOperator
|
||||
{
|
||||
static void Sink(object o) { }
|
||||
static void TaintArgument(ArraySegment<byte> segment) { }
|
||||
|
||||
public void M1()
|
||||
{
|
||||
byte[] bytes = new byte[1];
|
||||
TaintArgument(bytes);
|
||||
Sink(bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
edges
|
||||
| ImplicitConversionOperator.cs:11:23:11:27 | [post] call to operator implicit conversion : ArraySegment<Byte> | ImplicitConversionOperator.cs:12:14:12:18 | access to local variable bytes | provenance | |
|
||||
nodes
|
||||
| ImplicitConversionOperator.cs:11:23:11:27 | [post] call to operator implicit conversion : ArraySegment<Byte> | semmle.label | [post] call to operator implicit conversion : ArraySegment<Byte> |
|
||||
| ImplicitConversionOperator.cs:12:14:12:18 | access to local variable bytes | semmle.label | access to local variable bytes |
|
||||
subpaths
|
||||
#select
|
||||
| ImplicitConversionOperator.cs:12:14:12:18 | access to local variable bytes | ImplicitConversionOperator.cs:11:23:11:27 | [post] call to operator implicit conversion : ArraySegment<Byte> | ImplicitConversionOperator.cs:12:14:12:18 | access to local variable bytes | $@ | ImplicitConversionOperator.cs:11:23:11:27 | [post] call to operator implicit conversion : ArraySegment<Byte> | [post] call to operator implicit conversion : ArraySegment<Byte> |
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
import Taint::PathGraph
|
||||
|
||||
module TaintConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget().hasName("TaintArgument") and
|
||||
mc.getAnArgument() = src.(DataFlowPrivate::PostUpdateNode).getPreUpdateNode().asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget().hasName("Sink") and
|
||||
mc.getAnArgument() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Taint = TaintTracking::Global<TaintConfig>;
|
||||
|
||||
from Taint::PathNode source, Taint::PathNode sink
|
||||
where Taint::flowPath(source, sink)
|
||||
select sink, source, sink, "$@", source, source.toString()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user