Compare commits

..

15 Commits

Author SHA1 Message Date
Andrew Eisenberg
7ccb803dd1 Merge pull request #6375 from github/aeisenberg/pack/python
Packaging: Refactor Python into separate library and query packs
2021-08-16 10:59:19 -07:00
Andrew Eisenberg
016eed8e5c Packaging: Fix more suite references 2021-08-12 16:14:04 -07:00
Andrew Eisenberg
c75f65c493 Packaging: Fix identical-files.json 2021-08-11 19:21:04 -07:00
Andrew Eisenberg
210128e2ae Packaging: Update csharp libraries from main 2021-08-11 16:33:32 -07:00
Andrew Eisenberg
1e36706f4c Packaging: Fix libraryPathDependencies for legacy-support packs
Adds both *-all and *-queries to all.
2021-08-11 13:24:21 -07:00
Andrew Eisenberg
056f9b9471 Packaging: Update references to moved files 2021-08-11 13:23:50 -07:00
Andrew Eisenberg
8cc2201335 Packaging: Update identical files check 2021-08-11 13:23:50 -07:00
Andrew Eisenberg
3f5f73d730 Packaging: Refactor Python into separate library and query packs 2021-08-11 13:23:50 -07:00
Dave Bartolomeo
411a451798 Packaging: Move one more file into library pack 2021-07-28 11:30:11 -04:00
Dave Bartolomeo
b6f0a5dea9 Packaging: Refactor JavaScript into separate query and library packs 2021-07-28 09:46:24 -04:00
Dave Bartolomeo
7079cecdb0 Packaging: Refactor Java packs into separate query and library packs 2021-07-28 00:32:30 -04:00
Dave Bartolomeo
0a24667567 Packaging: Bump pack versions 2021-07-28 00:20:50 -04:00
Dave Bartolomeo
b12771069a Packaging: Mark C# library as a library 2021-07-27 16:22:21 -04:00
Dave Bartolomeo
38f892b120 Packaging: Refactor C# into separate library and query packs 2021-07-27 15:51:33 -04:00
Dave Bartolomeo
3e7bb7c296 Packaging C++: Refactor codeql-cpp into separate library and query packs
This commit does the following:

- creates a new cpp/ql/lib folder and moves the majority of `.qll` files
  from C++ into it.
- updates references to these files from the relevant `.ql` files.
- updates and adds `qlpack.yml` files to create publishable qlpacks
  for cpp.
- updates the ``misc/suite-helpers/qlpack.yml` file to make it
  publishable
- adds qlpack.lock.yml files for each published pack.
2021-07-27 15:03:53 -04:00
2830 changed files with 30548 additions and 174843 deletions

View File

@@ -1,5 +1,5 @@
{ "provide": [ "*/ql/src/qlpack.yml",
"*/ql/lib/qlpack.yml",
{ "provide": [ "*/ql/lib/qlpack.yml",
"*/ql/src/qlpack.yml",
"*/ql/test/qlpack.yml",
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
"*/ql/examples/qlpack.yml",

3
.gitattributes vendored
View File

@@ -48,6 +48,3 @@
*.gif -text
*.dll -text
*.pdb -text
java/ql/test/stubs/**/*.java linguist-generated=true
java/ql/test/experimental/stubs/**/*.java linguist-generated=true

View File

@@ -11,8 +11,6 @@ on:
- 'rc/*'
paths:
- 'csharp/**'
- '.github/codeql/**'
- '.github/workflows/codeql-analysis.yml'
schedule:
- cron: '0 9 * * 1'
@@ -40,8 +38,8 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
#- name: Autobuild
# uses: github/codeql-action/autobuild@main
- name: Autobuild
uses: github/codeql-action/autobuild@main
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -50,8 +48,9 @@ jobs:
# and modify them (or add more) to build your code if your project
# uses a compiled language
- run: |
dotnet build csharp
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@main

View File

@@ -8,7 +8,7 @@ on:
jobs:
update:
name: Update framework coverage report
if: github.repository == 'github/codeql'
if: github.event.repository.fork == false
runs-on: ubuntu-latest
steps:

5
.gitignore vendored
View File

@@ -5,6 +5,7 @@
# query compilation caches
.cache
data
# qltest projects and artifacts
*/ql/test/**/*.testproj
@@ -23,4 +24,8 @@
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
/codeql/
# Exclude output directories for compiled packs.
.codeql/
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json

View File

@@ -17,9 +17,3 @@
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
# CodeQL tools and associated docs
/docs/codeql-cli/ @github/codeql-cli-reviewers
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers

View File

@@ -6,7 +6,6 @@
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
@@ -430,10 +429,10 @@
"python/ql/src/Lexical/CommentedOutCodeReferences.inc.qhelp"
],
"IDE Contextual Queries": [
"cpp/ql/src/IDEContextual.qll",
"csharp/ql/src/IDEContextual.qll",
"java/ql/src/IDEContextual.qll",
"javascript/ql/src/IDEContextual.qll",
"cpp/ql/lib/IDEContextual.qll",
"csharp/ql/lib/IDEContextual.qll",
"java/ql/lib/IDEContextual.qll",
"javascript/ql/lib/IDEContextual.qll",
"python/ql/src/analysis/IDEContextual.qll"
],
"SSA C#": [
@@ -462,4 +461,4 @@
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll"
]
}
}

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* The 'Uncontrolled data in SQL query' (cpp/sql-injection) query now supports the `libpqxx` library.

View File

@@ -1,3 +0,0 @@
lgtm,codescanning
* The "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file) query now uses dataflow to produce additional results.
* Heuristics in the SensitiveExprs.qll library have been improved, making the "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file), "Cleartext storage of sensitive information in buffer" (cpp/cleartext-storage-buffer) and "Cleartext storage of sensitive information in an SQLite" (cpp/cleartext-storage-database) queries more accurate.

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Improvements have been made to the `cpp/toctou-race-condition` query, both to find more correct results and fewer false positive results.

View File

@@ -1,2 +0,0 @@
lgtm
* Improvements made to the (`cpp/uncontrolled-arithmetic`) query, reducing the frequency of false positive results.

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Virtual function specifiers are now accessible via the new predicates on `Function` (`.isDeclaredVirtual`, `.isOverride`, and `.isFinal`).

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Added `Function.hasTrailingReturnType` predicate to check whether a function was declared with a trailing return type.

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Added `RoutineType.hasCLinkage` predicate to check whether a function type has "C" language linkage.

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Lowered the precision of `cpp/potentially-dangerous-function` so it is run but not displayed on LGTM by default and so it's only run and displayed on Code Scanning if a broader suite like `cpp-security-extended` is opted into.

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Added `Element.getPrimaryQlClasses()` predicate, which gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* The query `cpp/implicit-bitfield-downcast` now accounts for C++ reference types, which leads to more true positive results.

View File

@@ -1,4 +0,0 @@
lgtm,codescanning
* The `SimpleRangeAnalysis` library includes information from the
immediate guard for determining the upper bound of a stack
variable for improved accuracy.

View File

@@ -1,4 +0,0 @@
lgtm,codescanning
* The `memberMayBeVarSize` predicate considers more fields to be variable size.
As a result, the "Static buffer overflow" query (cpp/static-buffer-overflow)
produces fewer false positives.

View File

@@ -1,4 +0,0 @@
---
dependencies: {}
compiled: false
lockVersion: 1.0.0

View File

@@ -1,4 +1,4 @@
name: codeql/cpp-examples
version: 0.0.2
version: 0.0.0
dependencies:
codeql/cpp-all: "*"
codeql/cpp-all: ^0.0.1

View File

@@ -3,5 +3,3 @@ version: 0.0.2
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
library: true
dependencies:
codeql/cpp-upgrades: 0.0.2

View File

@@ -58,11 +58,6 @@ class ElementBase extends @element {
/** DEPRECATED: use `getAPrimaryQlClass` instead. */
deprecated string getCanonicalQLClass() { result = this.getAPrimaryQlClass() }
/**
* Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.
*/
final string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") }
/**
* Gets the name of a primary CodeQL class to which this element belongs.
*

View File

@@ -171,7 +171,7 @@ class Container extends Locatable, @container {
* To get the full path, use `getAbsolutePath`.
*/
class Folder extends Container, @folder {
override string getAbsolutePath() { folders(underlyingElement(this), result) }
override string getAbsolutePath() { folders(underlyingElement(this), result, _) }
override Location getLocation() {
result.getContainer() = this and
@@ -190,7 +190,7 @@ class Folder extends Container, @folder {
* DEPRECATED: use `getAbsolutePath` instead.
* Gets the name of this folder.
*/
deprecated string getName() { folders(underlyingElement(this), result) }
deprecated string getName() { folders(underlyingElement(this), result, _) }
/**
* DEPRECATED: use `getAbsolutePath` instead.
@@ -208,7 +208,17 @@ class Folder extends Container, @folder {
* DEPRECATED: use `getBaseName` instead.
* Gets the last part of the folder name.
*/
deprecated string getShortName() { result = this.getBaseName() }
deprecated string getShortName() {
exists(string longnameRaw, string longname |
folders(underlyingElement(this), _, longnameRaw) and
longname = longnameRaw.replaceAll("\\", "/")
|
exists(int index |
result = longname.splitAt("/", index) and
not exists(longname.splitAt("/", index + 1))
)
)
}
/**
* DEPRECATED: use `getParentContainer` instead.
@@ -232,7 +242,7 @@ class Folder extends Container, @folder {
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
*/
class File extends Container, @file {
override string getAbsolutePath() { files(underlyingElement(this), result) }
override string getAbsolutePath() { files(underlyingElement(this), result, _, _, _) }
override string toString() { result = Container.super.toString() }
@@ -326,13 +336,7 @@ class File extends Container, @file {
* for example, for "file.tar.gz", this predicate will have the result
* "tar.gz", while `getExtension` will have the result "gz".
*/
string getExtensions() {
exists(string name, int firstDotPos |
name = this.getBaseName() and
firstDotPos = min([name.indexOf("."), name.length() - 1]) and
result = name.suffix(firstDotPos + 1)
)
}
string getExtensions() { files(underlyingElement(this), _, _, result, _) }
/**
* Gets the short name of this file, that is, the prefix of its base name up
@@ -347,16 +351,7 @@ class File extends Container, @file {
* for example, for "file.tar.gz", this predicate will have the result
* "file", while `getStem` will have the result "file.tar".
*/
string getShortName() {
exists(string name, int firstDotPos |
name = this.getBaseName() and
firstDotPos = min([name.indexOf("."), name.length()]) and
result = name.prefix(firstDotPos)
)
or
this.getAbsolutePath() = "" and
result = ""
}
string getShortName() { files(underlyingElement(this), _, result, _, _) }
}
/**

View File

@@ -82,23 +82,9 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
/** Holds if this function is inline. */
predicate isInline() { this.hasSpecifier("inline") }
/**
* Holds if this function is virtual.
*
* Unlike `isDeclaredVirtual()`, `isVirtual()` holds even if the function
* is not explicitly declared with the `virtual` specifier.
*/
/** Holds if this function is virtual. */
predicate isVirtual() { this.hasSpecifier("virtual") }
/** Holds if this function is declared with the `virtual` specifier. */
predicate isDeclaredVirtual() { this.hasSpecifier("declared_virtual") }
/** Holds if this function is declared with the `override` specifier. */
predicate isOverride() { this.hasSpecifier("override") }
/** Holds if this function is declared with the `final` specifier. */
predicate isFinal() { this.hasSpecifier("final") }
/**
* Holds if this function is deleted.
* This may be because it was explicitly deleted with an `= delete`
@@ -151,20 +137,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
*/
predicate isNaked() { getAnAttribute().hasName("naked") }
/**
* Holds if this function has a trailing return type.
*
* Note that this is true whether or not deduction took place. For example,
* this holds for both `e` and `f`, but not `g` or `h`:
* ```
* auto e() -> int { return 0; }
* auto f() -> auto { return 0; }
* auto g() { return 0; }
* int h() { return 0; }
* ```
*/
predicate hasTrailingReturnType() { this.hasSpecifier("has_trailing_return_type") }
/** Gets the return type of this function. */
Type getType() { function_return_type(underlyingElement(this), unresolveElement(result)) }

View File

@@ -18,12 +18,6 @@ import semmle.code.cpp.controlflow.ControlFlowGraph
* ...
* }
* ```
* But _not_ `4` in the following code:
* ```
* int myUninitializedVariable;
* myUninitializedVariable = 4;
* ```
* Instead, this is an `Assignment`.
*/
class Initializer extends ControlFlowNode, @initialiser {
override Location getLocation() { initialisers(underlyingElement(this), _, _, result) }

View File

@@ -46,7 +46,7 @@ private string escapeString(string s) {
* string representation comes first in lexicographical order.
*/
private Location getRepresentativeLocation(Locatable ast) {
result = min(Location loc | loc = ast.getLocation() | loc order by loc.toString())
result = rank[1](Location loc | loc = ast.getLocation() | loc order by loc.toString())
}
/**

View File

@@ -1621,19 +1621,6 @@ class RoutineType extends Type, @routinetype {
*/
Type getReturnType() { routinetypes(underlyingElement(this), unresolveElement(result)) }
/**
* Holds if this function type has "C" language linkage.
*
* This includes any function declared in a C source file, or explicitly marked as having "C" linkage:
* ```
* extern "C" void f();
* extern "C" {
* void g();
* }
* ```
*/
predicate hasCLinkage() { this.hasSpecifier("c_linkage") }
override string explain() {
result =
"function returning {" + this.getReturnType().explain() + "} with arguments (" +

View File

@@ -136,12 +136,6 @@ class Variable extends Declaration, @variable {
/**
* Gets an assignment expression that assigns to this variable.
* For example: `x=...` or `x+=...`.
*
* This does _not_ include the initialization of the variable. Use
* `Variable.getInitializer()` to get the variable's initializer,
* or use `Variable.getAnAssignedValue()` to get an expression that
* is the right-hand side of an assignment or an initialization of
* the varible.
*/
Assignment getAnAssignment() { result.getLValue() = this.getAnAccess() }

0
cpp/ql/lib/semmle/code/cpp/XML.qll Executable file → Normal file
View File

View File

@@ -2,18 +2,17 @@ import cpp
import semmle.code.cpp.dataflow.DataFlow
/**
* Holds if `v` is a member variable of `c` that looks like it might be variable sized
* in practice. For example:
* Holds if `v` is a member variable of `c` that looks like it might be variable sized in practice. For
* example:
* ```
* struct myStruct { // c
* int amount;
* char data[1]; // v
* };
* ```
* This requires that `v` is an array of size 0 or 1, and `v` is the last member of `c`.
* In addition, if the size of the structure is taken, there must be at least one instance
* where a `c` pointer is allocated with additional space.
* For example, holds for `c` if it occurs as
* This requires that `v` is an array of size 0 or 1, and `v` is the last member of `c`. In addition,
* there must be at least one instance where a `c` pointer is allocated with additional space. For
* example, holds for `c` if it occurs as
* ```
* malloc(sizeof(c) + 100 * sizeof(char))
* ```
@@ -28,25 +27,27 @@ predicate memberMayBeVarSize(Class c, MemberVariable v) {
i = max(int j | c.getCanonicalMember(j) instanceof Field | j) and
v = c.getCanonicalMember(i) and
// v is an array of size at most 1
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1 and
not c instanceof Union
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
) and
// If the size is taken, then arithmetic is performed on the result at least once
(
// `sizeof(c)` is not taken
not exists(SizeofOperator so |
so.(SizeofTypeOperator).getTypeOperand().getUnspecifiedType() = c or
so.(SizeofExprOperator).getExprOperand().getUnspecifiedType() = c
)
or
// or `sizeof(c)` is taken
exists(SizeofOperator so |
// `sizeof(c)` is taken
so.(SizeofTypeOperator).getTypeOperand().getUnspecifiedType() = c or
so.(SizeofExprOperator).getExprOperand().getUnspecifiedType() = c
|
// and arithmetic is performed on the result
// arithmetic is performed on the result
so.getParent*() instanceof AddExpr
)
or
exists(AddressOfExpr aoe |
// `&(c.v)` is taken
aoe.getAddressable() = v
)
or
exists(BuiltInOperationBuiltInOffsetOf oo |
// `offsetof(c, v)` using a builtin
oo.getAChild().(VariableAccess).getTarget() = v
)
)
}
@@ -60,10 +61,6 @@ int getBufferSize(Expr bufferExpr, Element why) {
result = bufferVar.getUnspecifiedType().(ArrayType).getSize() and
why = bufferVar and
not memberMayBeVarSize(_, bufferVar) and
not exists(Union bufferType |
bufferType.getAMemberVariable() = why and
bufferVar.getUnspecifiedType().(ArrayType).getSize() <= 1
) and
not result = 0 // zero sized arrays are likely to have special usage, for example
or
// behaving a bit like a 'union' overlapping other fields.
@@ -85,13 +82,6 @@ int getBufferSize(Expr bufferExpr, Element why) {
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
result = getBufferSize(parentPtr, _) + bufferVar.getType().getSize() - parentClass.getSize()
)
or
exists(Union bufferType |
bufferType.getAMemberVariable() = why and
why = bufferVar and
bufferVar.getUnspecifiedType().(ArrayType).getSize() <= 1 and
result = bufferType.getSize()
)
)
or
// buffer is a fixed size dynamic allocation

View File

@@ -923,29 +923,28 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
class Cc = CallContext;
class Cc = boolean;
class CcCall = CallContextCall;
class CcCall extends Cc {
CcCall() { this = true }
class CcNoCall = CallContextNoCall;
/** Holds if this call context may be `call`. */
predicate matchesCall(DataFlowCall call) { any() }
}
Cc ccNone() { result instanceof CallContextAny }
class CcNoCall extends Cc {
CcNoCall() { this = false }
}
Cc ccNone() { result = false }
private class LocalCc = Unit;
bindingset[call, c, outercc]
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
checkCallContextCall(outercc, call, c) and
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
else result = TSomeCall()
}
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1173,8 +1172,7 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -1862,8 +1860,7 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -2120,7 +2117,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2621,8 +2618,7 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -3262,16 +3258,24 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
this.(PathNodeImpl).isHidden() and
this.isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
not this.(PathNodeImpl).isHidden() and
not result.(PathNodeImpl).isHidden()
not this.isHidden() and
not result.isHidden()
}
/** Holds if this node is a source. */
@@ -3283,14 +3287,6 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
predicate isHidden() {
hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.getNodeEx() instanceof TNodeImplicitRead
}
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3317,15 +3313,10 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNode n) {
n instanceof PathNodeSink or directReach(n.getASuccessor())
}
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
/** Holds if `n` can reach a sink or is used in a subpath. */
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3334,14 +3325,12 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3633,87 +3622,6 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
private module Subpaths {
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths01(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
unbindConf(arg.getConfiguration()))
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths02(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
subpaths01(arg, par, sc, innercc, kind, out, apout) and
out.asNode() = kind.getAnOutNode(_)
}
pragma[nomagic]
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/
pragma[nomagic]
private predicate subpaths03(
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
) {
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, apout) and
ret.getNodeEx() = retnode and
kind = retnode.getKind() and
innercc = ret.getCallContext() and
sc = ret.getSummaryCtx() and
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
apout = ret.getAp() and
not ret.isHidden()
)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
pragma[only_bind_into](arg).getASuccessor() = par and
pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
out.getAp() = apout
)
}
/**
* Holds if `n` can reach a return node in a summarized subpath.
*/
predicate retReach(PathNode n) {
subpaths(_, _, n, _)
or
exists(PathNode mid |
retReach(mid) and
n.getASuccessor() = mid and
not subpaths(_, mid, _, _)
)
}
}
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*

View File

@@ -923,29 +923,28 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
class Cc = CallContext;
class Cc = boolean;
class CcCall = CallContextCall;
class CcCall extends Cc {
CcCall() { this = true }
class CcNoCall = CallContextNoCall;
/** Holds if this call context may be `call`. */
predicate matchesCall(DataFlowCall call) { any() }
}
Cc ccNone() { result instanceof CallContextAny }
class CcNoCall extends Cc {
CcNoCall() { this = false }
}
Cc ccNone() { result = false }
private class LocalCc = Unit;
bindingset[call, c, outercc]
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
checkCallContextCall(outercc, call, c) and
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
else result = TSomeCall()
}
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1173,8 +1172,7 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -1862,8 +1860,7 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -2120,7 +2117,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2621,8 +2618,7 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -3262,16 +3258,24 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
this.(PathNodeImpl).isHidden() and
this.isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
not this.(PathNodeImpl).isHidden() and
not result.(PathNodeImpl).isHidden()
not this.isHidden() and
not result.isHidden()
}
/** Holds if this node is a source. */
@@ -3283,14 +3287,6 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
predicate isHidden() {
hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.getNodeEx() instanceof TNodeImplicitRead
}
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3317,15 +3313,10 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNode n) {
n instanceof PathNodeSink or directReach(n.getASuccessor())
}
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
/** Holds if `n` can reach a sink or is used in a subpath. */
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3334,14 +3325,12 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3633,87 +3622,6 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
private module Subpaths {
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths01(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
unbindConf(arg.getConfiguration()))
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths02(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
subpaths01(arg, par, sc, innercc, kind, out, apout) and
out.asNode() = kind.getAnOutNode(_)
}
pragma[nomagic]
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/
pragma[nomagic]
private predicate subpaths03(
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
) {
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, apout) and
ret.getNodeEx() = retnode and
kind = retnode.getKind() and
innercc = ret.getCallContext() and
sc = ret.getSummaryCtx() and
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
apout = ret.getAp() and
not ret.isHidden()
)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
pragma[only_bind_into](arg).getASuccessor() = par and
pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
out.getAp() = apout
)
}
/**
* Holds if `n` can reach a return node in a summarized subpath.
*/
predicate retReach(PathNode n) {
subpaths(_, _, n, _)
or
exists(PathNode mid |
retReach(mid) and
n.getASuccessor() = mid and
not subpaths(_, mid, _, _)
)
}
}
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*

View File

@@ -923,29 +923,28 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
class Cc = CallContext;
class Cc = boolean;
class CcCall = CallContextCall;
class CcCall extends Cc {
CcCall() { this = true }
class CcNoCall = CallContextNoCall;
/** Holds if this call context may be `call`. */
predicate matchesCall(DataFlowCall call) { any() }
}
Cc ccNone() { result instanceof CallContextAny }
class CcNoCall extends Cc {
CcNoCall() { this = false }
}
Cc ccNone() { result = false }
private class LocalCc = Unit;
bindingset[call, c, outercc]
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
checkCallContextCall(outercc, call, c) and
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
else result = TSomeCall()
}
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1173,8 +1172,7 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -1862,8 +1860,7 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -2120,7 +2117,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2621,8 +2618,7 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -3262,16 +3258,24 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
this.(PathNodeImpl).isHidden() and
this.isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
not this.(PathNodeImpl).isHidden() and
not result.(PathNodeImpl).isHidden()
not this.isHidden() and
not result.isHidden()
}
/** Holds if this node is a source. */
@@ -3283,14 +3287,6 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
predicate isHidden() {
hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.getNodeEx() instanceof TNodeImplicitRead
}
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3317,15 +3313,10 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNode n) {
n instanceof PathNodeSink or directReach(n.getASuccessor())
}
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
/** Holds if `n` can reach a sink or is used in a subpath. */
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3334,14 +3325,12 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3633,87 +3622,6 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
private module Subpaths {
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths01(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
unbindConf(arg.getConfiguration()))
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths02(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
subpaths01(arg, par, sc, innercc, kind, out, apout) and
out.asNode() = kind.getAnOutNode(_)
}
pragma[nomagic]
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/
pragma[nomagic]
private predicate subpaths03(
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
) {
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, apout) and
ret.getNodeEx() = retnode and
kind = retnode.getKind() and
innercc = ret.getCallContext() and
sc = ret.getSummaryCtx() and
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
apout = ret.getAp() and
not ret.isHidden()
)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
pragma[only_bind_into](arg).getASuccessor() = par and
pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
out.getAp() = apout
)
}
/**
* Holds if `n` can reach a return node in a summarized subpath.
*/
predicate retReach(PathNode n) {
subpaths(_, _, n, _)
or
exists(PathNode mid |
retReach(mid) and
n.getASuccessor() = mid and
not subpaths(_, mid, _, _)
)
}
}
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*

View File

@@ -923,29 +923,28 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
class Cc = CallContext;
class Cc = boolean;
class CcCall = CallContextCall;
class CcCall extends Cc {
CcCall() { this = true }
class CcNoCall = CallContextNoCall;
/** Holds if this call context may be `call`. */
predicate matchesCall(DataFlowCall call) { any() }
}
Cc ccNone() { result instanceof CallContextAny }
class CcNoCall extends Cc {
CcNoCall() { this = false }
}
Cc ccNone() { result = false }
private class LocalCc = Unit;
bindingset[call, c, outercc]
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
checkCallContextCall(outercc, call, c) and
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
else result = TSomeCall()
}
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1173,8 +1172,7 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -1862,8 +1860,7 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -2120,7 +2117,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2621,8 +2618,7 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -3262,16 +3258,24 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
this.(PathNodeImpl).isHidden() and
this.isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
not this.(PathNodeImpl).isHidden() and
not result.(PathNodeImpl).isHidden()
not this.isHidden() and
not result.isHidden()
}
/** Holds if this node is a source. */
@@ -3283,14 +3287,6 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
predicate isHidden() {
hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.getNodeEx() instanceof TNodeImplicitRead
}
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3317,15 +3313,10 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNode n) {
n instanceof PathNodeSink or directReach(n.getASuccessor())
}
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
/** Holds if `n` can reach a sink or is used in a subpath. */
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3334,14 +3325,12 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3633,87 +3622,6 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
private module Subpaths {
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths01(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
unbindConf(arg.getConfiguration()))
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths02(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
subpaths01(arg, par, sc, innercc, kind, out, apout) and
out.asNode() = kind.getAnOutNode(_)
}
pragma[nomagic]
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/
pragma[nomagic]
private predicate subpaths03(
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
) {
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, apout) and
ret.getNodeEx() = retnode and
kind = retnode.getKind() and
innercc = ret.getCallContext() and
sc = ret.getSummaryCtx() and
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
apout = ret.getAp() and
not ret.isHidden()
)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
pragma[only_bind_into](arg).getASuccessor() = par and
pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
out.getAp() = apout
)
}
/**
* Holds if `n` can reach a return node in a summarized subpath.
*/
predicate retReach(PathNode n) {
subpaths(_, _, n, _)
or
exists(PathNode mid |
retReach(mid) and
n.getASuccessor() = mid and
not subpaths(_, mid, _, _)
)
}
}
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*

View File

@@ -786,18 +786,13 @@ private module Cached {
}
/**
* Holds if the call context `call` improves virtual dispatch in `callable`.
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
*/
cached
predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
reducedViableImplInCallContext(_, callable, call)
}
/**
* Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
*/
cached
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
or
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
}
@@ -851,15 +846,6 @@ private module Cached {
TAccessPathFrontSome(AccessPathFront apf)
}
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
*/
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
recordDataFlowCallSiteDispatch(call, callable) or
recordDataFlowCallSiteUnreachable(call, callable)
}
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/

View File

@@ -923,29 +923,28 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
class Cc = CallContext;
class Cc = boolean;
class CcCall = CallContextCall;
class CcCall extends Cc {
CcCall() { this = true }
class CcNoCall = CallContextNoCall;
/** Holds if this call context may be `call`. */
predicate matchesCall(DataFlowCall call) { any() }
}
Cc ccNone() { result instanceof CallContextAny }
class CcNoCall extends Cc {
CcNoCall() { this = false }
}
Cc ccNone() { result = false }
private class LocalCc = Unit;
bindingset[call, c, outercc]
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
checkCallContextCall(outercc, call, c) and
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
else result = TSomeCall()
}
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1173,8 +1172,7 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -1862,8 +1860,7 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -2120,7 +2117,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2621,8 +2618,7 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -3262,16 +3258,24 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
this.(PathNodeImpl).isHidden() and
this.isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
not this.(PathNodeImpl).isHidden() and
not result.(PathNodeImpl).isHidden()
not this.isHidden() and
not result.isHidden()
}
/** Holds if this node is a source. */
@@ -3283,14 +3287,6 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
predicate isHidden() {
hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.getNodeEx() instanceof TNodeImplicitRead
}
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3317,15 +3313,10 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNode n) {
n instanceof PathNodeSink or directReach(n.getASuccessor())
}
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
/** Holds if `n` can reach a sink or is used in a subpath. */
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3334,14 +3325,12 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3633,87 +3622,6 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
private module Subpaths {
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths01(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
unbindConf(arg.getConfiguration()))
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths02(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
subpaths01(arg, par, sc, innercc, kind, out, apout) and
out.asNode() = kind.getAnOutNode(_)
}
pragma[nomagic]
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/
pragma[nomagic]
private predicate subpaths03(
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
) {
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, apout) and
ret.getNodeEx() = retnode and
kind = retnode.getKind() and
innercc = ret.getCallContext() and
sc = ret.getSummaryCtx() and
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
apout = ret.getAp() and
not ret.isHidden()
)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
pragma[only_bind_into](arg).getASuccessor() = par and
pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
out.getAp() = apout
)
}
/**
* Holds if `n` can reach a return node in a summarized subpath.
*/
predicate retReach(PathNode n) {
subpaths(_, _, n, _)
or
exists(PathNode mid |
retReach(mid) and
n.getASuccessor() = mid and
not subpaths(_, mid, _, _)
)
}
}
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*

View File

@@ -735,12 +735,7 @@ private module FieldFlow {
private class FieldConfiguration extends Configuration {
FieldConfiguration() { this = "FieldConfiguration" }
override predicate isSource(Node source) {
storeStep(source, _, _)
or
// Also mark `foo(a.b);` as a source when `a.b` may be overwritten by `foo`.
readStep(_, _, any(Node node | node.asExpr() = source.asDefiningArgument()))
}
override predicate isSource(Node source) { storeStep(source, _, _) }
override predicate isSink(Node sink) { readStep(_, _, sink) }

View File

@@ -9,7 +9,7 @@ import semmle.code.cpp.Enum
private import semmle.code.cpp.dataflow.EscapesTree
/**
* A C/C++ access expression. This refers to a function (excluding function references in function call expressions), variable, or enum constant.
* A C/C++ access expression. This refers to a function, variable, or enum constant.
*/
class Access extends Expr, NameQualifiableElement, @access {
// As `@access` is a union type containing `@routineexpr` (which describes function accesses
@@ -350,15 +350,6 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess {
* int (*myFunctionPointer)(int) = &myFunctionTarget;
* }
* ```
* This excludes function accesses in function call expressions.
* For example the access `myFunctionTarget` in `myFunction` in the following code:
* ```
* int myFunctionTarget(int);
*
* void myFunction() {
* myFunctionTarget(1);
* }
* ```
*/
class FunctionAccess extends Access, @routineexpr {
FunctionAccess() { not iscall(underlyingElement(this), _) }

View File

@@ -5,8 +5,7 @@ import semmle.code.cpp.exprs.BitwiseOperation
/**
* A non-overloaded binary assignment operation, including `=`, `+=`, `&=`,
* etc. A C++ overloaded assignment operation looks syntactically identical but is instead
* a `FunctionCall`. This class does _not_ include variable initializers. To get a variable
* initializer, use `Initializer` instead.
* a `FunctionCall`.
*
* This is a QL base class for all (non-overloaded) assignments.
*/
@@ -35,8 +34,6 @@ class Assignment extends Operation, @assign_expr {
* ```
* a = b;
* ```
* Note that `int a = b;` is _not_ an `AssignExpr`. It is a `Variable`,
* and `b` can be obtained using `Variable.getInitializer()`.
*/
class AssignExpr extends Assignment, @assignexpr {
override string getOperator() { result = "=" }
@@ -49,9 +46,6 @@ class AssignExpr extends Assignment, @assignexpr {
/**
* A non-overloaded binary assignment operation other than `=`.
*
* This class does _not_ include variable initializers. To get a variable
* initializer, use `Initializer` instead.
*/
class AssignOperation extends Assignment, @assign_op_expr {
override string toString() { result = "... " + this.getOperator() + " ..." }

View File

@@ -550,39 +550,6 @@ module TaintedWithPath {
)
}
/**
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
* from `par` to `ret` within it, in the graph of data flow path explanations.
*/
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), sinkNode.inner()) and
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
key = "semmle.label" and val = n.toString()

View File

@@ -923,29 +923,28 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
class Cc = CallContext;
class Cc = boolean;
class CcCall = CallContextCall;
class CcCall extends Cc {
CcCall() { this = true }
class CcNoCall = CallContextNoCall;
/** Holds if this call context may be `call`. */
predicate matchesCall(DataFlowCall call) { any() }
}
Cc ccNone() { result instanceof CallContextAny }
class CcNoCall extends Cc {
CcNoCall() { this = false }
}
Cc ccNone() { result = false }
private class LocalCc = Unit;
bindingset[call, c, outercc]
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
checkCallContextCall(outercc, call, c) and
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
else result = TSomeCall()
}
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1173,8 +1172,7 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -1862,8 +1860,7 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -2120,7 +2117,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2621,8 +2618,7 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -3262,16 +3258,24 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
this.(PathNodeImpl).isHidden() and
this.isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
not this.(PathNodeImpl).isHidden() and
not result.(PathNodeImpl).isHidden()
not this.isHidden() and
not result.isHidden()
}
/** Holds if this node is a source. */
@@ -3283,14 +3287,6 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
predicate isHidden() {
hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.getNodeEx() instanceof TNodeImplicitRead
}
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3317,15 +3313,10 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNode n) {
n instanceof PathNodeSink or directReach(n.getASuccessor())
}
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
/** Holds if `n` can reach a sink or is used in a subpath. */
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3334,14 +3325,12 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3633,87 +3622,6 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
private module Subpaths {
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths01(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
unbindConf(arg.getConfiguration()))
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths02(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
subpaths01(arg, par, sc, innercc, kind, out, apout) and
out.asNode() = kind.getAnOutNode(_)
}
pragma[nomagic]
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/
pragma[nomagic]
private predicate subpaths03(
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
) {
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, apout) and
ret.getNodeEx() = retnode and
kind = retnode.getKind() and
innercc = ret.getCallContext() and
sc = ret.getSummaryCtx() and
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
apout = ret.getAp() and
not ret.isHidden()
)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
pragma[only_bind_into](arg).getASuccessor() = par and
pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
out.getAp() = apout
)
}
/**
* Holds if `n` can reach a return node in a summarized subpath.
*/
predicate retReach(PathNode n) {
subpaths(_, _, n, _)
or
exists(PathNode mid |
retReach(mid) and
n.getASuccessor() = mid and
not subpaths(_, mid, _, _)
)
}
}
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*

View File

@@ -923,29 +923,28 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
class Cc = CallContext;
class Cc = boolean;
class CcCall = CallContextCall;
class CcCall extends Cc {
CcCall() { this = true }
class CcNoCall = CallContextNoCall;
/** Holds if this call context may be `call`. */
predicate matchesCall(DataFlowCall call) { any() }
}
Cc ccNone() { result instanceof CallContextAny }
class CcNoCall extends Cc {
CcNoCall() { this = false }
}
Cc ccNone() { result = false }
private class LocalCc = Unit;
bindingset[call, c, outercc]
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
checkCallContextCall(outercc, call, c) and
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
else result = TSomeCall()
}
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1173,8 +1172,7 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -1862,8 +1860,7 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -2120,7 +2117,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2621,8 +2618,7 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -3262,16 +3258,24 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
this.(PathNodeImpl).isHidden() and
this.isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
not this.(PathNodeImpl).isHidden() and
not result.(PathNodeImpl).isHidden()
not this.isHidden() and
not result.isHidden()
}
/** Holds if this node is a source. */
@@ -3283,14 +3287,6 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
predicate isHidden() {
hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.getNodeEx() instanceof TNodeImplicitRead
}
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3317,15 +3313,10 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNode n) {
n instanceof PathNodeSink or directReach(n.getASuccessor())
}
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
/** Holds if `n` can reach a sink or is used in a subpath. */
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3334,14 +3325,12 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3633,87 +3622,6 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
private module Subpaths {
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths01(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
unbindConf(arg.getConfiguration()))
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths02(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
subpaths01(arg, par, sc, innercc, kind, out, apout) and
out.asNode() = kind.getAnOutNode(_)
}
pragma[nomagic]
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/
pragma[nomagic]
private predicate subpaths03(
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
) {
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, apout) and
ret.getNodeEx() = retnode and
kind = retnode.getKind() and
innercc = ret.getCallContext() and
sc = ret.getSummaryCtx() and
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
apout = ret.getAp() and
not ret.isHidden()
)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
pragma[only_bind_into](arg).getASuccessor() = par and
pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
out.getAp() = apout
)
}
/**
* Holds if `n` can reach a return node in a summarized subpath.
*/
predicate retReach(PathNode n) {
subpaths(_, _, n, _)
or
exists(PathNode mid |
retReach(mid) and
n.getASuccessor() = mid and
not subpaths(_, mid, _, _)
)
}
}
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*

View File

@@ -923,29 +923,28 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
class Cc = CallContext;
class Cc = boolean;
class CcCall = CallContextCall;
class CcCall extends Cc {
CcCall() { this = true }
class CcNoCall = CallContextNoCall;
/** Holds if this call context may be `call`. */
predicate matchesCall(DataFlowCall call) { any() }
}
Cc ccNone() { result instanceof CallContextAny }
class CcNoCall extends Cc {
CcNoCall() { this = false }
}
Cc ccNone() { result = false }
private class LocalCc = Unit;
bindingset[call, c, outercc]
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
checkCallContextCall(outercc, call, c) and
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
else result = TSomeCall()
}
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1173,8 +1172,7 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -1862,8 +1860,7 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -2120,7 +2117,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2621,8 +2618,7 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -3262,16 +3258,24 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
this.(PathNodeImpl).isHidden() and
this.isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
not this.(PathNodeImpl).isHidden() and
not result.(PathNodeImpl).isHidden()
not this.isHidden() and
not result.isHidden()
}
/** Holds if this node is a source. */
@@ -3283,14 +3287,6 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
predicate isHidden() {
hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.getNodeEx() instanceof TNodeImplicitRead
}
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3317,15 +3313,10 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNode n) {
n instanceof PathNodeSink or directReach(n.getASuccessor())
}
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
/** Holds if `n` can reach a sink or is used in a subpath. */
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3334,14 +3325,12 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3633,87 +3622,6 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
private module Subpaths {
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths01(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
unbindConf(arg.getConfiguration()))
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths02(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
subpaths01(arg, par, sc, innercc, kind, out, apout) and
out.asNode() = kind.getAnOutNode(_)
}
pragma[nomagic]
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/
pragma[nomagic]
private predicate subpaths03(
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
) {
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, apout) and
ret.getNodeEx() = retnode and
kind = retnode.getKind() and
innercc = ret.getCallContext() and
sc = ret.getSummaryCtx() and
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
apout = ret.getAp() and
not ret.isHidden()
)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
pragma[only_bind_into](arg).getASuccessor() = par and
pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
out.getAp() = apout
)
}
/**
* Holds if `n` can reach a return node in a summarized subpath.
*/
predicate retReach(PathNode n) {
subpaths(_, _, n, _)
or
exists(PathNode mid |
retReach(mid) and
n.getASuccessor() = mid and
not subpaths(_, mid, _, _)
)
}
}
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*

View File

@@ -923,29 +923,28 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
class Cc = CallContext;
class Cc = boolean;
class CcCall = CallContextCall;
class CcCall extends Cc {
CcCall() { this = true }
class CcNoCall = CallContextNoCall;
/** Holds if this call context may be `call`. */
predicate matchesCall(DataFlowCall call) { any() }
}
Cc ccNone() { result instanceof CallContextAny }
class CcNoCall extends Cc {
CcNoCall() { this = false }
}
Cc ccNone() { result = false }
private class LocalCc = Unit;
bindingset[call, c, outercc]
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
checkCallContextCall(outercc, call, c) and
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
else result = TSomeCall()
}
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1173,8 +1172,7 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -1862,8 +1860,7 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -2120,7 +2117,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2621,8 +2618,7 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
@@ -3262,16 +3258,24 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
this.(PathNodeImpl).isHidden() and
this.isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
not this.(PathNodeImpl).isHidden() and
not result.(PathNodeImpl).isHidden()
not this.isHidden() and
not result.isHidden()
}
/** Holds if this node is a source. */
@@ -3283,14 +3287,6 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
predicate isHidden() {
hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
or
this.getNodeEx() instanceof TNodeImplicitRead
}
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3317,15 +3313,10 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNode n) {
n instanceof PathNodeSink or directReach(n.getASuccessor())
}
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
/** Holds if `n` can reach a sink or is used in a subpath. */
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3334,14 +3325,12 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3633,87 +3622,6 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
private module Subpaths {
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths01(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
pathIntoCallable(arg, par, _, innercc, sc, _) and
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
unbindConf(arg.getConfiguration()))
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
* `kind`, `sc`, `apout`, and `innercc`.
*/
pragma[nomagic]
private predicate subpaths02(
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
NodeEx out, AccessPath apout
) {
subpaths01(arg, par, sc, innercc, kind, out, apout) and
out.asNode() = kind.getAnOutNode(_)
}
pragma[nomagic]
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/
pragma[nomagic]
private predicate subpaths03(
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
) {
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, apout) and
ret.getNodeEx() = retnode and
kind = retnode.getKind() and
innercc = ret.getCallContext() and
sc = ret.getSummaryCtx() and
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
apout = ret.getAp() and
not ret.isHidden()
)
}
/**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`.
*/
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
pragma[only_bind_into](arg).getASuccessor() = par and
pragma[only_bind_into](arg).getASuccessor() = out and
subpaths03(arg, p, ret, o, apout) and
par.getNodeEx() = p and
out.getNodeEx() = o and
out.getAp() = apout
)
}
/**
* Holds if `n` can reach a return node in a summarized subpath.
*/
predicate retReach(PathNode n) {
subpaths(_, _, n, _)
or
exists(PathNode mid |
retReach(mid) and
n.getASuccessor() = mid and
not subpaths(_, mid, _, _)
)
}
}
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*

View File

@@ -786,18 +786,13 @@ private module Cached {
}
/**
* Holds if the call context `call` improves virtual dispatch in `callable`.
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
*/
cached
predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
reducedViableImplInCallContext(_, callable, call)
}
/**
* Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
*/
cached
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
or
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
}
@@ -851,15 +846,6 @@ private module Cached {
TAccessPathFrontSome(AccessPathFront apf)
}
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
*/
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
recordDataFlowCallSiteDispatch(call, callable) or
recordDataFlowCallSiteUnreachable(call, callable)
}
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/

View File

@@ -35,7 +35,7 @@ module InstructionConsistency {
// To avoid an overwhelming number of results when the extractor merges functions with the
// same name, just pick a single location.
result =
min(Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
}
}

View File

@@ -1856,12 +1856,12 @@ class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
}
/**
* Gets the operand that represents the address of the allocation this instruction is initializing.
* Gets the address of the allocation this instruction is initializing.
*/
final AddressOperand getAllocationAddressOperand() { result = getAnOperand() }
/**
* Gets the address for the allocation this instruction is initializing.
* Gets the operand for the allocation this instruction is initializing.
*/
final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() }
}

View File

@@ -35,7 +35,7 @@ module InstructionConsistency {
// To avoid an overwhelming number of results when the extractor merges functions with the
// same name, just pick a single location.
result =
min(Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
}
}

View File

@@ -1856,12 +1856,12 @@ class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
}
/**
* Gets the operand that represents the address of the allocation this instruction is initializing.
* Gets the address of the allocation this instruction is initializing.
*/
final AddressOperand getAllocationAddressOperand() { result = getAnOperand() }
/**
* Gets the address for the allocation this instruction is initializing.
* Gets the operand for the allocation this instruction is initializing.
*/
final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() }
}

View File

@@ -35,7 +35,7 @@ module InstructionConsistency {
// To avoid an overwhelming number of results when the extractor merges functions with the
// same name, just pick a single location.
result =
min(Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
}
}

View File

@@ -1856,12 +1856,12 @@ class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
}
/**
* Gets the operand that represents the address of the allocation this instruction is initializing.
* Gets the address of the allocation this instruction is initializing.
*/
final AddressOperand getAllocationAddressOperand() { result = getAnOperand() }
/**
* Gets the address for the allocation this instruction is initializing.
* Gets the operand for the allocation this instruction is initializing.
*/
final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() }
}

View File

@@ -33,6 +33,3 @@ private import implementations.Recv
private import implementations.Accept
private import implementations.Poll
private import implementations.Select
private import implementations.MySql
private import implementations.SqLite3
private import implementations.PostgreSql

View File

@@ -1,32 +0,0 @@
/**
* Provides implementation classes modeling the MySql C API.
* See `semmle.code.cpp.models.Models` for usage information.
*/
private import semmle.code.cpp.models.interfaces.Sql
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
/**
* The `mysql_query` family of functions from the MySQL C API.
*/
private class MySqlExecutionFunction extends SqlExecutionFunction {
MySqlExecutionFunction() {
this.hasName(["mysql_query", "mysql_real_query", "mysql_real_query_nonblocking"])
}
override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
}
/**
* The `mysql_real_escape_string` family of functions from the MySQL C API.
*/
private class MySqlBarrierFunction extends SqlBarrierFunction {
MySqlBarrierFunction() {
this.hasName(["mysql_real_escape_string", "mysql_real_escape_string_quote"])
}
override predicate barrierSqlArgument(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(2) and
output.isParameterDeref(1)
}
}

View File

@@ -1,94 +0,0 @@
private import semmle.code.cpp.models.interfaces.Sql
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
private predicate pqxxTransactionSqlArgument(string function, int arg) {
function = "exec" and arg = 0
or
function = "exec0" and arg = 0
or
function = "exec1" and arg = 0
or
function = "exec_n" and arg = 1
or
function = "exec_params" and arg = 0
or
function = "exec_params0" and arg = 0
or
function = "exec_params1" and arg = 0
or
function = "exec_params_n" and arg = 1
or
function = "query_value" and arg = 0
or
function = "stream" and arg = 0
}
private predicate pqxxConnectionSqlArgument(string function, int arg) {
function = "prepare" and arg = 1
}
private predicate pqxxTransationClassNames(string className, string namespace) {
namespace = "pqxx" and
className in [
"dbtransaction", "nontransaction", "basic_robusttransaction", "robusttransaction",
"subtransaction", "transaction", "basic_transaction", "transaction_base", "work"
]
}
private predicate pqxxConnectionClassNames(string className, string namespace) {
namespace = "pqxx" and
className in ["connection_base", "basic_connection", "connection"]
}
private predicate pqxxEscapeArgument(string function, int arg) {
arg = 0 and
function in ["esc", "esc_raw", "quote", "quote_raw", "quote_name", "quote_table", "esc_like"]
}
private class PostgreSqlExecutionFunction extends SqlExecutionFunction {
PostgreSqlExecutionFunction() {
exists(Class c |
this.getDeclaringType() = c and
// transaction exec and connection prepare variations
(
pqxxTransationClassNames(c.getName(), c.getNamespace().getName()) and
pqxxTransactionSqlArgument(this.getName(), _)
or
pqxxConnectionSqlArgument(this.getName(), _) and
pqxxConnectionClassNames(c.getName(), c.getNamespace().getName())
)
)
}
override predicate hasSqlArgument(FunctionInput input) {
exists(int argIndex |
pqxxTransactionSqlArgument(this.getName(), argIndex)
or
pqxxConnectionSqlArgument(this.getName(), argIndex)
|
input.isParameterDeref(argIndex)
)
}
}
private class PostgreSqlBarrierFunction extends SqlBarrierFunction {
PostgreSqlBarrierFunction() {
exists(Class c |
this.getDeclaringType() = c and
// transaction and connection escape functions
(
pqxxTransationClassNames(c.getName(), c.getNamespace().getName()) or
pqxxConnectionClassNames(c.getName(), c.getNamespace().getName())
) and
pqxxEscapeArgument(this.getName(), _)
)
}
override predicate barrierSqlArgument(FunctionInput input, FunctionOutput output) {
exists(int argIndex |
input.isParameterDeref(argIndex) and
output.isReturnValueDeref() and
pqxxEscapeArgument(this.getName(), argIndex)
)
}
}

View File

@@ -1,21 +0,0 @@
/**
* Provides implementation classes modeling the SQLite C API.
* See `semmle.code.cpp.models.Models` for usage information.
*/
private import semmle.code.cpp.models.interfaces.Sql
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
/**
* The `sqlite3_exec` and `sqlite3_prepare` families of functions from the SQLite C API.
*/
private class SqLite3ExecutionFunction extends SqlExecutionFunction {
SqLite3ExecutionFunction() {
this.hasName([
"sqlite3_exec", "sqlite3_prepare", "sqlite3_prepare_v2", "sqlite3_prepare_v3",
"sqlite3_prepare16", "sqlite3_prepare16_v2", "sqlite3_prepare16_v3"
])
}
override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
}

View File

@@ -16,7 +16,6 @@ private class StrdupFunction extends AllocationFunction, ArrayFunction, DataFlow
hasGlobalName([
// --- C library allocation
"strdup", // strdup(str)
"strdupa", // strdupa(str) - returns stack allocated buffer
"wcsdup", // wcsdup(str)
"_strdup", // _strdup(str)
"_wcsdup", // _wcsdup(str)
@@ -32,8 +31,6 @@ private class StrdupFunction extends AllocationFunction, ArrayFunction, DataFlow
input.isParameterDeref(0) and
output.isReturnValueDeref()
}
override predicate requiresDealloc() { not hasGlobalName("strdupa") }
}
/**
@@ -41,11 +38,11 @@ private class StrdupFunction extends AllocationFunction, ArrayFunction, DataFlow
*/
private class StrndupFunction extends AllocationFunction, ArrayFunction, DataFlowFunction {
StrndupFunction() {
hasGlobalName([
// -- C library allocation
"strndup", // strndup(str, maxlen)
"strndupa" // strndupa(str, maxlen) -- returns stack allocated buffer
])
exists(string name |
hasGlobalName(name) and
// --- C library allocation
name = "strndup" // strndup(str, maxlen)
)
}
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
@@ -59,6 +56,4 @@ private class StrndupFunction extends AllocationFunction, ArrayFunction, DataFlo
) and
output.isReturnValueDeref()
}
override predicate requiresDealloc() { not hasGlobalName("strndupa") }
}

View File

@@ -1,30 +0,0 @@
/**
* Provides abstract classes for modeling functions that execute and escape SQL query strings.
* To extend this QL library, create a QL class extending `SqlExecutionFunction` or `SqlEscapeFunction`
* with a characteristic predicate that selects the function or set of functions you are modeling.
* Within that class, override the predicates provided by the class to match the way a
* parameter flows into the function and, in the case of `SqlEscapeFunction`, out of the function.
*/
private import cpp
/**
* An abstract class that represents a function that executes an SQL query.
*/
abstract class SqlExecutionFunction extends Function {
/**
* Holds if `input` to this function represents SQL code to be executed.
*/
abstract predicate hasSqlArgument(FunctionInput input);
}
/**
* An abstract class that represents a function that is a barrier to an SQL query string.
*/
abstract class SqlBarrierFunction extends Function {
/**
* Holds if the `output` is a barrier to the SQL input `input` such that is it safe to pass to
* an `SqlExecutionFunction`.
*/
abstract predicate barrierSqlArgument(FunctionInput input, FunctionOutput output);
}

View File

@@ -95,19 +95,10 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
/**
* If this definition is a phi node corresponding to a guard,
* then return the variable access and the guard.
* then return the variable and the guard.
*/
predicate isGuardPhi(VariableAccess va, Expr guard, boolean branch) {
guard_defn(va, guard, this, branch)
}
/**
* If this definition is a phi node corresponding to a guard,
* then return the variable guarded, the variable access and the guard.
*/
predicate isGuardPhi(StackVariable v, VariableAccess va, Expr guard, boolean branch) {
guard_defn(va, guard, this, branch) and
va.getTarget() = v
predicate isGuardPhi(VariableAccess v, Expr guard, boolean branch) {
guard_defn(v, guard, this, branch)
}
/** Gets the primary location of this definition. */

View File

@@ -1530,30 +1530,6 @@ private predicate isUnsupportedGuardPhi(Variable v, RangeSsaDefinition phi, Vari
)
}
/**
* Gets the upper bound of the expression, if the expression is guarded.
* An upper bound can only be found, if a guard phi node can be found, and the
* expression has only one immediate predecessor.
*/
private float getGuardedUpperBound(VariableAccess guardedAccess) {
exists(
RangeSsaDefinition def, StackVariable v, VariableAccess guardVa, Expr guard, boolean branch
|
def.isGuardPhi(v, guardVa, guard, branch) and
// If the basic block for the variable access being examined has
// more than one predecessor, the guard phi node could originate
// from one of the predecessors. This is because the guard phi
// node is attached to the block at the end of the edge and not on
// the actual edge. It is therefore not possible to determine which
// edge the guard phi node belongs to. The predicate below ensures
// that there is one predecessor, albeit somewhat conservative.
exists(unique(BasicBlock b | b = def.(BasicBlock).getAPredecessor())) and
guardedAccess = def.getAUse(v) and
result = max(float ub | upperBoundFromGuard(guard, guardVa, ub, branch)) and
not convertedExprMightOverflow(guard.getAChild+())
)
}
cached
private module SimpleRangeAnalysisCached {
/**
@@ -1589,9 +1565,9 @@ private module SimpleRangeAnalysisCached {
*/
cached
float upperBound(Expr expr) {
// Combine the upper bounds returned by getTruncatedUpperBounds and
// getGuardedUpperBound into a single maximum value
result = min([max(getTruncatedUpperBounds(expr)), getGuardedUpperBound(expr)])
// Combine the upper bounds returned by getTruncatedUpperBounds into a
// single maximum value.
result = max(float ub | ub = getTruncatedUpperBounds(expr) | ub)
}
/**

View File

@@ -19,15 +19,6 @@ class FileWrite extends Expr {
* Gets the expression for the object being written to.
*/
Expr getDest() { fileWrite(this, _, result) }
/**
* Gets the conversion character for this write, if it exists and is known. For example in the following code the write of `value1` has conversion character `"s"`, whereas the write of `value2` has no conversion specifier.
* ```
* fprintf(file, "%s", value1);
* stream << value2;
* ```
*/
string getSourceConvChar(Expr source) { fileWriteWithConvChar(this, source, result) }
}
/**
@@ -159,20 +150,3 @@ private predicate fileWrite(Call write, Expr source, Expr dest) {
// file stream using '<<', 'put' or 'write'
fileStreamChain(write, source, dest)
}
/**
* Whether the function call is a write to a file from 'source' with
* conversion character 'conv'. Does not hold if there isn't a conversion
* character, or if it is unknown (for example the format string is not a
* constant).
*/
private predicate fileWriteWithConvChar(FormattingFunctionCall ffc, Expr source, string conv) {
// fprintf
exists(FormattingFunction f, int n |
f = ffc.getTarget() and
source = ffc.getFormatArgument(n)
|
exists(f.getOutputParameterIndex(true)) and
conv = ffc.(FormattingFunctionCall).getFormat().(FormatLiteral).getConversionChar(n)
)
}

View File

@@ -41,8 +41,7 @@ private predicate stmtDominates(Stmt dominator, Stmt dominated) {
}
/**
* Holds if the value of `use` is guarded to be less than something, and `e`
* is in code controlled by that guard (where the guard condition held).
* Holds if the value of `use` is guarded to be less than something.
*/
pragma[nomagic]
predicate guardedLesser(Operation e, Expr use) {
@@ -68,8 +67,7 @@ predicate guardedLesser(Operation e, Expr use) {
}
/**
* Holds if the value of `use` is guarded to be greater than something, and `e`
* is in code controlled by that guard (where the guard condition held).
* Holds if the value of `use` is guarded to be greater than something.
*/
pragma[nomagic]
predicate guardedGreater(Operation e, Expr use) {
@@ -122,10 +120,6 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
// overflow possible if large or small
e instanceof MulExpr and
not (guardedLesser(e, varUse(v)) and guardedGreater(e, varUse(v)))
or
// overflow possible if large or small
e instanceof AssignMulExpr and
not (guardedLesser(e, varUse(v)) and guardedGreater(e, varUse(v)))
)
}
@@ -153,9 +147,5 @@ predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) {
// underflow possible if large or small
e instanceof MulExpr and
not (guardedLesser(e, varUse(v)) and guardedGreater(e, varUse(v)))
or
// underflow possible if large or small
e instanceof AssignMulExpr and
not (guardedLesser(e, varUse(v)) and guardedGreater(e, varUse(v)))
)
}

View File

@@ -7,7 +7,6 @@ import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.commons.Environment
import semmle.code.cpp.security.SecurityOptions
import semmle.code.cpp.models.interfaces.FlowSource
import semmle.code.cpp.models.interfaces.Sql
/**
* Extend this class to customize the security queries for
@@ -35,11 +34,13 @@ class SecurityOptions extends string {
* An argument to a function that is passed to a SQL server.
*/
predicate sqlArgument(string function, int arg) {
exists(FunctionInput input, SqlExecutionFunction sql |
sql.hasName(function) and
input.isParameterDeref(arg) and
sql.hasSqlArgument(input)
)
// MySQL C API
function = "mysql_query" and arg = 1
or
function = "mysql_real_query" and arg = 1
or
// SQLite3 C API
function = "sqlite3_exec" and arg = 1
}
/**

View File

@@ -14,13 +14,14 @@ private predicate suspicious(string s) {
(
s.matches("%password%") or
s.matches("%passwd%") or
s.matches("%account%") or
s.matches("%accnt%") or
s.matches("%trusted%")
) and
not (
s.matches("%hash%") or
s.matches("%crypt%") or
s.matches("%file%") or
s.matches("%path%")
s.matches("%hashed%") or
s.matches("%encrypted%") or
s.matches("%crypt%")
)
}
@@ -28,20 +29,14 @@ private predicate suspicious(string s) {
* A variable that might contain a password or other sensitive information.
*/
class SensitiveVariable extends Variable {
SensitiveVariable() {
suspicious(getName().toLowerCase()) and
not this.getUnspecifiedType() instanceof IntegralType
}
SensitiveVariable() { suspicious(getName().toLowerCase()) }
}
/**
* A function that might return a password or other sensitive information.
*/
class SensitiveFunction extends Function {
SensitiveFunction() {
suspicious(getName().toLowerCase()) and
not this.getUnspecifiedType() instanceof IntegralType
}
SensitiveFunction() { suspicious(getName().toLowerCase()) }
}
/**

View File

@@ -310,14 +310,23 @@ diagnostics(
int location: @location_default ref
);
/*
fromSource(0) = unknown,
fromSource(1) = from source,
fromSource(2) = from library
*/
files(
unique int id: @file,
string name: string ref
string name: string ref,
string simple: string ref,
string ext: string ref,
int fromSource: int ref
);
folders(
unique int id: @folder,
string name: string ref
string name: string ref,
string simple: string ref
);
@container = @folder | @file

View File

@@ -12875,6 +12875,18 @@
<k>name</k>
<v>59320</v>
</e>
<e>
<k>simple</k>
<v>40580</v>
</e>
<e>
<k>ext</k>
<v>97</v>
</e>
<e>
<k>fromSource</k>
<v>10</v>
</e>
</columnsizes>
<dependencies>
<dep>
@@ -12894,6 +12906,54 @@
</val>
</dep>
<dep>
<src>id</src>
<trg>simple</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>59320</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>id</src>
<trg>ext</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>59320</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>id</src>
<trg>fromSource</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>59320</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>name</src>
<trg>id</trg>
<val>
@@ -12909,6 +12969,406 @@
</hist>
</val>
</dep>
<dep>
<src>name</src>
<trg>simple</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>59320</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>name</src>
<trg>ext</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>59320</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>name</src>
<trg>fromSource</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>59320</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>simple</src>
<trg>id</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>30814</v>
</b>
<b>
<a>2</a>
<b>3</b>
<v>6123</v>
</b>
<b>
<a>3</a>
<b>7</b>
<v>3197</v>
</b>
<b>
<a>7</a>
<b>42</b>
<v>444</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>simple</src>
<trg>name</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>30814</v>
</b>
<b>
<a>2</a>
<b>3</b>
<v>6123</v>
</b>
<b>
<a>3</a>
<b>7</b>
<v>3197</v>
</b>
<b>
<a>7</a>
<b>42</b>
<v>444</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>simple</src>
<trg>ext</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>36277</v>
</b>
<b>
<a>2</a>
<b>3</b>
<v>3739</v>
</b>
<b>
<a>3</a>
<b>6</b>
<v>563</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>simple</src>
<trg>fromSource</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>40580</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>ext</src>
<trg>id</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>10</v>
</b>
<b>
<a>3</a>
<b>4</b>
<v>10</v>
</b>
<b>
<a>15</a>
<b>16</b>
<v>10</v>
</b>
<b>
<a>38</a>
<b>39</b>
<v>10</v>
</b>
<b>
<a>80</a>
<b>81</b>
<v>10</v>
</b>
<b>
<a>114</a>
<b>115</b>
<v>10</v>
</b>
<b>
<a>441</a>
<b>442</b>
<v>10</v>
</b>
<b>
<a>768</a>
<b>769</b>
<v>10</v>
</b>
<b>
<a>4013</a>
<b>4014</b>
<v>10</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>ext</src>
<trg>name</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>10</v>
</b>
<b>
<a>3</a>
<b>4</b>
<v>10</v>
</b>
<b>
<a>15</a>
<b>16</b>
<v>10</v>
</b>
<b>
<a>38</a>
<b>39</b>
<v>10</v>
</b>
<b>
<a>80</a>
<b>81</b>
<v>10</v>
</b>
<b>
<a>114</a>
<b>115</b>
<v>10</v>
</b>
<b>
<a>441</a>
<b>442</b>
<v>10</v>
</b>
<b>
<a>768</a>
<b>769</b>
<v>10</v>
</b>
<b>
<a>4013</a>
<b>4014</b>
<v>10</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>ext</src>
<trg>simple</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>10</v>
</b>
<b>
<a>3</a>
<b>4</b>
<v>10</v>
</b>
<b>
<a>15</a>
<b>16</b>
<v>10</v>
</b>
<b>
<a>38</a>
<b>39</b>
<v>10</v>
</b>
<b>
<a>75</a>
<b>76</b>
<v>10</v>
</b>
<b>
<a>112</a>
<b>113</b>
<v>10</v>
</b>
<b>
<a>428</a>
<b>429</b>
<v>10</v>
</b>
<b>
<a>688</a>
<b>689</b>
<v>10</v>
</b>
<b>
<a>2838</a>
<b>2839</b>
<v>10</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>ext</src>
<trg>fromSource</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>97</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>fromSource</src>
<trg>id</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>5473</a>
<b>5474</b>
<v>10</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>fromSource</src>
<trg>name</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>5473</a>
<b>5474</b>
<v>10</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>fromSource</src>
<trg>simple</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>3744</a>
<b>3745</b>
<v>10</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>fromSource</src>
<trg>ext</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>9</a>
<b>10</b>
<v>10</v>
</b>
</bs>
</hist>
</val>
</dep>
</dependencies>
</relation>
<relation>
@@ -12923,6 +13383,10 @@
<k>name</k>
<v>10817</v>
</e>
<e>
<k>simple</k>
<v>3099</v>
</e>
</columnsizes>
<dependencies>
<dep>
@@ -12942,6 +13406,22 @@
</val>
</dep>
<dep>
<src>id</src>
<trg>simple</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>10817</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>name</src>
<trg>id</trg>
<val>
@@ -12957,6 +13437,94 @@
</hist>
</val>
</dep>
<dep>
<src>name</src>
<trg>simple</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>10817</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>simple</src>
<trg>id</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>1669</v>
</b>
<b>
<a>2</a>
<b>3</b>
<v>661</v>
</b>
<b>
<a>3</a>
<b>4</b>
<v>433</v>
</b>
<b>
<a>4</a>
<b>17</b>
<v>238</v>
</b>
<b>
<a>27</a>
<b>121</b>
<v>97</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>simple</src>
<trg>name</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>1669</v>
</b>
<b>
<a>2</a>
<b>3</b>
<v>661</v>
</b>
<b>
<a>3</a>
<b>4</b>
<v>433</v>
</b>
<b>
<a>4</a>
<b>17</b>
<v>238</v>
</b>
<b>
<a>27</a>
<b>121</b>
<v>97</v>
</b>
</bs>
</hist>
</val>
</dep>
</dependencies>
</relation>
<relation>

View File

@@ -8,7 +8,7 @@
* @tags reliability
*/
import NewDelete
import Critical.NewDelete
from Expr alloc, Expr free, Expr freed
where

View File

@@ -8,7 +8,7 @@
* @tags reliability
*/
import NewDelete
import Critical.NewDelete
from Expr alloc, Expr free, Expr freed
where

View File

@@ -11,7 +11,7 @@
* external/cwe/cwe-401
*/
import NewDelete
import Critical.NewDelete
/**
* Holds if `allocKind` and `freeKind` indicate corresponding

View File

@@ -9,7 +9,7 @@
* documentation
*/
import CommentedOutCode
import Documentation.CommentedOutCode
from CommentedOutCode comment
select comment, "This comment appears to contain commented-out code"

View File

@@ -13,15 +13,10 @@
import cpp
from BitField fi, VariableAccess va, Type fct
from BitField fi, VariableAccess va
where
(
if va.getFullyConverted().getType() instanceof ReferenceType
then fct = va.getFullyConverted().getType().(ReferenceType).getBaseType()
else fct = va.getFullyConverted().getType()
) and
fi.getNumBits() > fct.getSize() * 8 and
va.getExplicitlyConverted().getType().getSize() > fct.getSize() and
fi.getNumBits() > va.getFullyConverted().getType().getSize() * 8 and
va.getExplicitlyConverted().getType().getSize() > va.getFullyConverted().getType().getSize() and
va.getTarget() = fi and
not fct.getUnspecifiedType() instanceof BoolType
not va.getActualType() instanceof BoolType
select va, "Implicit downcast of bitfield $@", fi, fi.toString()

View File

@@ -29,19 +29,11 @@ class ImproperNullTerminationReachability extends StackVariableReachabilityWithR
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
node = declWithNoInit(v)
or
exists(Call c, int bufferArg, int sizeArg |
exists(Call c, VariableAccess va |
c = node and
(
c.getTarget().hasName("readlink") and bufferArg = 1 and sizeArg = 2
or
c.getTarget().hasName("readlinkat") and bufferArg = 2 and sizeArg = 3
) and
c.getArgument(bufferArg).(VariableAccess).getTarget() = v and
(
// buffer size parameter likely matches the full buffer size
c.getArgument(sizeArg) instanceof SizeofOperator or
c.getArgument(sizeArg).getValue().toInt() = v.getType().getSize()
)
c.getTarget().hasName("readlink") and
c.getArgument(1) = va and
va.getTarget() = v
)
}

View File

@@ -41,7 +41,7 @@ DeclStmt declWithNoInit(LocalVariable v) {
result.getADeclaration() = v and
not exists(v.getInitializer()) and
/* The type of the variable is not stack-allocated. */
exists(Type t | t = v.getType() | not allocatedType(t))
not allocatedType(v.getType())
}
class UninitialisedLocalReachability extends StackVariableReachability {

View File

@@ -1,12 +0,0 @@
/**
* @name Sum of frontend and extractor time
* @description The sum of elapsed frontend time, and the sum of elapsed extractor time.
* This query is for internal use only and may change without notice.
* @kind table
* @id cpp/frontend-and-extractor-time
*/
import cpp
select sum(Compilation c, float seconds | compilation_time(c, _, 2, seconds) | seconds) as sum_frontend_elapsed_seconds,
sum(Compilation c, float seconds | compilation_time(c, _, 4, seconds) | seconds) as sum_extractor_elapsed_seconds

View File

@@ -30,15 +30,7 @@ class Configuration extends TaintTrackingConfiguration {
}
override predicate isBarrier(Expr e) {
super.isBarrier(e)
or
e.getUnspecifiedType() instanceof IntegralType
or
exists(SqlBarrierFunction sql, int arg, FunctionInput input |
e = sql.getACallToThisFunction().getArgument(arg) and
input.isParameterDeref(arg) and
sql.barrierSqlArgument(input, _)
)
super.isBarrier(e) or e.getUnspecifiedType() instanceof IntegralType
}
}

View File

@@ -74,15 +74,8 @@ private class RandS extends RandomFunction {
predicate missingGuard(VariableAccess va, string effect) {
exists(Operation op | op.getAnOperand() = va |
// underflow - random numbers are usually non-negative, so underflow is
// only likely if the type is unsigned. Multiplication is also unlikely to
// cause underflow of a non-negative number.
missingGuardAgainstUnderflow(op, va) and
effect = "underflow" and
op.getUnspecifiedType().(IntegralType).isUnsigned() and
not op instanceof MulExpr
missingGuardAgainstUnderflow(op, va) and effect = "underflow"
or
// overflow
missingGuardAgainstOverflow(op, va) and effect = "overflow"
)
}
@@ -115,9 +108,6 @@ class UncontrolledArithConfiguration extends TaintTracking::Configuration {
op instanceof BitwiseAndExpr or
op instanceof ComplementExpr
).getAnOperand*()
or
// block unintended flow to pointers
node.asExpr().getUnspecifiedType() instanceof PointerType
}
}

View File

@@ -5,7 +5,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 7.5
* @precision high
* @precision medium
* @id cpp/cleartext-storage-file
* @tags security
* external/cwe/cwe-313
@@ -14,40 +14,10 @@
import cpp
import semmle.code.cpp.security.SensitiveExprs
import semmle.code.cpp.security.FileWrite
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
/**
* An operation on a filename.
*/
predicate filenameOperation(FunctionCall op, Expr path) {
exists(string name | name = op.getTarget().getName() |
name =
[
"remove", "unlink", "rmdir", "rename", "fopen", "open", "freopen", "_open", "_wopen",
"_wfopen", "_fsopen", "_wfsopen", "chmod", "chown", "stat", "lstat", "fstat", "access",
"_access", "_waccess", "_access_s", "_waccess_s"
] and
path = op.getArgument(0)
or
name = ["fopen_s", "wfopen_s", "rename"] and
path = op.getArgument(1)
)
}
predicate isFileName(GVN gvn) {
exists(FunctionCall op, Expr path |
filenameOperation(op, path) and
gvn = globalValueNumber(path)
)
}
from FileWrite w, SensitiveExpr source, Expr mid, Expr dest
from FileWrite w, SensitiveExpr source, Expr dest
where
DataFlow::localFlow(DataFlow::exprNode(source), DataFlow::exprNode(mid)) and
mid = w.getASource() and
dest = w.getDest() and
not isFileName(globalValueNumber(source)) and // file names are not passwords
not exists(string convChar | convChar = w.getSourceConvChar(mid) | not convChar = ["s", "S"]) // ignore things written with other conversion characters
source = w.getASource() and
dest = w.getDest()
select w, "This write into file '" + dest.toString() + "' may contain unencrypted data from $@",
source, "this source."

View File

@@ -6,7 +6,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 7.7
* @precision high
* @precision medium
* @id cpp/toctou-race-condition
* @tags security
* external/cwe/cwe-367
@@ -16,60 +16,59 @@ import cpp
import semmle.code.cpp.controlflow.Guards
/**
* An operation on a filename that is likely to modify the corresponding file
* and may return an indication of success.
* An operation on a filename.
*
* Note: we're not interested in operations where the file is specified by a
* descriptor, rather than a filename, as they are better behaved. We are
* interested in functions that take a filename and return a file descriptor,
* however.
* Note: we're not interested in operations on file descriptors, as they
* are better behaved.
*/
FunctionCall filenameOperation(Expr path) {
exists(string name | name = result.getTarget().getName() |
name =
[
"remove", "unlink", "rmdir", "rename", "fopen", "open", "freopen", "_open", "_wopen",
"_wfopen", "_fsopen", "_wfsopen"
] and
(
name = "remove" or
name = "unlink" or
name = "rmdir" or
name = "rename" or
name = "chmod" or
name = "chown" or
name = "fopen" or
name = "open" or
name = "freopen" or
name = "_open" or
name = "_wopen" or
name = "_wfopen"
) and
result.getArgument(0) = path
or
name = ["fopen_s", "wfopen_s", "rename"] and
(
name = "fopen_s" or
name = "wfopen_s"
) and
result.getArgument(1) = path
)
or
result = sensitiveFilenameOperation(path)
}
/**
* An operation on a filename that is likely to modify the security properties
* of the corresponding file and may return an indication of success.
*/
FunctionCall sensitiveFilenameOperation(Expr path) {
exists(string name | name = result.getTarget().getName() |
name = ["chmod", "chown"] and
result.getArgument(0) = path
)
}
/**
* An operation on a filename that returns information in the return value but
* does not modify the corresponding file. For example, `access`.
* A use of `access` (or similar) on a filename.
*/
FunctionCall accessCheck(Expr path) {
exists(string name | name = result.getTarget().getName() |
name = ["access", "_access", "_waccess", "_access_s", "_waccess_s"]
name = "access" or
name = "_access" or
name = "_waccess" or
name = "_access_s" or
name = "_waccess_s"
) and
path = result.getArgument(0)
}
/**
* An operation on a filename that returns information via a pointer argument
* and any return value, but does not modify the corresponding file. For
* example, `stat`.
* A use of `stat` (or similar) on a filename.
*/
FunctionCall stat(Expr path, Expr buf) {
exists(string name | name = result.getTarget().getName() |
name = ["stat", "lstat", "fstat"] or
name = "stat" or
name = "lstat" or
name = "fstat" or
name.matches("\\_stat%") or
name.matches("\\_wstat%")
) and
@@ -78,7 +77,7 @@ FunctionCall stat(Expr path, Expr buf) {
}
/**
* Holds if `use` refers to `source`, either by being the same or by
* Holds if `use` points to `source`, either by being the same or by
* one step of variable indirection.
*/
predicate referenceTo(Expr source, Expr use) {
@@ -89,45 +88,36 @@ predicate referenceTo(Expr source, Expr use) {
)
}
from Expr check, Expr checkPath, FunctionCall use, Expr usePath
from FunctionCall fc, Expr check, Expr checkUse, Expr opUse
where
// `check` looks like a check on a filename
// checkUse looks like a check on a filename
(
(
// either:
// an access check
check = accessCheck(checkPath)
or
// a stat
check = stat(checkPath, _)
or
// access to a member variable on the stat buf
// (morally, this should be a use-use pair, but it seems unlikely
// that this variable will get reused in practice)
exists(Expr call, Expr e, Variable v |
call = stat(checkPath, e) and
e.getAChild*().(VariableAccess).getTarget() = v and
check.(VariableAccess).getTarget() = v and
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
)
) and
// `op` looks like an operation on a filename
use = filenameOperation(usePath)
// either:
// an access check
check = accessCheck(checkUse)
or
// a stat
check = stat(checkUse, _)
or
// another filename operation (null pointers can indicate errors)
check = filenameOperation(checkPath) and
// `op` looks like a sensitive operation on a filename
use = sensitiveFilenameOperation(usePath)
check = filenameOperation(checkUse)
or
// access to a member variable on the stat buf
// (morally, this should be a use-use pair, but it seems unlikely
// that this variable will get reused in practice)
exists(Variable buf | exists(stat(checkUse, buf.getAnAccess())) |
check.(VariableAccess).getQualifier() = buf.getAnAccess()
)
) and
// `checkPath` and `usePath` refer to the same SSA variable
exists(SsaDefinition def, StackVariable v |
def.getAUse(v) = checkPath and def.getAUse(v) = usePath
) and
// the return value of `check` is used (possibly with one step of
// variable indirection) in a guard which controls `use`
// checkUse and opUse refer to the same SSA variable
exists(SsaDefinition def, StackVariable v | def.getAUse(v) = checkUse and def.getAUse(v) = opUse) and
// opUse looks like an operation on a filename
fc = filenameOperation(opUse) and
// the return value of check is used (possibly with one step of
// variable indirection) in a guard which controls fc
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
guard.controls(use.(ControlFlowNode).getBasicBlock(), _)
guard.controls(fc.(ControlFlowNode).getBasicBlock(), _)
)
select use,
select fc,
"The $@ being operated upon was previously $@, but the underlying file may have been changed since then.",
usePath, "filename", check, "checked"
opUse, "filename", check, "checked"

View File

@@ -182,7 +182,7 @@ class ThrowingAllocator extends Function {
// 3. the allocator isn't marked with `throw()` or `noexcept`.
not exists(this.getBlock()) and
not exists(Parameter p | p = this.getAParameter() |
p.getUnspecifiedType().stripType() instanceof NoThrowType
p.getUnspecifiedType() instanceof NoThrowType
) and
not this.isNoExcept() and
not this.isNoThrow()

View File

@@ -26,7 +26,7 @@ can use their own storage.</p>
<p>Similarly replace calls to <code>localtime</code> with
<code>localtime_r</code>, calls to <code>ctime</code> with
<code>ctime_r</code> and calls to <code>asctime</code> with
<code>asctime_r</code> (if those functions exist on your platform).</p>
<code>asctime_r</code>.</p>
</recommendation>
<example>

View File

@@ -4,7 +4,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 10.0
* @precision medium
* @precision high
* @id cpp/potentially-dangerous-function
* @tags reliability
* security

View File

@@ -3,4 +3,3 @@
- apply: code-scanning-selectors.yml
from: codeql/suite-helpers
- apply: codeql-suites/exclude-slow-queries.yml
from: codeql/cpp-queries

View File

@@ -3,7 +3,6 @@
- apply: lgtm-selectors.yml
from: codeql/suite-helpers
- apply: codeql-suites/exclude-slow-queries.yml
from: codeql/cpp-queries
# These are only for IDE use.
- exclude:
tags contain:

View File

@@ -1,6 +1,5 @@
- description: Security-and-quality queries for C and C++
- queries: .
- apply: security-and-quality-selectors.yml
from: codeql/suite-helpers
from: codeql-suite-helpers
- apply: codeql-suites/exclude-slow-queries.yml
from: codeql/cpp-queries

View File

@@ -3,4 +3,3 @@
- apply: security-extended-selectors.yml
from: codeql/suite-helpers
- apply: codeql-suites/exclude-slow-queries.yml
from: codeql/cpp-queries

View File

@@ -0,0 +1,28 @@
#include <iostream>
#include <stdexcept>
#include <pqxx/pqxx>
int main(int argc, char ** argv) {
if (argc != 2) {
throw std::runtime_error("Give me a string!");
}
pqxx::connection c;
pqxx::work w(c);
// BAD
char *userName = argv[1];
char query1[1000] = {0};
sprintf(query1, "SELECT UID FROM USERS where name = \"%s\"", userName);
pqxx::row r = w.exec1(query1);
w.commit();
std::cout << r[0].as<int>() << std::endl;
// GOOD
pqxx::result r2 = w.exec("SELECT " + w.quote(argv[1]));
w.commit();
std::cout << r2[0][0].c_str() << std::endl;
return 0;
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The code passes user input as part of a SQL query without escaping special elements.
It generates a SQL query to Postgres using <code>sprintf</code>,
with the user-supplied data directly passed as an argument
to <code>sprintf</code>. This leaves the code vulnerable to attack by SQL Injection.</p>
</overview>
<recommendation>
<p>Use a library routine to escape characters in the user-supplied
string before converting it to SQL. Use <code>esc</code> and <code>quote</code> pqxx library functions.</p>
</recommendation>
<example>
<sample src="SqlPqxxTainted.cpp" />
</example>
<references>
<li>MSDN Library: <a href="https://docs.microsoft.com/en-us/sql/relational-databases/security/sql-injection">SQL Injection</a>.</li>
<!-- LocalWords: SQL CWE
-->
</references>
</qhelp>

View File

@@ -0,0 +1,113 @@
/**
* @name Uncontrolled data in SQL query to Postgres
* @description Including user-supplied data in a SQL query to Postgres
* without neutralizing special elements can make code
* vulnerable to SQL Injection.
* @kind path-problem
* @problem.severity error
* @precision high
* @id cpp/sql-injection-via-pqxx
* @tags security
* external/cwe/cwe-089
*/
import cpp
import semmle.code.cpp.security.Security
import semmle.code.cpp.dataflow.TaintTracking
import DataFlow::PathGraph
predicate pqxxTransationClassNames(string className, string namespace) {
namespace = "pqxx" and
className in [
"dbtransaction", "nontransaction", "basic_robusttransaction", "robusttransaction",
"subtransaction", "transaction", "basic_transaction", "transaction_base", "work"
]
}
predicate pqxxConnectionClassNames(string className, string namespace) {
namespace = "pqxx" and
className in ["connection_base", "basic_connection", "connection"]
}
predicate pqxxTransactionSqlArgument(string function, int arg) {
function = "exec" and arg = 0
or
function = "exec0" and arg = 0
or
function = "exec1" and arg = 0
or
function = "exec_n" and arg = 1
or
function = "exec_params" and arg = 0
or
function = "exec_params0" and arg = 0
or
function = "exec_params1" and arg = 0
or
function = "exec_params_n" and arg = 1
or
function = "query_value" and arg = 0
or
function = "stream" and arg = 0
}
predicate pqxxConnectionSqlArgument(string function, int arg) { function = "prepare" and arg = 1 }
Expr getPqxxSqlArgument() {
exists(FunctionCall fc, Expr e, int argIndex, UserType t |
// examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'.
e = fc.getQualifier() and
// to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior
// and return pointer to a connection/transation object
e.getType().refersTo(t) and
// transaction exec and connection prepare variations
(
pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) and
pqxxTransactionSqlArgument(fc.getTarget().getName(), argIndex)
or
pqxxConnectionClassNames(t.getName(), t.getNamespace().getName()) and
pqxxConnectionSqlArgument(fc.getTarget().getName(), argIndex)
) and
result = fc.getArgument(argIndex)
)
}
predicate pqxxEscapeArgument(string function, int arg) {
arg = 0 and
function in ["esc", "esc_raw", "quote", "quote_raw", "quote_name", "quote_table", "esc_like"]
}
predicate isEscapedPqxxArgument(Expr argExpr) {
exists(FunctionCall fc, Expr e, int argIndex, UserType t |
// examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'.
e = fc.getQualifier() and
// to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior
// and return pointer to a connection/transation object
e.getType().refersTo(t) and
// transaction and connection escape functions
(
pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) or
pqxxConnectionClassNames(t.getName(), t.getNamespace().getName())
) and
pqxxEscapeArgument(fc.getTarget().getName(), argIndex) and
// is escaped arg == argExpr
argExpr = fc.getArgument(argIndex)
)
}
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "SqlPqxxTainted" }
override predicate isSource(DataFlow::Node source) { isUserInput(source.asExpr(), _) }
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = getPqxxSqlArgument() }
override predicate isSanitizer(DataFlow::Node node) { isEscapedPqxxArgument(node.asExpr()) }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config, string taintCause
where
config.hasFlowPath(source, sink) and
isUserInput(source.getNode().asExpr(), taintCause)
select sink, source, sink, "This argument to a SQL query function is derived from $@", source,
"user input (" + taintCause + ")"

View File

@@ -1,34 +0,0 @@
...
int i1;
char c1;
...
if((c1<50)&&(c>10))
switch(c1){
case 300: // BAD: the code will not be executed
...
if((i1<5)&&(i1>0))
switch(i1){ // BAD
case 21: // BAD: the code will not be executed
...
switch(c1){
...
dafault: // BAD: maybe it will be right `default`
...
}
...
switch(c1){
i1=c1*2; // BAD: the code will not be executed
case 12:
...
switch(c1){ // GOOD
case 12:
break;
case 10:
break;
case 9:
break;
default:
break;
}
...

View File

@@ -1,24 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A mismatch between conditionals and <code>switch</code> cases can lead to control-flow violations (CWE-691) where the developer either does not handle all combinations of conditions or unintentionally created dead code (CWE-561).</p>
</overview>
<example>
<p>The following example demonstrates fallacious and fixed ways of using a <code>switch</code> statement.</p>
<sample src="FindIncorrectlyUsedSwitch.c" />
</example>
<references>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">MSC12-C. Detect and remove code that has no effect or is never executed</a>.
</li>
</references>
</qhelp>

View File

@@ -1,153 +0,0 @@
/**
* @name Incorrect switch statement
* @description --Finding places the dangerous use of a switch.
* --For example, when the range of values for a condition does not cover all of the selection values..
* @kind problem
* @id cpp/operator-find-incorrectly-used-switch
* @problem.severity warning
* @precision medium
* @tags correctness
* security
* external/cwe/cwe-561
* external/cwe/cwe-691
* external/cwe/cwe-478
*/
import cpp
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
/** Holds if the range contains no boundary values. */
predicate isRealRange(Expr exp) {
upperBound(exp).toString() != "18446744073709551616" and
upperBound(exp).toString() != "9223372036854775807" and
upperBound(exp).toString() != "4294967295" and
upperBound(exp).toString() != "Infinity" and
upperBound(exp).toString() != "NaN" and
lowerBound(exp).toString() != "-9223372036854775808" and
lowerBound(exp).toString() != "-4294967296" and
lowerBound(exp).toString() != "-Infinity" and
lowerBound(exp).toString() != "NaN" and
upperBound(exp) != 2147483647 and
upperBound(exp) != 268435455 and
upperBound(exp) != 33554431 and
upperBound(exp) != 8388607 and
upperBound(exp) != 65535 and
upperBound(exp) != 32767 and
upperBound(exp) != 255 and
upperBound(exp) != 127 and
upperBound(exp) != 63 and
upperBound(exp) != 31 and
upperBound(exp) != 15 and
upperBound(exp) != 7 and
lowerBound(exp) != -2147483648 and
lowerBound(exp) != -268435456 and
lowerBound(exp) != -33554432 and
lowerBound(exp) != -8388608 and
lowerBound(exp) != -65536 and
lowerBound(exp) != -32768 and
lowerBound(exp) != -128
}
/** Holds if the range of values for the condition is less than the choices. */
predicate isNotAllSelected(SwitchStmt swtmp) {
not swtmp.getExpr().isConstant() and
exists(int i |
i != 0 and
(
i = lowerBound(swtmp.getASwitchCase().getExpr()) and
upperBound(swtmp.getExpr()) < i
or
(
i = upperBound(swtmp.getASwitchCase().getExpr()) or
i = upperBound(swtmp.getASwitchCase().getEndExpr())
) and
lowerBound(swtmp.getExpr()) > i
)
)
}
/** Holds if the range of values for the condition is greater than the selection. */
predicate isConditionBig(SwitchStmt swtmp) {
not swtmp.hasDefaultCase() and
not exists(int iu, int il |
(
iu = upperBound(swtmp.getASwitchCase().getExpr()) or
iu = upperBound(swtmp.getASwitchCase().getEndExpr())
) and
upperBound(swtmp.getExpr()) = iu and
(
il = lowerBound(swtmp.getASwitchCase().getExpr()) or
il = lowerBound(swtmp.getASwitchCase().getEndExpr())
) and
lowerBound(swtmp.getExpr()) = il
)
}
/** Holds if there are labels inside the block with names similar to `default` or `case`. */
predicate isWrongLableName(SwitchStmt swtmp) {
not swtmp.hasDefaultCase() and
exists(LabelStmt lb |
(
(
lb.getName().charAt(0) = "d" or
lb.getName().charAt(0) = "c"
) and
(
lb.getName().charAt(1) = "e" or
lb.getName().charAt(1) = "a"
) and
(
lb.getName().charAt(2) = "f" or
lb.getName().charAt(2) = "s"
)
) and
lb.getEnclosingStmt().getParentStmt*() = swtmp.getStmt() and
not exists(GotoStmt gs | gs.getName() = lb.getName())
)
}
/** Holds if the block contains code before the first `case`. */
predicate isCodeBeforeCase(SwitchStmt swtmp) {
exists(Expr exp |
exp.getEnclosingStmt().getParentStmt*() = swtmp.getStmt() and
not exists(Loop lp |
exp.getEnclosingStmt().getParentStmt*() = lp and
lp.getEnclosingStmt().getParentStmt*() = swtmp.getStmt()
) and
not exists(Stmt sttmp, SwitchCase sctmp |
sttmp = swtmp.getASwitchCase().getAStmt() and
sctmp = swtmp.getASwitchCase() and
(
exp.getEnclosingStmt().getParentStmt*() = sttmp or
exp.getEnclosingStmt() = sctmp
)
)
)
}
from SwitchStmt sw, string msg
where
isRealRange(sw.getExpr()) and
lowerBound(sw.getExpr()) != upperBound(sw.getExpr()) and
lowerBound(sw.getExpr()) != 0 and
not exists(Expr cexp |
cexp = sw.getASwitchCase().getExpr() and not isRealRange(cexp)
or
cexp = sw.getASwitchCase().getEndExpr() and not isRealRange(cexp)
) and
not exists(Expr exptmp |
exptmp = sw.getExpr().getAChild*() and
not exptmp.isConstant() and
not isRealRange(exptmp)
) and
(sw.getASwitchCase().terminatesInBreakStmt() or sw.getASwitchCase().terminatesInReturnStmt()) and
(
isNotAllSelected(sw) and msg = "The range of condition values is less than the selection."
or
isConditionBig(sw) and msg = "The range of condition values is wider than the choices."
)
or
isWrongLableName(sw) and msg = "Possibly erroneous label name."
or
isCodeBeforeCase(sw) and msg = "Code before case will not be executed."
select sw, msg

View File

@@ -1,13 +0,0 @@
...
fs = socket(AF_UNIX, SOCK_STREAM, 0)
...
close(fs);
fs = -1; // GOOD
...
...
fs = socket(AF_UNIX, SOCK_STREAM, 0)
...
close(fs);
if(fs) close(fs); // BAD
...

View File

@@ -1,26 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Double release of the descriptor can lead to a crash of the program. Requires the attention of developers.</p>
</overview>
<recommendation>
<p>We recommend that you exclude situations of possible double release.</p>
</recommendation>
<example>
<p>The following example demonstrates an erroneous and corrected use of descriptor deallocation.</p>
<sample src="DoubleRelease.c" />
</example>
<references>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO46-C.+Do+not+access+a+closed+file">FIO46-C. Do not access a closed file</a>.
</li>
</references>
</qhelp>

View File

@@ -1,142 +0,0 @@
/**
* @name Errors When Double Release
* @description Double release of the descriptor can lead to a crash of the program.
* @kind problem
* @id cpp/double-release
* @problem.severity warning
* @precision medium
* @tags security
* external/cwe/cwe-675
* external/cwe/cwe-666
*/
import cpp
import semmle.code.cpp.commons.File
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.valuenumbering.HashCons
/**
* A function call that potentially does not return (such as `exit`).
*/
class CallMayNotReturn extends FunctionCall {
CallMayNotReturn() {
// call that is known to not return
not exists(this.(ControlFlowNode).getASuccessor())
or
// call to another function that may not return
exists(CallMayNotReturn exit | getTarget() = exit.getEnclosingFunction())
or
exists(ThrowExpr tex | tex = this.(ControlFlowNode).getASuccessor())
}
}
/** Holds if there are no assignment expressions to the function argument. */
pragma[inline]
predicate checkChangeVariable(FunctionCall fc0, ControlFlowNode fc1, ControlFlowNode fc2) {
not exists(Expr exptmp |
(
exptmp = fc0.getArgument(0).(VariableAccess).getTarget().getAnAssignedValue() or
exptmp.(AddressOfExpr).getOperand() =
fc0.getArgument(0).(VariableAccess).getTarget().getAnAccess()
) and
exptmp = fc1.getASuccessor*() and
exptmp = fc2.getAPredecessor*()
) and
(
(
not fc0.getArgument(0) instanceof PointerFieldAccess and
not fc0.getArgument(0) instanceof ValueFieldAccess
or
fc0.getArgument(0).(VariableAccess).getQualifier() instanceof ThisExpr
)
or
not exists(Expr exptmp |
(
exptmp =
fc0.getArgument(0)
.(VariableAccess)
.getQualifier()
.(VariableAccess)
.getTarget()
.getAnAssignedValue() or
exptmp.(AddressOfExpr).getOperand() =
fc0.getArgument(0)
.(VariableAccess)
.getQualifier()
.(VariableAccess)
.getTarget()
.getAnAccess()
) and
exptmp = fc1.getASuccessor*() and
exptmp = fc2.getAPredecessor*()
)
)
}
/** Holds if the underlying expression is a call to the close function. Provided that the function parameter does not change after the call. */
predicate closeReturn(FunctionCall fc) {
fcloseCall(fc, _) and
checkChangeVariable(fc, fc, fc.getEnclosingFunction())
}
/** Holds if the underlying expression is a call to the close function. Provided that the function parameter does not change before the call. */
predicate closeWithoutChangeBefore(FunctionCall fc) {
fcloseCall(fc, _) and
checkChangeVariable(fc, fc.getEnclosingFunction().getEntryPoint(), fc)
}
/** Holds, if a sequential call of the specified functions is possible, via a higher-level function call. */
predicate callInOtherFunctions(FunctionCall fc, FunctionCall fc1) {
exists(FunctionCall fec1, FunctionCall fec2 |
fc.getEnclosingFunction() != fc1.getEnclosingFunction() and
fec1 = fc.getEnclosingFunction().getACallToThisFunction() and
fec2 = fc1.getEnclosingFunction().getACallToThisFunction() and
fec1.getASuccessor*() = fec2 and
checkChangeVariable(fc, fec1, fec2)
)
}
/** Holds if successive calls to close functions are possible. */
predicate interDoubleCloseFunctions(FunctionCall fc, FunctionCall fc1) {
fcloseCall(fc, _) and
fcloseCall(fc1, _) and
fc != fc1 and
fc.getASuccessor*() = fc1 and
checkChangeVariable(fc, fc, fc1)
}
/** Holds if the first arguments of the two functions are similar. */
predicate similarArguments(FunctionCall fc, FunctionCall fc1) {
globalValueNumber(fc.getArgument(0)) = globalValueNumber(fc1.getArgument(0))
or
fc.getArgument(0).(VariableAccess).getTarget() = fc1.getArgument(0).(VariableAccess).getTarget() and
(
not fc.getArgument(0) instanceof PointerFieldAccess and
not fc.getArgument(0) instanceof ValueFieldAccess
or
fc.getArgument(0).(VariableAccess).getQualifier() instanceof ThisExpr
)
or
fc.getArgument(0).(VariableAccess).getTarget() = fc1.getArgument(0).(VariableAccess).getTarget() and
(
fc.getArgument(0) instanceof PointerFieldAccess or
fc.getArgument(0) instanceof ValueFieldAccess
) and
hashCons(fc.getArgument(0)) = hashCons(fc1.getArgument(0))
}
from FunctionCall fc, FunctionCall fc1
where
not exists(CallMayNotReturn fctmp | fctmp = fc.getASuccessor*()) and
not exists(IfStmt ifs | ifs.getCondition().getAChild*() = fc) and
(
// detecting a repeated call situation within one function
closeReturn(fc) and
closeWithoutChangeBefore(fc1) and
callInOtherFunctions(fc, fc1)
or
// detection of repeated call in different functions
interDoubleCloseFunctions(fc, fc1)
) and
similarArguments(fc, fc1)
select fc, "Second call to the $@ function is possible.", fc1, fc1.getTarget().getName()

Some files were not shown because too many files have changed in this diff Show More