mirror of
https://github.com/github/codeql.git
synced 2025-12-21 11:16:30 +01:00
Merge remote-tracking branch 'upstream/master' into ir-copy-unloaded-result
Fixed conflicts by accepting new qltest output.
Conflicts:
cpp/ql/test/library-tests/ir/ir/raw_ir.expected
cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected
cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected
cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected
cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected
This commit is contained in:
@@ -14,9 +14,16 @@ import cpp
|
||||
|
||||
from RelationalOperation e, BinaryBitwiseOperation lhs
|
||||
where
|
||||
lhs = e.getGreaterOperand() and
|
||||
lhs.getActualType().(IntegralType).isSigned() and
|
||||
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
|
||||
// `lhs > 0` (or `0 < lhs`)
|
||||
// (note that `lhs < 0`, `lhs >= 0` or `lhs <= 0` all imply that the signedness of
|
||||
// `lhs` is understood, so should not be flagged).
|
||||
(e instanceof GTExpr or e instanceof LTExpr) and
|
||||
e.getGreaterOperand() = lhs and
|
||||
e.getLesserOperand().getValue() = "0" and
|
||||
// lhs is signed
|
||||
lhs.getActualType().(IntegralType).isSigned() and
|
||||
// if `lhs` has the form `x & c`, with constant `c`, `c` is negative
|
||||
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
|
||||
// exception for cases involving macros
|
||||
not e.isAffectedByMacro()
|
||||
select e, "Potential unsafe sign check of a bitwise operation."
|
||||
|
||||
@@ -14,5 +14,8 @@
|
||||
import cpp
|
||||
|
||||
from ComparisonOperation co, ComparisonOperation chco
|
||||
where co.getAChild() = chco and not chco.isParenthesised()
|
||||
where
|
||||
co.getAChild() = chco and
|
||||
not chco.isParenthesised() and
|
||||
not co.isFromUninstantiatedTemplate(_)
|
||||
select co, "Check the comparison operator precedence."
|
||||
|
||||
@@ -30,7 +30,7 @@ private predicate additionalLogicalCheck(Expr e, string operation, int valueToCh
|
||||
/**
|
||||
* An `Operation` that seems to be checking for leap year.
|
||||
*/
|
||||
class CheckForLeapYearOperation extends Operation {
|
||||
class CheckForLeapYearOperation extends Expr {
|
||||
CheckForLeapYearOperation() {
|
||||
exists(BinaryArithmeticOperation bo | bo = this |
|
||||
bo.getAnOperand().getValue().toInt() = 4 and
|
||||
@@ -39,8 +39,6 @@ class CheckForLeapYearOperation extends Operation {
|
||||
additionalLogicalCheck(this.getEnclosingElement(), "%", 400)
|
||||
)
|
||||
}
|
||||
|
||||
override string getOperator() { result = "LeapYearCheck" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols.</p>
|
||||
</overview>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @name Boost_asio TLS Settings Misconfiguration
|
||||
* @description Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id cpp/boost/tls_settings_misconfiguration
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.boostorg.asio.protocols
|
||||
|
||||
class ExistsAnyFlowConfig extends DataFlow::Configuration {
|
||||
ExistsAnyFlowConfig() { this = "ExistsAnyFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { any() }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { any() }
|
||||
}
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
||||
exists(
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
|
||||
Expr optionsSink
|
||||
|
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
exists(VariableAccess contextSetOptions |
|
||||
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionNotSet(ConstructorCall cc, int flag) {
|
||||
not exists(
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
|
||||
Expr optionsSink
|
||||
|
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
exists(VariableAccess contextSetOptions |
|
||||
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(FunctionCall fcSetOptions, BoostorgAsio::SslSetOptionsFunction f |
|
||||
f.getACallToThisFunction() = fcSetOptions
|
||||
|
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor,
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, Expr protocolSource, Expr protocolSink,
|
||||
ConstructorCall cc, Expr e, string msg
|
||||
where
|
||||
configConstructor.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
|
||||
cc.getArgument(0) = protocolSink and
|
||||
(
|
||||
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not exists(Expr optionsSink |
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
|
||||
)
|
||||
or
|
||||
BoostorgAsio::isExprTlsBoostProtocol(protocolSource) and
|
||||
not BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not exists(Expr optionsSink |
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
|
||||
)
|
||||
) and
|
||||
(
|
||||
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3()) and
|
||||
e = cc and
|
||||
msg = "no_sslv3 has not been set"
|
||||
or
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1()) and
|
||||
e = cc and
|
||||
msg = "no_tlsv1 has not been set"
|
||||
or
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1()) and
|
||||
e = cc and
|
||||
msg = "no_tlsv1_1 has not been set"
|
||||
or
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2(), e) and
|
||||
msg = "no_tlsv1_2 was set"
|
||||
)
|
||||
select cc, "Usage of $@ with protocol $@ is not configured correctly: The option $@.", cc,
|
||||
"boost::asio::ssl::context::context", protocolSource, protocolSource.toString(), e, msg
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using boost::asio library but specifying a deprecated hardcoded protocol.</p>
|
||||
<p>Using a deprecated hardcoded protocol instead of negotiting would lock your application to a protocol that has known vulnerabilities or weaknesses.</p>
|
||||
</overview>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @name boost::asio Use of deprecated hardcoded Protocol
|
||||
* @description Using a deprecated hard-coded protocol using the boost::asio library.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id cpp/boost/use-of-deprecated-hardcoded-security-protocol
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.boostorg.asio.protocols
|
||||
|
||||
from
|
||||
BoostorgAsio::SslContextCallConfig config, Expr protocolSource, Expr protocolSink,
|
||||
ConstructorCall cc
|
||||
where
|
||||
config.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
|
||||
not exists(BoostorgAsio::SslContextCallTlsProtocolConfig tlsConfig |
|
||||
tlsConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
|
||||
) and
|
||||
cc.getArgument(0) = protocolSink and
|
||||
exists(BoostorgAsio::SslContextCallBannedProtocolConfig bannedConfig |
|
||||
bannedConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
|
||||
)
|
||||
select protocolSink, "Usage of $@ specifying a deprecated hardcoded protocol $@ in function $@.",
|
||||
cc, "boost::asio::ssl::context::context", protocolSource, protocolSource.toString(),
|
||||
cc.getEnclosingFunction(), cc.getEnclosingFunction().toString()
|
||||
14
cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
Normal file
14
cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
Normal file
@@ -0,0 +1,14 @@
|
||||
- description: Standard LGTM queries for C/C++, including ones not displayed by default
|
||||
- qlpack: codeql-cpp
|
||||
- apply: lgtm-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
# These queries are infeasible to compute on large projects:
|
||||
- exclude:
|
||||
query path:
|
||||
- Security/CWE/CWE-497/ExposedSystemData.ql
|
||||
- Critical/DescriptorMayNotBeClosed.ql
|
||||
- Critical/DescriptorNeverClosed.ql
|
||||
- Critical/FileMayNotBeClosed.ql
|
||||
- Critical/FileNeverClosed.ql
|
||||
- Critical/MemoryMayNotBeFreed.ql
|
||||
- Critical/MemoryNeverFreed.ql
|
||||
4
cpp/ql/src/codeql-suites/cpp-lgtm.qls
Normal file
4
cpp/ql/src/codeql-suites/cpp-lgtm.qls
Normal file
@@ -0,0 +1,4 @@
|
||||
- description: Standard LGTM queries for C/C++
|
||||
- apply: codeql-suites/cpp-lgtm-full.qls
|
||||
- apply: lgtm-displayed-only.yml
|
||||
from: codeql-suite-helpers
|
||||
@@ -51,7 +51,7 @@ class ReferenceCopyAssignmentOperator extends MemberFunction {
|
||||
|
||||
/**
|
||||
* A call to a function called swap. Note: could be a member,
|
||||
* std::swap or a function overloading std::swap (not in std::)
|
||||
* `std::swap` or a function overloading `std::swap` (not in `std::`)
|
||||
* so keep it simple
|
||||
*/
|
||||
FunctionCall getASwapCall() {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
/*
|
||||
* See More Effective C++ item 7.
|
||||
* Note: Meyers allows unary & to be overloaded but not comma
|
||||
* Note: Meyers allows unary `&` to be overloaded but not comma.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -15,11 +15,11 @@ import cpp
|
||||
/*
|
||||
* Interpretation and deviations:
|
||||
* - if the higher operator has precedence > arithmetic then it is fine
|
||||
* RATIONALE: exprs like f(), *x, &x are easily understood to bind tightly
|
||||
* RATIONALE: exprs like `f()`, `*x`, `&x` are easily understood to bind tightly
|
||||
* - if the higher operator is the RHS of an assign then it is fine
|
||||
* RATIONALE: cf. MISRA, too many cases excluded otherwise
|
||||
* - comparison operators can be mixed with arithmetic
|
||||
* RATIONALE: x==y+z is common and unambiguous
|
||||
* RATIONALE: `x==y+z` is common and unambiguous
|
||||
*/
|
||||
|
||||
predicate arithmeticPrecedence(int p) { p = 12 or p = 13 }
|
||||
|
||||
4
cpp/ql/src/qlpack.yml
Normal file
4
cpp/ql/src/qlpack.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
name: codeql-cpp
|
||||
version: 0.0.0
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
suites: codeql-suites
|
||||
@@ -14,8 +14,12 @@ private import semmle.code.cpp.internal.QualifiedName as Q
|
||||
* ```
|
||||
* extern int myglobal;
|
||||
* ```
|
||||
* Each of these declarations is given its own distinct `DeclarationEntry`,
|
||||
* but they all share the same `Declaration`.
|
||||
* and defined in one:
|
||||
* ```
|
||||
* int myglobal;
|
||||
* ```
|
||||
* Each of these declarations (including the definition) is given its own
|
||||
* distinct `DeclarationEntry`, but they all share the same `Declaration`.
|
||||
*
|
||||
* Some derived class of `Declaration` do not have a corresponding
|
||||
* `DeclarationEntry`, because they always have a unique source location.
|
||||
@@ -206,9 +210,19 @@ abstract class Declaration extends Locatable, @declaration {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ declaration entry. See the comment above `Declaration` for an
|
||||
* explanation of the relationship between `Declaration` and
|
||||
* `DeclarationEntry`.
|
||||
* A C/C++ declaration entry. For example the following code contains five
|
||||
* declaration entries:
|
||||
* ```
|
||||
* extern int myGlobal;
|
||||
* int myVariable;
|
||||
* typedef char MyChar;
|
||||
* void myFunction();
|
||||
* void myFunction() {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* See the comment above `Declaration` for an explanation of the relationship
|
||||
* between `Declaration` and `DeclarationEntry`.
|
||||
*/
|
||||
abstract class DeclarationEntry extends Locatable {
|
||||
/** Gets a specifier associated with this declaration entry. */
|
||||
@@ -281,8 +295,19 @@ abstract class DeclarationEntry extends Locatable {
|
||||
* A declaration that can potentially have more C++ access rights than its
|
||||
* enclosing element. This comprises `Class` (they have access to their own
|
||||
* private members) along with other `UserType`s and `Function` (they can be
|
||||
* the target of `friend` declarations).
|
||||
* the target of `friend` declarations). For example `MyClass` and
|
||||
* `myFunction` in the following code:
|
||||
* ```
|
||||
* class MyClass
|
||||
* {
|
||||
* public:
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
* void myFunction() {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* In the C++ standard (N4140 11.2), rules for access control revolve around
|
||||
* the informal phrase "_R_ occurs in a member or friend of class C", where
|
||||
* `AccessHolder` corresponds to this _R_.
|
||||
@@ -416,8 +441,19 @@ abstract class AccessHolder extends Declaration {
|
||||
/**
|
||||
* A declaration that very likely has more C++ access rights than its
|
||||
* enclosing element. This comprises `Class` (they have access to their own
|
||||
* private members) along with any target of a `friend` declaration.
|
||||
* private members) along with any target of a `friend` declaration. For
|
||||
* example `MyClass` and `friendFunction` in the following code:
|
||||
* ```
|
||||
* class MyClass
|
||||
* {
|
||||
* public:
|
||||
* friend void friendFunction();
|
||||
* };
|
||||
*
|
||||
* void friendFunction() {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* Most access rights are computed for `DirectAccessHolder` instead of
|
||||
* `AccessHolder` -- that's more efficient because there are fewer
|
||||
* `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`,
|
||||
|
||||
@@ -158,3 +158,10 @@ class Parameter extends LocalScopeVariable, @parameter {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `int` that is a parameter index for some function. This is needed for binding in certain cases.
|
||||
*/
|
||||
class ParameterIndex extends int {
|
||||
ParameterIndex() { exists(Parameter p | this = p.getIndex()) }
|
||||
}
|
||||
|
||||
@@ -3,11 +3,17 @@ private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* A C/C++ typedef type. See 4.9.1.
|
||||
*
|
||||
* Represents either of the following typedef styles:
|
||||
*
|
||||
* * CTypedefType: typedef <type> <name>;
|
||||
* * UsingAliasTypedefType: using <name> = <type>;
|
||||
*/
|
||||
class TypedefType extends UserType {
|
||||
TypedefType() { usertypes(underlyingElement(this), _, 5) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "TypedefType" }
|
||||
TypedefType() {
|
||||
usertypes(underlyingElement(this), _, 5) or
|
||||
usertypes(underlyingElement(this), _, 14)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base type of this typedef type.
|
||||
@@ -26,10 +32,6 @@ class TypedefType extends UserType {
|
||||
result = this.getBaseType().getPointerIndirectionLevel()
|
||||
}
|
||||
|
||||
override string explain() {
|
||||
result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
|
||||
}
|
||||
|
||||
override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // Just an alias
|
||||
|
||||
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } // Just an alias
|
||||
@@ -45,6 +47,32 @@ class TypedefType extends UserType {
|
||||
override Type stripType() { result = getBaseType().stripType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A traditional C/C++ typedef type. See 4.9.1.
|
||||
*/
|
||||
class CTypedefType extends TypedefType {
|
||||
CTypedefType() { usertypes(underlyingElement(this), _, 5) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "CTypedefType" }
|
||||
|
||||
override string explain() {
|
||||
result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A using alias C++ typedef type.
|
||||
*/
|
||||
class UsingAliasTypedefType extends TypedefType {
|
||||
UsingAliasTypedefType() { usertypes(underlyingElement(this), _, 14) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "UsingAliasTypedefType" }
|
||||
|
||||
override string explain() {
|
||||
result = "using {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ typedef type that is directly enclosed by a function.
|
||||
*/
|
||||
|
||||
@@ -169,7 +169,11 @@ class FormattingFunctionCall extends Expr {
|
||||
* Gets the number of arguments to this call that are parameters to the
|
||||
* format string.
|
||||
*/
|
||||
int getNumFormatArgument() { result = count(this.getFormatArgument(_)) }
|
||||
int getNumFormatArgument() {
|
||||
result = count(this.getFormatArgument(_)) and
|
||||
// format arguments must be known
|
||||
exists(getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import cpp
|
||||
import BasicBlocks
|
||||
private import semmle.code.cpp.controlflow.internal.ConstantExprs
|
||||
private import semmle.code.cpp.controlflow.internal.CFG
|
||||
|
||||
/**
|
||||
* A control-flow node is either a statement or an expression; in addition,
|
||||
@@ -86,11 +87,11 @@ import ControlFlowGraphPublic
|
||||
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
|
||||
|
||||
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
|
||||
truecond(unresolveElement(n1), unresolveElement(n2))
|
||||
qlCFGTrueSuccessor(n1, n2)
|
||||
}
|
||||
|
||||
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
|
||||
falsecond(unresolveElement(n1), unresolveElement(n2))
|
||||
qlCFGFalseSuccessor(n1, n2)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +121,7 @@ abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase {
|
||||
* `AdditionalControlFlowEdge`. Use this relation instead of `successors`.
|
||||
*/
|
||||
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
|
||||
successors(unresolveElement(source), unresolveElement(target))
|
||||
qlCFGSuccessor(source, target)
|
||||
or
|
||||
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ private predicate compares_eq(
|
||||
|
||||
/**
|
||||
* If `test => part` and `part => left == right + k` then `test => left == right + k`.
|
||||
* Similarly for the case where `test` is false.
|
||||
* Similarly for the case where `test` is false.
|
||||
*/
|
||||
private predicate logical_comparison_eq(
|
||||
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
|
||||
@@ -275,7 +275,7 @@ private predicate compares_ge(
|
||||
|
||||
/**
|
||||
* If `test => part` and `part => left < right + k` then `test => left < right + k`.
|
||||
* Similarly for the case where `test` evaluates false.
|
||||
* Similarly for the case where `test` evaluates false.
|
||||
*/
|
||||
private predicate logical_comparison_lt(
|
||||
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
|
||||
@@ -362,7 +362,7 @@ private predicate add_lt(
|
||||
)
|
||||
}
|
||||
|
||||
/** The int value of integer constant expression. */
|
||||
/** The `int` value of integer constant expression. */
|
||||
private int int_value(Expr e) {
|
||||
e.getUnderlyingType() instanceof IntegralType and
|
||||
result = e.getValue().toInt()
|
||||
|
||||
@@ -29,11 +29,11 @@ class SsaDefinition extends ControlFlowNodeBase {
|
||||
|
||||
/**
|
||||
* Gets a string representation of the SSA variable represented by the pair
|
||||
* (this, v).
|
||||
* `(this, v)`.
|
||||
*/
|
||||
string toString(LocalScopeVariable v) { exists(StandardSSA x | result = x.toString(this, v)) }
|
||||
|
||||
/** Gets a use of the SSA variable represented by the pair (this, v). */
|
||||
/** Gets a use of the SSA variable represented by the pair `(this, v)`. */
|
||||
VariableAccess getAUse(LocalScopeVariable v) {
|
||||
exists(StandardSSA x | result = x.getAUse(this, v))
|
||||
}
|
||||
|
||||
@@ -173,49 +173,36 @@ predicate excludeNode(Node n) {
|
||||
excludeNode(n.getParentNode())
|
||||
}
|
||||
|
||||
private newtype TPos =
|
||||
PosBefore() or
|
||||
PosAt() or
|
||||
PosAfter() or
|
||||
PosBeforeDestructors() or
|
||||
PosAfterDestructors()
|
||||
|
||||
/** A `Pos` without a `bindingset` requirement on the constructor. */
|
||||
private class AnyPos extends TPos {
|
||||
string toString() { result = "Pos" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A constant that indicates the type of sub-node in a pair of `(Node, Pos)`.
|
||||
* See the comment block at the top of this file.
|
||||
*/
|
||||
private class Pos extends AnyPos {
|
||||
// This is to make sure we get compile errors in code that forgets to restrict a `Pos`.
|
||||
private class Pos extends int {
|
||||
bindingset[this]
|
||||
Pos() { any() }
|
||||
|
||||
/** Holds if this is the position just _before_ the associated `Node`. */
|
||||
predicate isBefore() { this = PosBefore() }
|
||||
predicate isBefore() { this = 0 }
|
||||
|
||||
/** Holds if `(n, this)` is the sub-node that represents `n` itself. */
|
||||
predicate isAt() { this = PosAt() }
|
||||
predicate isAt() { this = 1 }
|
||||
|
||||
/** Holds if this is the position just _after_ the associated `Node`. */
|
||||
predicate isAfter() { this = PosAfter() }
|
||||
predicate isAfter() { this = 2 }
|
||||
|
||||
/**
|
||||
* Holds if `(n, this)` is the virtual sub-node that comes just _before_ any
|
||||
* implicit destructor calls following `n`. The node `n` will be some node
|
||||
* that may be followed by local variables going out of scope.
|
||||
*/
|
||||
predicate isBeforeDestructors() { this = PosBeforeDestructors() }
|
||||
predicate isBeforeDestructors() { this = 3 }
|
||||
|
||||
/**
|
||||
* Holds if `(n, this)` is the virtual sub-node that comes just _after_ any
|
||||
* implicit destructor calls following `n`. The node `n` will be some node
|
||||
* that may be followed by local variables going out of scope.
|
||||
*/
|
||||
predicate isAfterDestructors() { this = PosAfterDestructors() }
|
||||
predicate isAfterDestructors() { this = 4 }
|
||||
|
||||
pragma[inline]
|
||||
predicate nodeBefore(Node n, Node nEq) { this.isBefore() and n = nEq }
|
||||
@@ -489,17 +476,6 @@ private Node getLastControlOrderChild(Node n) {
|
||||
result = getControlOrderChildDense(n, max(int i | exists(getControlOrderChildDense(n, i))))
|
||||
}
|
||||
|
||||
private newtype TSpec =
|
||||
SpecPos(AnyPos p) or
|
||||
SpecAround() or
|
||||
SpecAroundDestructors() or
|
||||
SpecBarrier()
|
||||
|
||||
/** A `Spec` without a `bindingset` requirement on the constructor. */
|
||||
private class AnySpec extends TSpec {
|
||||
string toString() { result = "Spec" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A constant that represents two positions: one position for when it's used as
|
||||
* the _source_ of a sub-edge, and another position for when it's used as the
|
||||
@@ -507,25 +483,10 @@ private class AnySpec extends TSpec {
|
||||
* themselves as both source and target, as well as two _around_ values and a
|
||||
* _barrier_ value.
|
||||
*/
|
||||
private class Spec extends AnySpec {
|
||||
private class Spec extends Pos {
|
||||
bindingset[this]
|
||||
Spec() { any() }
|
||||
|
||||
/** See Pos.isBefore. */
|
||||
predicate isBefore() { this = SpecPos(PosBefore()) }
|
||||
|
||||
/** See Pos.isAt. */
|
||||
predicate isAt() { this = SpecPos(PosAt()) }
|
||||
|
||||
/** See Pos.isAfter. */
|
||||
predicate isAfter() { this = SpecPos(PosAfter()) }
|
||||
|
||||
/** See Pos.isBeforeDestructors. */
|
||||
predicate isBeforeDestructors() { this = SpecPos(PosBeforeDestructors()) }
|
||||
|
||||
/** See Pos.isAfterDestructors. */
|
||||
predicate isAfterDestructors() { this = SpecPos(PosAfterDestructors()) }
|
||||
|
||||
/**
|
||||
* Holds if this spec, when used on a node `n` between `(n1, p1)` and
|
||||
* `(n2, p2)`, should add the following sub-edges.
|
||||
@@ -533,7 +494,7 @@ private class Spec extends AnySpec {
|
||||
* (n1, p1) ----> before(n)
|
||||
* after(n) ----> (n2, p2)
|
||||
*/
|
||||
predicate isAround() { this = SpecAround() }
|
||||
predicate isAround() { this = 5 }
|
||||
|
||||
/**
|
||||
* Holds if this spec, when used on a node `n` between `(n1, p1)` and
|
||||
@@ -542,16 +503,17 @@ private class Spec extends AnySpec {
|
||||
* (n1, p1) ----> beforeDestructors(n)
|
||||
* afterDestructors(n) ----> (n2, p2)
|
||||
*/
|
||||
predicate isAroundDestructors() { this = SpecAroundDestructors() }
|
||||
predicate isAroundDestructors() { this = 6 }
|
||||
|
||||
/**
|
||||
* Holds if this node is a _barrier_. A barrier resolves to no positions and
|
||||
* can be inserted between nodes that should have no sub-edges between them.
|
||||
*/
|
||||
predicate isBarrier() { this = SpecBarrier() }
|
||||
predicate isBarrier() { this = 7 }
|
||||
|
||||
Pos getSourcePos() {
|
||||
this = SpecPos(result)
|
||||
this = [0 .. 4] and
|
||||
result = this
|
||||
or
|
||||
this.isAround() and
|
||||
result.isAfter()
|
||||
@@ -561,7 +523,8 @@ private class Spec extends AnySpec {
|
||||
}
|
||||
|
||||
Pos getTargetPos() {
|
||||
this = SpecPos(result)
|
||||
this = [0 .. 4] and
|
||||
result = this
|
||||
or
|
||||
this.isAround() and
|
||||
result.isBefore()
|
||||
@@ -888,6 +851,21 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
|
||||
p2.nodeAfter(n2, s)
|
||||
)
|
||||
or
|
||||
// ConstexprIfStmt -> condition ; { then, else } -> // same as IfStmt
|
||||
exists(ConstexprIfStmt s |
|
||||
p1.nodeAt(n1, s) and
|
||||
p2.nodeBefore(n2, s.getCondition())
|
||||
or
|
||||
p1.nodeAfter(n1, s.getThen()) and
|
||||
p2.nodeBeforeDestructors(n2, s)
|
||||
or
|
||||
p1.nodeAfter(n1, s.getElse()) and
|
||||
p2.nodeBeforeDestructors(n2, s)
|
||||
or
|
||||
p1.nodeAfterDestructors(n1, s) and
|
||||
p2.nodeAfter(n2, s)
|
||||
)
|
||||
or
|
||||
// WhileStmt -> condition ; body -> condition ; after dtors -> after
|
||||
exists(WhileStmt s |
|
||||
p1.nodeAt(n1, s) and
|
||||
@@ -1175,9 +1153,8 @@ private class ExceptionSource extends Node {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `test` is the test of a control-flow construct that will always
|
||||
* have true/false sub-edges out of it, where the `truth`-sub-edge goes to
|
||||
* `(n2, p2)`.
|
||||
* Holds if `test` is the test of a control-flow construct where the `truth`
|
||||
* sub-edge goes to `(n2, p2)`.
|
||||
*/
|
||||
private predicate conditionJumpsTop(Expr test, boolean truth, Node n2, Pos p2) {
|
||||
exists(IfStmt s | test = s.getCondition() |
|
||||
@@ -1192,6 +1169,24 @@ private predicate conditionJumpsTop(Expr test, boolean truth, Node n2, Pos p2) {
|
||||
p2.nodeBeforeDestructors(n2, s)
|
||||
)
|
||||
or
|
||||
exists(ConstexprIfStmt s, string cond |
|
||||
test = s.getCondition() and
|
||||
cond = test.getFullyConverted().getValue()
|
||||
|
|
||||
truth = true and
|
||||
cond != "0" and
|
||||
p2.nodeBefore(n2, s.getThen())
|
||||
or
|
||||
truth = false and
|
||||
cond = "0" and
|
||||
p2.nodeBefore(n2, s.getElse())
|
||||
or
|
||||
not exists(s.getElse()) and
|
||||
truth = false and
|
||||
cond = "0" and
|
||||
p2.nodeBeforeDestructors(n2, s)
|
||||
)
|
||||
or
|
||||
exists(Loop l |
|
||||
(
|
||||
l instanceof WhileStmt
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate localFlowBigStep(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
||||
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
}
|
||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = f.toString() + concat(" : " + ppReprType(t))
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
override string toString() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate localFlowBigStep(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
||||
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
}
|
||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = f.toString() + concat(" : " + ppReprType(t))
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
override string toString() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate localFlowBigStep(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
||||
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
}
|
||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = f.toString() + concat(" : " + ppReprType(t))
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
override string toString() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate localFlowBigStep(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
||||
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
}
|
||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = f.toString() + concat(" : " + ppReprType(t))
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
override string toString() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate localFlowBigStep(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
||||
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
}
|
||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = f.toString() + concat(" : " + ppReprType(t))
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
override string toString() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -51,7 +51,9 @@ class ArgumentNode extends Node {
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
private newtype TReturnKind = TNormalReturnKind()
|
||||
private newtype TReturnKind =
|
||||
TNormalReturnKind() or
|
||||
TRefReturnKind(int i) { exists(Parameter parameter | i = parameter.getIndex()) }
|
||||
|
||||
/**
|
||||
* A return kind. A return kind describes how a value can be returned
|
||||
@@ -59,23 +61,54 @@ private newtype TReturnKind = TNormalReturnKind()
|
||||
*/
|
||||
class ReturnKind extends TReturnKind {
|
||||
/** Gets a textual representation of this return kind. */
|
||||
string toString() { result = "return" }
|
||||
string toString() {
|
||||
this instanceof TNormalReturnKind and
|
||||
result = "return"
|
||||
or
|
||||
this instanceof TRefReturnKind and
|
||||
result = "ref"
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends ExprNode {
|
||||
ReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
|
||||
/** A data flow node that represents a returned value in the called function. */
|
||||
abstract class ReturnNode extends Node {
|
||||
/** Gets the kind of this returned value. */
|
||||
abstract ReturnKind getKind();
|
||||
}
|
||||
|
||||
/** A `ReturnNode` that occurs as the result of a `ReturnStmt`. */
|
||||
private class NormalReturnNode extends ReturnNode, ExprNode {
|
||||
NormalReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
override ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends ExprNode {
|
||||
OutNode() { this.getExpr() instanceof Call }
|
||||
/**
|
||||
* A `ReturnNode` that occurs as a result of a definition of a reference
|
||||
* parameter reaching the end of a function body.
|
||||
*/
|
||||
private class RefReturnNode extends ReturnNode, RefParameterFinalValueNode {
|
||||
/** Gets the kind of this returned value. */
|
||||
override ReturnKind getKind() { result = TRefReturnKind(this.getParameter().getIndex()) }
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call at the call site. */
|
||||
abstract class OutNode extends Node {
|
||||
/** Gets the underlying call. */
|
||||
abstract DataFlowCall getCall();
|
||||
}
|
||||
|
||||
private class ExprOutNode extends OutNode, ExprNode {
|
||||
ExprOutNode() { this.getExpr() instanceof Call }
|
||||
|
||||
/** Gets the underlying call. */
|
||||
DataFlowCall getCall() { result = this.getExpr() }
|
||||
override DataFlowCall getCall() { result = this.getExpr() }
|
||||
}
|
||||
|
||||
private class RefOutNode extends OutNode, DefinitionByReferenceNode {
|
||||
/** Gets the underlying call. */
|
||||
override DataFlowCall getCall() { result = this.getArgument().getParent() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,6 +118,11 @@ class OutNode extends ExprNode {
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result = call.getNode() and
|
||||
kind = TNormalReturnKind()
|
||||
or
|
||||
exists(int i |
|
||||
result.asDefiningArgument() = call.getArgument(i) and
|
||||
kind = TRefReturnKind(i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,3 +302,5 @@ class DataFlowCall extends Expr {
|
||||
/** Gets the enclosing callable of this call. */
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
@@ -25,7 +25,8 @@ private newtype TNode =
|
||||
not c.getTarget().getParameter(i).getUnderlyingType().(PointerType).getBaseType().isConst()
|
||||
)
|
||||
} or
|
||||
TUninitializedNode(LocalVariable v) { not v.hasInitializer() }
|
||||
TUninitializedNode(LocalVariable v) { not v.hasInitializer() } or
|
||||
TRefParameterFinalValueNode(Parameter p) { exists(FlowVar var | var.reachesRefParameter(p)) }
|
||||
|
||||
/**
|
||||
* A node in a data flow graph.
|
||||
@@ -248,6 +249,23 @@ class UninitializedNode extends Node, TUninitializedNode {
|
||||
LocalVariable getLocalVariable() { result = v }
|
||||
}
|
||||
|
||||
/** INTERNAL: do not use. The final value of a non-const ref parameter. */
|
||||
class RefParameterFinalValueNode extends Node, TRefParameterFinalValueNode {
|
||||
Parameter p;
|
||||
|
||||
RefParameterFinalValueNode() { this = TRefParameterFinalValueNode(p) }
|
||||
|
||||
override Function getFunction() { result = p.getFunction() }
|
||||
|
||||
override Type getType() { result = p.getType() }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
|
||||
override Location getLocation() { result = p.getLocation() }
|
||||
|
||||
Parameter getParameter() { result = p }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
* changed its state.
|
||||
@@ -490,7 +508,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
or
|
||||
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
|
||||
) and
|
||||
varToExprStep(var, nodeTo.asExpr())
|
||||
varToNodeStep(var, nodeTo)
|
||||
)
|
||||
or
|
||||
// Expr -> DefinitionByReferenceNode
|
||||
@@ -533,9 +551,13 @@ private predicate exprToVarStep(Expr assignedExpr, FlowVar var) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression `e` is an access of the variable `var`.
|
||||
* Holds if the node `n` is an access of the variable `var`.
|
||||
*/
|
||||
private predicate varToExprStep(FlowVar var, Expr e) { e = var.getAnAccess() }
|
||||
private predicate varToNodeStep(FlowVar var, Node n) {
|
||||
n.asExpr() = var.getAnAccess()
|
||||
or
|
||||
var.reachesRefParameter(n.(RefParameterFinalValueNode).getParameter())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `fromExpr` to `toExpr` directly, in the case
|
||||
@@ -578,8 +600,8 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
||||
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel, int iIn |
|
||||
call.getTarget() = f and
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
outModel.isOutReturnValue() and
|
||||
inModel.isInParameter(iIn) and
|
||||
outModel.isReturnValue() and
|
||||
inModel.isParameter(iIn) and
|
||||
fromExpr = call.getArgument(iIn)
|
||||
)
|
||||
)
|
||||
@@ -589,12 +611,12 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
outModel.isParameterDeref(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
inModel.isParameterDeref(argInIndex) and
|
||||
call.passesByReference(argInIndex, exprIn)
|
||||
or
|
||||
inModel.isInParameter(argInIndex) and
|
||||
inModel.isParameter(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -62,9 +62,20 @@ class FlowVar extends TFlowVar {
|
||||
cached
|
||||
abstract predicate definedByReference(Expr arg);
|
||||
|
||||
/**
|
||||
* Holds if this `FlowVar` is a `PartialDefinition` whose defined expression
|
||||
* is `e`.
|
||||
*/
|
||||
cached
|
||||
abstract predicate definedPartiallyAt(Expr e);
|
||||
|
||||
/**
|
||||
* Holds if this `FlowVar` is a definition of a reference parameter `p` that
|
||||
* persists until the function returns.
|
||||
*/
|
||||
cached
|
||||
abstract predicate reachesRefParameter(Parameter p);
|
||||
|
||||
/**
|
||||
* Holds if this `FlowVar` corresponds to the initial value of `v`. The following
|
||||
* is an exhaustive list of cases where this may happen.
|
||||
@@ -338,6 +349,9 @@ module FlowVar_internal {
|
||||
param = v
|
||||
}
|
||||
|
||||
// `fullySupportedSsaVariable` excludes reference types
|
||||
override predicate reachesRefParameter(Parameter p) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this `SsaVar` corresponds to a non-phi definition. Users of this
|
||||
* library will never directly use an `SsaVar` that comes from a phi node,
|
||||
@@ -387,6 +401,13 @@ module FlowVar_internal {
|
||||
sbb = v.(Parameter).getFunction().getEntryPoint()
|
||||
}
|
||||
|
||||
override predicate reachesRefParameter(Parameter p) {
|
||||
parameterIsNonConstReference(p) and
|
||||
p = v and
|
||||
// This definition reaches the exit node of the function CFG
|
||||
getAReachedBlockVarSBB(this).getANode() = p.getFunction()
|
||||
}
|
||||
|
||||
override predicate definedByInitialValue(LocalScopeVariable lsv) {
|
||||
blockVarDefinedByVariable(sbb, lsv) and
|
||||
lsv = v
|
||||
@@ -490,7 +511,7 @@ module FlowVar_internal {
|
||||
exists(VariableAccess va |
|
||||
va.getTarget() = result and
|
||||
readAccess(va) and
|
||||
bbNotInLoop(va.getBasicBlock())
|
||||
exists(BasicBlock bb | bb = va.getBasicBlock() | not this.bbInLoop(bb))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -513,10 +534,8 @@ module FlowVar_internal {
|
||||
bbInLoopCondition(bb)
|
||||
}
|
||||
|
||||
predicate bbNotInLoop(BasicBlock bb) {
|
||||
not this.bbInLoop(bb) and
|
||||
bb.getEnclosingFunction() = this.getEnclosingFunction()
|
||||
}
|
||||
/** Holds if `sbb` is inside this loop. */
|
||||
predicate sbbInLoop(SubBasicBlock sbb) { this.bbInLoop(sbb.getBasicBlock()) }
|
||||
|
||||
/**
|
||||
* Holds if `bb` is a basic block inside this loop where `v` has not been
|
||||
@@ -537,22 +556,19 @@ module FlowVar_internal {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if some loop always assigns to `v` before leaving through an edge
|
||||
* from `bbInside` in its condition to `bbOutside` outside the loop, where
|
||||
* (`sbbDef`, `v`) is a `BlockVar` defined outside the loop. Also, `v` must
|
||||
* be used outside the loop.
|
||||
* Holds if `loop` always assigns to `v` before leaving through an edge
|
||||
* from `bbInside` in its condition to `bbOutside` outside the loop. Also,
|
||||
* `v` must be used outside the loop.
|
||||
*/
|
||||
predicate skipLoop(
|
||||
SubBasicBlock sbbInside, SubBasicBlock sbbOutside, SubBasicBlock sbbDef, Variable v
|
||||
SubBasicBlock sbbInside, SubBasicBlock sbbOutside, Variable v, AlwaysTrueUponEntryLoop loop
|
||||
) {
|
||||
exists(AlwaysTrueUponEntryLoop loop, BasicBlock bbInside, BasicBlock bbOutside |
|
||||
exists(BasicBlock bbInside, BasicBlock bbOutside |
|
||||
loop.alwaysAssignsBeforeLeavingCondition(bbInside, bbOutside, v) and
|
||||
bbInside = sbbInside.getBasicBlock() and
|
||||
bbOutside = sbbOutside.getBasicBlock() and
|
||||
sbbInside.lastInBB() and
|
||||
sbbOutside.firstInBB() and
|
||||
loop.bbNotInLoop(sbbDef.getBasicBlock()) and
|
||||
exists(TBlockVar(sbbDef, v))
|
||||
sbbOutside.firstInBB()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -571,7 +587,7 @@ module FlowVar_internal {
|
||||
start = TBlockVar(sbbDef, v) and
|
||||
result = mid.getASuccessor() and
|
||||
variableLiveInSBB(result, v) and
|
||||
not skipLoop(mid, result, sbbDef, v) and
|
||||
forall(AlwaysTrueUponEntryLoop loop | skipLoop(mid, result, v, loop) | loop.sbbInLoop(sbbDef)) and
|
||||
not assignmentLikeOperation(result, v, _, _)
|
||||
)
|
||||
}
|
||||
@@ -593,12 +609,23 @@ module FlowVar_internal {
|
||||
private predicate variableLiveInSBB(SubBasicBlock sbb, Variable v) {
|
||||
variableAccessInSBB(v, sbb, _)
|
||||
or
|
||||
// Non-const reference parameters are live at the end of the function
|
||||
parameterIsNonConstReference(v) and
|
||||
sbb.contains(v.(Parameter).getFunction())
|
||||
or
|
||||
exists(SubBasicBlock succ | succ = sbb.getASuccessor() |
|
||||
variableLiveInSBB(succ, v) and
|
||||
not variableNotLiveBefore(succ, v)
|
||||
)
|
||||
}
|
||||
|
||||
predicate parameterIsNonConstReference(Parameter p) {
|
||||
exists(ReferenceType refType |
|
||||
refType = p.getUnderlyingType() and
|
||||
not refType.getBaseType().isConst()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if liveness of `v` should stop propagating backwards from `sbb`.
|
||||
*/
|
||||
@@ -679,10 +706,11 @@ module FlowVar_internal {
|
||||
predicate dominatedByOverwrite(UninitializedLocalVariable v, VariableAccess va) {
|
||||
exists(BasicBlock bb, int vaIndex |
|
||||
va = bb.getNode(vaIndex) and
|
||||
va.getTarget() = v
|
||||
|
|
||||
va.getTarget() = v and
|
||||
vaIndex > indexOfFirstOverwriteInBB(v, bb)
|
||||
or
|
||||
va = bb.getNode(vaIndex) and
|
||||
va.getTarget() = v and
|
||||
bbStrictlyDominates(getAnOverwritingBB(v), bb)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -122,11 +122,11 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
outModel.isParameterDeref(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
||||
// Taint flows from a pointer to a dereference, which DataFlow does not handle
|
||||
// memcpy(&dest_var, tainted_ptr, len)
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
inModel.isParameterDeref(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
@@ -134,15 +134,15 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(TaintFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
outModel.isParameterDeref(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel | f.hasTaintFlow(inModel, outModel) |
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
inModel.isParameterDeref(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
or
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
inModel.isParameterDeref(argInIndex) and
|
||||
call.passesByReference(argInIndex, exprIn)
|
||||
or
|
||||
inModel.isInParameter(argInIndex) and
|
||||
inModel.isParameter(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -18,7 +18,18 @@ abstract class Access extends Expr, NameQualifiableElement {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ enum constant access expression.
|
||||
* A C/C++ `enum` constant access expression. For example the access to
|
||||
* `MYENUMCONST1` in `myFunction` in the following code:
|
||||
* ```
|
||||
* enum MyEnum {
|
||||
* MYENUMCONST1,
|
||||
* MYENUMCONST2
|
||||
* };
|
||||
*
|
||||
* void myFunction() {
|
||||
* MyEnum v = MYENUMCONST1;
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class EnumConstantAccess extends Access, @varaccess {
|
||||
override string getCanonicalQLClass() { result = "EnumConstantAccess" }
|
||||
@@ -27,15 +38,23 @@ class EnumConstantAccess extends Access, @varaccess {
|
||||
exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c)))
|
||||
}
|
||||
|
||||
/** Gets the accessed enum constant. */
|
||||
/** Gets the accessed `enum` constant. */
|
||||
override EnumConstant getTarget() { varbind(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/** Gets a textual representation of this enum constant access. */
|
||||
/** Gets a textual representation of this `enum` constant access. */
|
||||
override string toString() { result = this.getTarget().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ variable access expression.
|
||||
* A C/C++ variable access expression. For example the accesses to
|
||||
* `x` and `y` in `myFunction` in the following code:
|
||||
* ```
|
||||
* int x;
|
||||
*
|
||||
* void myFunction(int y) {
|
||||
* x = y;
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class VariableAccess extends Access, @varaccess {
|
||||
override string getCanonicalQLClass() { result = "VariableAccess" }
|
||||
@@ -129,7 +148,18 @@ class VariableAccess extends Access, @varaccess {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ field access expression.
|
||||
* A C/C++ field access expression. For example the accesses to
|
||||
* `x` and `y` in `myMethod` in the following code:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void myMethod(MyClass &other) {
|
||||
* x = other.y;
|
||||
* }
|
||||
*
|
||||
* int x, y;
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class FieldAccess extends VariableAccess {
|
||||
override string getCanonicalQLClass() { result = "FieldAccess" }
|
||||
@@ -141,8 +171,23 @@ class FieldAccess extends VariableAccess {
|
||||
}
|
||||
|
||||
/**
|
||||
* A field access of the form `obj->field`. The type of `obj` is a pointer,
|
||||
* so this is equivalent to `(*obj).field`.
|
||||
* A field access whose qualifier is a pointer to a class, struct or union.
|
||||
* These typically take the form `obj->field`. Another case is a field access
|
||||
* with an implicit `this->` qualifier, which is often a `PointerFieldAccess`
|
||||
* (but see also `ImplicitThisFieldAccess`).
|
||||
*
|
||||
* For example the accesses to `x` and `y` in `myMethod` in the following code
|
||||
* are each a `PointerFieldAccess`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void myMethod(MyClass *other) {
|
||||
* other->x = y;
|
||||
* }
|
||||
*
|
||||
* int x, y;
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class PointerFieldAccess extends FieldAccess {
|
||||
override string getCanonicalQLClass() { result = "PointerFieldAccess" }
|
||||
@@ -169,7 +214,18 @@ class DotFieldAccess extends FieldAccess {
|
||||
|
||||
/**
|
||||
* A field access of the form `obj.field`, where the type of `obj` is a
|
||||
* reference to a class/struct/union.
|
||||
* reference to a class/struct/union. For example the accesses to `y` in
|
||||
* `myMethod` in the following code:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void myMethod(MyClass a, MyClass &b) {
|
||||
* a.x = b.y;
|
||||
* }
|
||||
*
|
||||
* int x, y;
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ReferenceFieldAccess extends DotFieldAccess {
|
||||
override string getCanonicalQLClass() { result = "ReferenceFieldAccess" }
|
||||
@@ -179,7 +235,18 @@ class ReferenceFieldAccess extends DotFieldAccess {
|
||||
|
||||
/**
|
||||
* A field access of the form `obj.field`, where the type of `obj` is a
|
||||
* class/struct/union (and not a reference).
|
||||
* class/struct/union (and not a reference). For example the accesses to `x`
|
||||
* in `myMethod` in the following code:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void myMethod(MyClass a, MyClass &b) {
|
||||
* a.x = b.y;
|
||||
* }
|
||||
*
|
||||
* int x, y;
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ValueFieldAccess extends DotFieldAccess {
|
||||
override string getCanonicalQLClass() { result = "ValueFieldAccess" }
|
||||
@@ -198,25 +265,40 @@ private predicate referenceConversion(Conversion c) {
|
||||
/**
|
||||
* Holds if `e` is a reference expression (that is, it has a type of the
|
||||
* form `T&`), which is converted to a value. For example:
|
||||
*
|
||||
* ```
|
||||
* int myfcn(MyStruct &x) {
|
||||
* return x.field;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this example, the type of `x` is `MyStruct&`, but it gets implicitly
|
||||
* converted to `MyStruct` in the expression `x.field`.
|
||||
*/
|
||||
private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.getConversion+()) }
|
||||
|
||||
/**
|
||||
* A field access of a field of `this`. The access has no qualifier because
|
||||
* the use of `this` is implicit. For example, `field` is equivalent to
|
||||
* `this->field` if `field` is a member of `this`.
|
||||
* A field access of a field of `this` which has no qualifier because
|
||||
* the use of `this` is implicit. For example, in the following code the
|
||||
* implicit call to the destructor of `A` has no qualifier because the
|
||||
* use of `this` is implicit:
|
||||
* ```
|
||||
* class A {
|
||||
* public:
|
||||
* ~A() {
|
||||
* // ...
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* class B {
|
||||
* public:
|
||||
* A a;
|
||||
*
|
||||
* ~B() {
|
||||
* // Implicit call to the destructor of `A`.
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
* Note: the C++ front-end often automatically desugars `field` to
|
||||
* `this->field`, so most implicit accesses of `this->field` are instances
|
||||
* `this->field`, so most accesses of `this->field` are instances
|
||||
* of `PointerFieldAccess` (with `ThisExpr` as the qualifier), not
|
||||
* `ImplicitThisFieldAccess`.
|
||||
*/
|
||||
@@ -250,7 +332,15 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ function access expression.
|
||||
* A C/C++ function access expression. For example the access to
|
||||
* `myFunctionTarget` in `myFunction` in the following code:
|
||||
* ```
|
||||
* int myFunctionTarget(int);
|
||||
*
|
||||
* void myFunction() {
|
||||
* int (*myFunctionPointer)(int) = &myTarget;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class FunctionAccess extends Access, @routineexpr {
|
||||
FunctionAccess() { not iscall(underlyingElement(this), _) }
|
||||
@@ -269,7 +359,7 @@ class FunctionAccess extends Access, @routineexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a parameter of a function signature for the purposes of a decltype.
|
||||
* An access to a parameter of a function signature for the purposes of a `decltype`.
|
||||
*
|
||||
* For example, given the following code:
|
||||
* ```
|
||||
@@ -279,7 +369,7 @@ class FunctionAccess extends Access, @routineexpr {
|
||||
* }
|
||||
* ```
|
||||
* The return type of the function is a decltype, the expression of which contains
|
||||
* an add expression, which in turn has two ParamAccessForType children.
|
||||
* an add expression, which in turn has two `ParamAccessForType` children.
|
||||
*/
|
||||
class ParamAccessForType extends Expr, @param_ref {
|
||||
override string toString() { result = "param access" }
|
||||
@@ -287,7 +377,22 @@ class ParamAccessForType extends Expr, @param_ref {
|
||||
|
||||
/**
|
||||
* An access to a type. This occurs in certain contexts where a built-in
|
||||
* works on types directly rather than variables, expressions etc.
|
||||
* works on types directly rather than variables, expressions etc. For
|
||||
* example the reference to `MyClass` in `__is_pod` in the following code:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
* void myFunction() {
|
||||
* if (__is_pod(MyClass))
|
||||
* {
|
||||
* ...
|
||||
* } else {
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class TypeName extends Expr, @type_operand {
|
||||
override string getCanonicalQLClass() { result = "TypeName" }
|
||||
@@ -296,9 +401,17 @@ class TypeName extends Expr, @type_operand {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ array access expression.
|
||||
* A C/C++ array access expression. For example, the access to `as` in
|
||||
* `myFunction` in the following code:
|
||||
* ```
|
||||
* int as[10];
|
||||
*
|
||||
* For calls to operator[], which look syntactically identical, see OverloadedArrayExpr.
|
||||
* void myFunction() {
|
||||
* as[0]++;
|
||||
* }
|
||||
* ```
|
||||
* For calls to `operator[]`, which look syntactically identical, see
|
||||
* `OverloadedArrayExpr`.
|
||||
*/
|
||||
class ArrayExpr extends Expr, @subscriptexpr {
|
||||
override string getCanonicalQLClass() { result = "ArrayExpr" }
|
||||
@@ -306,14 +419,14 @@ class ArrayExpr extends Expr, @subscriptexpr {
|
||||
/**
|
||||
* Gets the array or pointer expression being subscripted.
|
||||
*
|
||||
* This is arr in both arr[0] and 0[arr].
|
||||
* This is `arr` in both `arr[0]` and `0[arr]`.
|
||||
*/
|
||||
Expr getArrayBase() { result = this.getChild(0) }
|
||||
|
||||
/**
|
||||
* Gets the expression giving the index into the array.
|
||||
*
|
||||
* This is 0 in both arr[0] and 0[arr].
|
||||
* This is `0` in both `arr[0]` and `0[arr]`.
|
||||
*/
|
||||
Expr getArrayOffset() { result = this.getChild(1) }
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
|
||||
*/
|
||||
class ConjugationExpr extends UnaryArithmeticOperation, @conjugation {
|
||||
override string getOperator() { result = "~" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConjugationExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,6 +144,8 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post
|
||||
*/
|
||||
class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
|
||||
override string getOperator() { result = "__real" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "RealPartExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,6 +153,8 @@ class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
|
||||
*/
|
||||
class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr {
|
||||
override string getOperator() { result = "__imag" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryPartExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,6 +223,8 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr {
|
||||
class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
||||
override string getOperator() { result = "*" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryMulExpr" }
|
||||
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
@@ -226,6 +234,8 @@ class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
||||
class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
||||
override string getOperator() { result = "/" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryDivExpr" }
|
||||
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
@@ -235,6 +245,8 @@ class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
||||
class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
||||
override string getOperator() { result = "+" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "RealImaginaryAddExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
}
|
||||
|
||||
@@ -244,6 +256,8 @@ class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
||||
class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
||||
override string getOperator() { result = "+" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryRealAddExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
}
|
||||
|
||||
@@ -253,6 +267,8 @@ class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
||||
class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "RealImaginarySubExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
}
|
||||
|
||||
@@ -262,6 +278,8 @@ class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
||||
class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryRealSubExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
}
|
||||
|
||||
@@ -270,6 +288,8 @@ class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
||||
*/
|
||||
class MinExpr extends BinaryArithmeticOperation, @minexpr {
|
||||
override string getOperator() { result = "<?" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "MinExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,6 +297,8 @@ class MinExpr extends BinaryArithmeticOperation, @minexpr {
|
||||
*/
|
||||
class MaxExpr extends BinaryArithmeticOperation, @maxexpr {
|
||||
override string getOperator() { result = ">?" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "MaxExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -519,3 +519,18 @@ class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr {
|
||||
class VectorFillOperation extends UnaryOperation, @vec_fill {
|
||||
override string getOperator() { result = "(vector fill)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The GNU `__builtin_complex` operation.
|
||||
*/
|
||||
class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex {
|
||||
override string toString() { result = "__builtin_complex" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "BuiltInComplexOperation" }
|
||||
|
||||
/** Gets the operand corresponding to the real part of the complex number. */
|
||||
Expr getRealOperand() { this.hasChild(result, 0) }
|
||||
|
||||
/** Gets the operand corresponding to the imaginary part of the complex number. */
|
||||
Expr getImaginaryOperand() { this.hasChild(result, 1) }
|
||||
}
|
||||
|
||||
@@ -131,6 +131,8 @@ class Expr extends StmtParent, @expr {
|
||||
valuebind(_, underlyingElement(this))
|
||||
or
|
||||
addressConstantExpression(this)
|
||||
or
|
||||
constantTemplateLiteral(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1119,3 +1121,17 @@ private predicate isStandardPlacementNewAllocator(Function operatorNew) {
|
||||
|
||||
// Pulled out for performance. See QL-796.
|
||||
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
|
||||
|
||||
/**
|
||||
* Holds if `e` is a literal of unknown value in a template, or a cast thereof.
|
||||
* We assume that such literals are constant.
|
||||
*/
|
||||
private predicate constantTemplateLiteral(Expr e) {
|
||||
// Unknown literals in uninstantiated templates could be enum constant
|
||||
// accesses or pointer-to-member literals.
|
||||
e instanceof Literal and
|
||||
e.isFromUninstantiatedTemplate(_) and
|
||||
not exists(e.getValue())
|
||||
or
|
||||
constantTemplateLiteral(e.(Cast).getExpr())
|
||||
}
|
||||
|
||||
@@ -132,7 +132,6 @@ class HexLiteral extends Literal {
|
||||
* A C/C++ aggregate literal.
|
||||
*/
|
||||
class AggregateLiteral extends Expr, @aggregateliteral {
|
||||
// if this is turned into a Literal we need to change mayBeImpure
|
||||
override string getCanonicalQLClass() { result = "AggregateLiteral" }
|
||||
|
||||
/**
|
||||
@@ -145,6 +144,10 @@ class AggregateLiteral extends Expr, @aggregateliteral {
|
||||
result = this.(ClassAggregateLiteral).getFieldExpr(f)
|
||||
}
|
||||
|
||||
override predicate mayBeImpure() { this.getAChild().mayBeImpure() }
|
||||
|
||||
override predicate mayBeGloballyImpure() { this.getAChild().mayBeGloballyImpure() }
|
||||
|
||||
/** Gets a textual representation of this aggregate literal. */
|
||||
override string toString() { result = "{...}" }
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ private predicate constantAddressLValue(Expr lvalue) {
|
||||
// tells us how it's going to be used.
|
||||
lvalue.(FunctionAccess).getType() instanceof RoutineType
|
||||
or
|
||||
// Pointer-to-member literals in uninstantiated templates
|
||||
lvalue instanceof Literal and
|
||||
not exists(lvalue.getValue()) and
|
||||
lvalue.isFromUninstantiatedTemplate(_)
|
||||
or
|
||||
// String literals have array types and undergo array-to-pointer conversion.
|
||||
lvalue instanceof StringLiteral
|
||||
or
|
||||
@@ -61,6 +66,10 @@ private predicate constantAddressPointer(Expr pointer) {
|
||||
// tells us how it's going to be used.
|
||||
pointer.(FunctionAccess).getType() instanceof FunctionPointerType
|
||||
or
|
||||
// Pointer to member function. These accesses are always pointers even though
|
||||
// their type is `RoutineType`.
|
||||
pointer.(FunctionAccess).getTarget() instanceof MemberFunction
|
||||
or
|
||||
addressConstantVariable(pointer.(VariableAccess).getTarget()) and
|
||||
pointer.getType().getUnderlyingType() instanceof PointerType
|
||||
or
|
||||
|
||||
@@ -4,25 +4,17 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* A predictable expression is one where an external user can predict
|
||||
* A predictable instruction is one where an external user can predict
|
||||
* the value. For example, a literal in the source code is considered
|
||||
* predictable.
|
||||
*/
|
||||
// TODO: Change to use Instruction instead of Expr. Naive attempt breaks
|
||||
// TaintedAllocationSize qltest.
|
||||
private predicate predictable(Expr expr) {
|
||||
expr instanceof Literal
|
||||
or
|
||||
exists(BinaryOperation binop | binop = expr |
|
||||
predictable(binop.getLeftOperand()) and predictable(binop.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(UnaryOperation unop | unop = expr | predictable(unop.getOperand()))
|
||||
}
|
||||
|
||||
// TODO: remove when `predictable` has an `Instruction` parameter instead of `Expr`.
|
||||
private predicate predictableInstruction(Instruction instr) {
|
||||
predictable(DataFlow::instructionNode(instr).asExpr())
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
// This could be a conversion on a string literal
|
||||
predictableInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate localFlowBigStep(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
||||
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
}
|
||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = f.toString() + concat(" : " + ppReprType(t))
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
override string toString() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate localFlowBigStep(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
||||
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
}
|
||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = f.toString() + concat(" : " + ppReprType(t))
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
override string toString() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate localFlowBigStep(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
||||
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
}
|
||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = f.toString() + concat(" : " + ppReprType(t))
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
override string toString() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate localFlowBigStep(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||
) {
|
||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
||||
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
}
|
||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
* tracked object. The final type indicates the type of the tracked object.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = f.toString() + concat(" : " + ppReprType(t))
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
override string toString() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -204,3 +204,5 @@ class DataFlowCall extends CallInstruction {
|
||||
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
@@ -66,11 +66,14 @@ private newtype TOpcode =
|
||||
TCallSideEffect() or
|
||||
TCallReadSideEffect() or
|
||||
TIndirectReadSideEffect() or
|
||||
TIndirectWriteSideEffect() or
|
||||
TIndirectMustWriteSideEffect() or
|
||||
TIndirectMayWriteSideEffect() or
|
||||
TBufferReadSideEffect() or
|
||||
TBufferWriteSideEffect() or
|
||||
TBufferMustWriteSideEffect() or
|
||||
TBufferMayWriteSideEffect() or
|
||||
TSizedBufferReadSideEffect() or
|
||||
TSizedBufferMustWriteSideEffect() or
|
||||
TSizedBufferMayWriteSideEffect() or
|
||||
TChi() or
|
||||
TInlineAsm() or
|
||||
TUnreached() or
|
||||
@@ -135,17 +138,28 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { }
|
||||
*/
|
||||
abstract class WriteSideEffectOpcode extends SideEffectOpcode { }
|
||||
|
||||
/**
|
||||
* An opcode that definitely writes to a set of memory locations as a side effect.
|
||||
*/
|
||||
abstract class MustWriteSideEffectOpcode extends WriteSideEffectOpcode { }
|
||||
|
||||
/**
|
||||
* An opcode that may overwrite some, all, or none of an existing set of memory locations. Modeled
|
||||
* as a read of the original contents, plus a "may" write of the new contents.
|
||||
*/
|
||||
abstract class MayWriteSideEffectOpcode extends SideEffectOpcode { }
|
||||
abstract class MayWriteSideEffectOpcode extends WriteSideEffectOpcode { }
|
||||
|
||||
/**
|
||||
* An opcode that accesses a buffer via an `AddressOperand` and a `BufferSizeOperand`.
|
||||
* An opcode that accesses a buffer via an `AddressOperand`.
|
||||
*/
|
||||
abstract class BufferAccessOpcode extends MemoryAccessOpcode { }
|
||||
|
||||
/**
|
||||
* An opcode that accesses a buffer via an `AddressOperand` with a `BufferSizeOperand` specifying
|
||||
* the number of elements accessed.
|
||||
*/
|
||||
abstract class SizedBufferAccessOpcode extends BufferAccessOpcode { }
|
||||
|
||||
module Opcode {
|
||||
class NoOp extends Opcode, TNoOp {
|
||||
final override string toString() { result = "NoOp" }
|
||||
@@ -416,9 +430,9 @@ module Opcode {
|
||||
final override string toString() { result = "IndirectReadSideEffect" }
|
||||
}
|
||||
|
||||
class IndirectWriteSideEffect extends WriteSideEffectOpcode, MemoryAccessOpcode,
|
||||
TIndirectWriteSideEffect {
|
||||
final override string toString() { result = "IndirectWriteSideEffect" }
|
||||
class IndirectMustWriteSideEffect extends MustWriteSideEffectOpcode, MemoryAccessOpcode,
|
||||
TIndirectMustWriteSideEffect {
|
||||
final override string toString() { result = "IndirectMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode,
|
||||
@@ -430,9 +444,9 @@ module Opcode {
|
||||
final override string toString() { result = "BufferReadSideEffect" }
|
||||
}
|
||||
|
||||
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode,
|
||||
TBufferWriteSideEffect {
|
||||
final override string toString() { result = "BufferWriteSideEffect" }
|
||||
class BufferMustWriteSideEffect extends MustWriteSideEffectOpcode, BufferAccessOpcode,
|
||||
TBufferMustWriteSideEffect {
|
||||
final override string toString() { result = "BufferMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode,
|
||||
@@ -440,6 +454,21 @@ module Opcode {
|
||||
final override string toString() { result = "BufferMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferAccessOpcode,
|
||||
TSizedBufferReadSideEffect {
|
||||
final override string toString() { result = "SizedBufferReadSideEffect" }
|
||||
}
|
||||
|
||||
class SizedBufferMustWriteSideEffect extends MustWriteSideEffectOpcode, SizedBufferAccessOpcode,
|
||||
TSizedBufferMustWriteSideEffect {
|
||||
final override string toString() { result = "SizedBufferMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
class SizedBufferMayWriteSideEffect extends MayWriteSideEffectOpcode, SizedBufferAccessOpcode,
|
||||
TSizedBufferMayWriteSideEffect {
|
||||
final override string toString() { result = "SizedBufferMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
class Chi extends Opcode, TChi {
|
||||
final override string toString() { result = "Chi" }
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ module InstructionSanity {
|
||||
or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||
or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
|
||||
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||
or
|
||||
@@ -48,8 +48,8 @@ module InstructionSanity {
|
||||
or
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof MayWriteSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -120,6 +120,15 @@ module InstructionSanity {
|
||||
)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
@@ -609,9 +618,14 @@ class VariableInstruction extends Instruction {
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
|
||||
final override string getImmediateString() { result = var.toString() }
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
final IRVariable getVariable() { result = var }
|
||||
final IRVariable getIRVariable() { result = var }
|
||||
|
||||
/**
|
||||
* Gets the AST variable that this instruction's IR variable refers to, if one exists.
|
||||
*/
|
||||
final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
|
||||
}
|
||||
|
||||
class FieldInstruction extends Instruction {
|
||||
@@ -644,6 +658,16 @@ class ConstantValueInstruction extends Instruction {
|
||||
final string getValue() { result = value }
|
||||
}
|
||||
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
final int getIndex() { result = index }
|
||||
}
|
||||
|
||||
class EnterFunctionInstruction extends Instruction {
|
||||
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
||||
}
|
||||
@@ -1175,6 +1199,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
|
||||
*/
|
||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1182,13 +1208,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
*/
|
||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the read of an indirect buffer parameter within a function call.
|
||||
*/
|
||||
class SizedBufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
SizedBufferReadSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::SizedBufferReadSideEffect
|
||||
}
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a side effect of a function call.
|
||||
*/
|
||||
class WriteSideEffectInstruction extends SideEffectInstruction {
|
||||
WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect parameter within a function call.
|
||||
*/
|
||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
|
||||
class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
@@ -1197,18 +1249,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
|
||||
class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
SizedBufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the potential write of an indirect parameter within a function call.
|
||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||
* written.
|
||||
*/
|
||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
@@ -1222,7 +1290,7 @@ class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||
*/
|
||||
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
@@ -1230,6 +1298,22 @@ class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||
*/
|
||||
class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
SizedBufferMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a GNU or MSVC inline assembly statement.
|
||||
*/
|
||||
|
||||
@@ -254,6 +254,16 @@ class AddressOperand extends RegisterOperand {
|
||||
override string toString() { result = "Address" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperand extends RegisterOperand {
|
||||
override BufferSizeOperandTag tag;
|
||||
|
||||
override string toString() { result = "BufferSize" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
||||
* `ReturnValue`, `ThrowValue`).
|
||||
@@ -390,10 +400,10 @@ class SideEffectOperand extends TypedOperand {
|
||||
useInstr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectWriteSideEffectInstruction and
|
||||
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferWriteSideEffectInstruction and
|
||||
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
|
||||
@@ -135,14 +135,14 @@ private predicate variableAddressValueNumber(
|
||||
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
instr.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
instr.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
||||
|
||||
@@ -282,7 +282,7 @@ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
|
||||
// The variable's address escapes if the result of any
|
||||
// VariableAddressInstruction that computes the variable's address escapes.
|
||||
exists(VariableAddressInstruction instr |
|
||||
instr.getVariable() = var and
|
||||
instr.getIRVariable() = var and
|
||||
resultEscapesNonReturn(instr)
|
||||
)
|
||||
}
|
||||
@@ -305,7 +305,7 @@ predicate variableAddressEscapes(IRVariable var) {
|
||||
*/
|
||||
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
|
||||
// The address of a variable points to that variable, at offset 0.
|
||||
instr.(VariableAddressInstruction).getVariable() = var and
|
||||
instr.(VariableAddressInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
or
|
||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||
|
||||
@@ -334,7 +334,7 @@ private module Cached {
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result = getNewIRVariable(getOldInstruction(instruction)
|
||||
.(OldIR::VariableInstruction)
|
||||
.getVariable())
|
||||
.getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -342,6 +342,11 @@ private module Cached {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
|
||||
@@ -66,12 +66,14 @@ AddressOperandTag addressOperand() { result = TAddressOperand() }
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperand extends RegisterOperandTag, TBufferSizeOperand {
|
||||
class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand {
|
||||
final override string toString() { result = "BufferSize" }
|
||||
|
||||
final override int getSortOrder() { result = 1 }
|
||||
}
|
||||
|
||||
BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() }
|
||||
|
||||
/**
|
||||
* The operand representing the read side effect of a `SideEffectInstruction`.
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,7 @@ module InstructionSanity {
|
||||
or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||
or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
|
||||
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||
or
|
||||
@@ -48,8 +48,8 @@ module InstructionSanity {
|
||||
or
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof MayWriteSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -120,6 +120,15 @@ module InstructionSanity {
|
||||
)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
@@ -609,9 +618,14 @@ class VariableInstruction extends Instruction {
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
|
||||
final override string getImmediateString() { result = var.toString() }
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
final IRVariable getVariable() { result = var }
|
||||
final IRVariable getIRVariable() { result = var }
|
||||
|
||||
/**
|
||||
* Gets the AST variable that this instruction's IR variable refers to, if one exists.
|
||||
*/
|
||||
final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
|
||||
}
|
||||
|
||||
class FieldInstruction extends Instruction {
|
||||
@@ -644,6 +658,16 @@ class ConstantValueInstruction extends Instruction {
|
||||
final string getValue() { result = value }
|
||||
}
|
||||
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
final int getIndex() { result = index }
|
||||
}
|
||||
|
||||
class EnterFunctionInstruction extends Instruction {
|
||||
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
||||
}
|
||||
@@ -1175,6 +1199,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
|
||||
*/
|
||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1182,13 +1208,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
*/
|
||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the read of an indirect buffer parameter within a function call.
|
||||
*/
|
||||
class SizedBufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
SizedBufferReadSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::SizedBufferReadSideEffect
|
||||
}
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a side effect of a function call.
|
||||
*/
|
||||
class WriteSideEffectInstruction extends SideEffectInstruction {
|
||||
WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect parameter within a function call.
|
||||
*/
|
||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
|
||||
class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
@@ -1197,18 +1249,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
|
||||
class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
SizedBufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the potential write of an indirect parameter within a function call.
|
||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||
* written.
|
||||
*/
|
||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
@@ -1222,7 +1290,7 @@ class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||
*/
|
||||
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
@@ -1230,6 +1298,22 @@ class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||
*/
|
||||
class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
SizedBufferMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a GNU or MSVC inline assembly statement.
|
||||
*/
|
||||
|
||||
@@ -254,6 +254,16 @@ class AddressOperand extends RegisterOperand {
|
||||
override string toString() { result = "Address" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperand extends RegisterOperand {
|
||||
override BufferSizeOperandTag tag;
|
||||
|
||||
override string toString() { result = "BufferSize" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
||||
* `ReturnValue`, `ThrowValue`).
|
||||
@@ -390,10 +400,10 @@ class SideEffectOperand extends TypedOperand {
|
||||
useInstr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectWriteSideEffectInstruction and
|
||||
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferWriteSideEffectInstruction and
|
||||
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
|
||||
@@ -135,14 +135,14 @@ private predicate variableAddressValueNumber(
|
||||
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
instr.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
instr.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
||||
|
||||
@@ -259,6 +259,14 @@ private module Cached {
|
||||
.getInstructionConstantValue(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionIndex(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction)
|
||||
|
||||
@@ -5,6 +5,7 @@ private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedFunction
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function. The call may be from an actual
|
||||
@@ -22,6 +23,8 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
id = -1 and result = getCallTarget()
|
||||
or
|
||||
result = getArgument(id)
|
||||
or
|
||||
id = getNumberOfArguments() and result = getSideEffects()
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
@@ -66,6 +69,9 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
then result = getArgument(argIndex + 1).getFirstInstruction()
|
||||
else result = getInstruction(CallTag())
|
||||
)
|
||||
or
|
||||
child = getSideEffects() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -75,12 +81,19 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
tag = CallTag() and
|
||||
if hasSideEffect()
|
||||
then result = getInstruction(CallSideEffectTag())
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
else
|
||||
if hasPreciseSideEffect()
|
||||
then result = getSideEffects().getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
or
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
(
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
if hasPreciseSideEffect()
|
||||
then result = getSideEffects().getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -165,6 +178,8 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
*/
|
||||
abstract TranslatedExpr getArgument(int index);
|
||||
|
||||
abstract int getNumberOfArguments();
|
||||
|
||||
/**
|
||||
* If there are any arguments, gets the first instruction of the first
|
||||
* argument. Otherwise, returns the call instruction.
|
||||
@@ -191,6 +206,10 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
tag = CallSideEffectTag() and
|
||||
result = getResult()
|
||||
}
|
||||
|
||||
predicate hasPreciseSideEffect() { exists(getSideEffects()) }
|
||||
|
||||
TranslatedSideEffects getSideEffects() { result.getCall() = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,6 +264,8 @@ abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, TranslatedC
|
||||
final override TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(expr.getArgument(index).getFullyConverted())
|
||||
}
|
||||
|
||||
final override int getNumberOfArguments() { result = expr.getNumberOfArguments() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,11 +290,11 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
}
|
||||
|
||||
override predicate hasReadSideEffect() {
|
||||
not expr.getTarget().(SideEffectFunction).neverReadsMemory()
|
||||
not expr.getTarget().(SideEffectFunction).hasOnlySpecificReadSideEffects()
|
||||
}
|
||||
|
||||
override predicate hasWriteSideEffect() {
|
||||
not expr.getTarget().(SideEffectFunction).neverWritesMemory()
|
||||
not expr.getTarget().(SideEffectFunction).hasOnlySpecificWriteSideEffects()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,3 +316,229 @@ class TranslatedStructorCall extends TranslatedFunctionCall {
|
||||
|
||||
override predicate hasQualifier() { any() }
|
||||
}
|
||||
|
||||
class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
|
||||
Call expr;
|
||||
|
||||
TranslatedSideEffects() { this = TTranslatedSideEffects(expr) }
|
||||
|
||||
override string toString() { result = "(side effects for " + expr.toString() + ")" }
|
||||
|
||||
override Locatable getAST() { result = expr }
|
||||
|
||||
Call getCall() { result = expr }
|
||||
|
||||
override TranslatedElement getChild(int i) {
|
||||
result = rank[i + 1](TranslatedSideEffect tse, int isWrite, int index |
|
||||
(
|
||||
tse.getCall() = getCall() and
|
||||
tse.getArgumentIndex() = index and
|
||||
if tse.isWrite() then isWrite = 1 else isWrite = 0
|
||||
)
|
||||
|
|
||||
tse order by isWrite, index
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement te) {
|
||||
exists(int i |
|
||||
getChild(i) = te and
|
||||
if exists(getChild(i + 1))
|
||||
then result = getChild(i + 1).getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type t, boolean isGLValue) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() }
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
|
||||
|
||||
override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedFunction` containing this expression.
|
||||
*/
|
||||
final TranslatedFunction getEnclosingFunction() {
|
||||
result = getTranslatedFunction(expr.getEnclosingFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Function` containing this expression.
|
||||
*/
|
||||
override Function getFunction() { result = expr.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEffect {
|
||||
Call call;
|
||||
Expr arg;
|
||||
int index;
|
||||
boolean write;
|
||||
|
||||
TranslatedSideEffect() { this = TTranslatedArgumentSideEffect(call, arg, index, write) }
|
||||
|
||||
override Locatable getAST() { result = arg }
|
||||
|
||||
Expr getExpr() { result = arg }
|
||||
|
||||
Call getCall() { result = call }
|
||||
|
||||
int getArgumentIndex() { result = index }
|
||||
|
||||
predicate isWrite() { write = true }
|
||||
|
||||
override string toString() {
|
||||
write = true and
|
||||
result = "(write side effect for " + arg.toString() + ")"
|
||||
or
|
||||
write = false and
|
||||
result = "(read side effect for " + arg.toString() + ")"
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int n) { none() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type t, boolean isGLValue) {
|
||||
isWrite() and
|
||||
hasSpecificWriteSideEffect(opcode) and
|
||||
tag = OnlyInstructionTag() and
|
||||
(
|
||||
opcode instanceof BufferAccessOpcode and
|
||||
t instanceof UnknownType
|
||||
or
|
||||
not opcode instanceof BufferAccessOpcode and
|
||||
(
|
||||
t = arg.getUnspecifiedType().(DerivedType).getBaseType() and
|
||||
not t instanceof VoidType
|
||||
or
|
||||
arg.getUnspecifiedType().(DerivedType).getBaseType() instanceof VoidType and
|
||||
t instanceof UnknownType
|
||||
)
|
||||
or
|
||||
index = -1 and
|
||||
not arg.getUnspecifiedType() instanceof DerivedType and
|
||||
t = arg.getUnspecifiedType()
|
||||
) and
|
||||
isGLValue = false
|
||||
or
|
||||
not isWrite() and
|
||||
hasSpecificReadSideEffect(opcode) and
|
||||
tag = OnlyInstructionTag() and
|
||||
t instanceof VoidType and
|
||||
isGLValue = false
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
result = getParent().getChildSuccessor(this) and
|
||||
tag = OnlyInstructionTag() and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getTranslatedExpr(arg).getResult()
|
||||
or
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
not isWrite() and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
or
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof BufferSizeOperandTag and
|
||||
result = getTranslatedExpr(call
|
||||
.getArgument(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
|
||||
.getFullyConverted()).getResult()
|
||||
}
|
||||
|
||||
override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag instanceof OnlyInstructionTag and
|
||||
result = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
or
|
||||
tag instanceof OnlyInstructionTag and
|
||||
result = arg.getType().getUnspecifiedType() and
|
||||
not result instanceof DerivedType and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
}
|
||||
|
||||
predicate hasSpecificWriteSideEffect(Opcode op) {
|
||||
exists(boolean buffer, boolean mustWrite |
|
||||
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
|
||||
then
|
||||
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, true, mustWrite) and
|
||||
buffer = true and
|
||||
(
|
||||
mustWrite = false and op instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||
or
|
||||
mustWrite = true and op instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||
)
|
||||
else (
|
||||
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, buffer, mustWrite) and
|
||||
(
|
||||
buffer = true and mustWrite = false and op instanceof Opcode::BufferMayWriteSideEffect
|
||||
or
|
||||
buffer = false and mustWrite = false and op instanceof Opcode::IndirectMayWriteSideEffect
|
||||
or
|
||||
buffer = true and mustWrite = true and op instanceof Opcode::BufferMustWriteSideEffect
|
||||
or
|
||||
buffer = false and mustWrite = true and op instanceof Opcode::IndirectMustWriteSideEffect
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
not call.getTarget() instanceof SideEffectFunction and
|
||||
getArgumentIndex() != -1 and
|
||||
op instanceof Opcode::BufferMayWriteSideEffect
|
||||
or
|
||||
not call.getTarget() instanceof SideEffectFunction and
|
||||
getArgumentIndex() = -1 and
|
||||
op instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
|
||||
predicate hasSpecificReadSideEffect(Opcode op) {
|
||||
exists(boolean buffer |
|
||||
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(index, buffer) and
|
||||
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
|
||||
then buffer = true and op instanceof Opcode::SizedBufferReadSideEffect
|
||||
else (
|
||||
buffer = true and op instanceof Opcode::BufferReadSideEffect
|
||||
or
|
||||
buffer = false and op instanceof Opcode::IndirectReadSideEffect
|
||||
)
|
||||
)
|
||||
or
|
||||
not call.getTarget() instanceof SideEffectFunction and
|
||||
op instanceof Opcode::IndirectReadSideEffect
|
||||
}
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getTranslatedExpr(call).getInstruction(CallTag())
|
||||
}
|
||||
|
||||
final override int getInstructionIndex(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = index
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedFunction` containing this expression.
|
||||
*/
|
||||
final TranslatedFunction getEnclosingFunction() {
|
||||
result = getTranslatedFunction(arg.getEnclosingFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Function` containing this expression.
|
||||
*/
|
||||
override Function getFunction() { result = arg.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ private import TranslatedFunction
|
||||
private import TranslatedStmt
|
||||
private import TranslatedExpr
|
||||
private import IRConstruction
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* Gets the built-in `int` type.
|
||||
@@ -379,7 +380,44 @@ newtype TTranslatedElement =
|
||||
// An allocation size for a `new` or `new[]` expression
|
||||
TTranslatedAllocationSize(NewOrNewArrayExpr newExpr) { not ignoreExpr(newExpr) } or
|
||||
// The declaration/initialization part of a `ConditionDeclExpr`
|
||||
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) }
|
||||
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
|
||||
// The side effects of a `Call` {
|
||||
TTranslatedSideEffects(Call expr) { exists(TTranslatedArgumentSideEffect(expr, _, _, _)) } or // A precise side effect of an argument to a `Call` {
|
||||
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
|
||||
(
|
||||
expr = call.getArgument(n).getFullyConverted()
|
||||
or
|
||||
expr = call.getQualifier().getFullyConverted() and
|
||||
n = -1
|
||||
) and
|
||||
(
|
||||
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(n, _) and
|
||||
isWrite = false
|
||||
or
|
||||
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(n, _, _) and
|
||||
isWrite = true
|
||||
or
|
||||
not call.getTarget() instanceof SideEffectFunction and
|
||||
exists(Type t | t = expr.getUnspecifiedType() |
|
||||
t instanceof ArrayType or
|
||||
t instanceof PointerType or
|
||||
t instanceof ReferenceType
|
||||
) and
|
||||
(
|
||||
isWrite = true or
|
||||
isWrite = false
|
||||
)
|
||||
or
|
||||
not call.getTarget() instanceof SideEffectFunction and
|
||||
n = -1 and
|
||||
(
|
||||
isWrite = true or
|
||||
isWrite = false
|
||||
)
|
||||
) and
|
||||
not ignoreExpr(expr) and
|
||||
not ignoreExpr(call)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the first explicitly initialized element in `initList`
|
||||
@@ -572,6 +610,12 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
*/
|
||||
string getInstructionConstantValue(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is an `IndexedInstruction`, gets the
|
||||
* index for that instruction.
|
||||
*/
|
||||
int getInstructionIndex(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `PointerArithmeticInstruction`,
|
||||
* gets the size of the type pointed to by the pointer.
|
||||
|
||||
@@ -1690,6 +1690,10 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
|
||||
any()
|
||||
}
|
||||
|
||||
final override int getNumberOfArguments() {
|
||||
result = expr.getAllocatorCall().getNumberOfArguments()
|
||||
}
|
||||
|
||||
final override TranslatedExpr getArgument(int index) {
|
||||
// If the allocator is the default operator new(void*), there will be no
|
||||
// allocator call in the AST. Otherwise, there will be an allocator call
|
||||
|
||||
@@ -30,7 +30,7 @@ module InstructionSanity {
|
||||
or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||
or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
|
||||
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||
or
|
||||
@@ -48,8 +48,8 @@ module InstructionSanity {
|
||||
or
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof MayWriteSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -120,6 +120,15 @@ module InstructionSanity {
|
||||
)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
@@ -609,9 +618,14 @@ class VariableInstruction extends Instruction {
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
|
||||
final override string getImmediateString() { result = var.toString() }
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
final IRVariable getVariable() { result = var }
|
||||
final IRVariable getIRVariable() { result = var }
|
||||
|
||||
/**
|
||||
* Gets the AST variable that this instruction's IR variable refers to, if one exists.
|
||||
*/
|
||||
final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
|
||||
}
|
||||
|
||||
class FieldInstruction extends Instruction {
|
||||
@@ -644,6 +658,16 @@ class ConstantValueInstruction extends Instruction {
|
||||
final string getValue() { result = value }
|
||||
}
|
||||
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
final int getIndex() { result = index }
|
||||
}
|
||||
|
||||
class EnterFunctionInstruction extends Instruction {
|
||||
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
||||
}
|
||||
@@ -1175,6 +1199,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
|
||||
*/
|
||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1182,13 +1208,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
*/
|
||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the read of an indirect buffer parameter within a function call.
|
||||
*/
|
||||
class SizedBufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
SizedBufferReadSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::SizedBufferReadSideEffect
|
||||
}
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a side effect of a function call.
|
||||
*/
|
||||
class WriteSideEffectInstruction extends SideEffectInstruction {
|
||||
WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect parameter within a function call.
|
||||
*/
|
||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
|
||||
class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
@@ -1197,18 +1249,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
|
||||
class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
SizedBufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the potential write of an indirect parameter within a function call.
|
||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||
* written.
|
||||
*/
|
||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
@@ -1222,7 +1290,7 @@ class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||
*/
|
||||
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
@@ -1230,6 +1298,22 @@ class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||
*/
|
||||
class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
SizedBufferMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a GNU or MSVC inline assembly statement.
|
||||
*/
|
||||
|
||||
@@ -254,6 +254,16 @@ class AddressOperand extends RegisterOperand {
|
||||
override string toString() { result = "Address" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperand extends RegisterOperand {
|
||||
override BufferSizeOperandTag tag;
|
||||
|
||||
override string toString() { result = "BufferSize" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
||||
* `ReturnValue`, `ThrowValue`).
|
||||
@@ -390,10 +400,10 @@ class SideEffectOperand extends TypedOperand {
|
||||
useInstr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectWriteSideEffectInstruction and
|
||||
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferWriteSideEffectInstruction and
|
||||
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
|
||||
@@ -135,14 +135,14 @@ private predicate variableAddressValueNumber(
|
||||
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
instr.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
instr.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
||||
|
||||
@@ -282,7 +282,7 @@ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
|
||||
// The variable's address escapes if the result of any
|
||||
// VariableAddressInstruction that computes the variable's address escapes.
|
||||
exists(VariableAddressInstruction instr |
|
||||
instr.getVariable() = var and
|
||||
instr.getIRVariable() = var and
|
||||
resultEscapesNonReturn(instr)
|
||||
)
|
||||
}
|
||||
@@ -305,7 +305,7 @@ predicate variableAddressEscapes(IRVariable var) {
|
||||
*/
|
||||
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
|
||||
// The address of a variable points to that variable, at offset 0.
|
||||
instr.(VariableAddressInstruction).getVariable() = var and
|
||||
instr.(VariableAddressInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
or
|
||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||
|
||||
@@ -334,7 +334,7 @@ private module Cached {
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result = getNewIRVariable(getOldInstruction(instruction)
|
||||
.(OldIR::VariableInstruction)
|
||||
.getVariable())
|
||||
.getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -342,6 +342,11 @@ private module Cached {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import implementations.IdentityFunction
|
||||
private import implementations.Inet
|
||||
private import implementations.Memcpy
|
||||
private import implementations.Memset
|
||||
private import implementations.Printf
|
||||
private import implementations.Pure
|
||||
private import implementations.Strcat
|
||||
|
||||
@@ -16,9 +16,9 @@ class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFuncti
|
||||
)
|
||||
}
|
||||
|
||||
override predicate neverReadsMemory() { any() }
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate neverWritesMemory() { any() }
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { none() }
|
||||
|
||||
@@ -34,6 +34,6 @@ class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFuncti
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// These functions simply return the argument value.
|
||||
input.isInParameter(0) and output.isOutReturnValue()
|
||||
input.isParameter(0) and output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ class InetNtoa extends TaintFunction {
|
||||
InetNtoa() { hasGlobalName("inet_ntoa") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameter(0) and
|
||||
output.isOutReturnPointer()
|
||||
input.isParameter(0) and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ class InetAton extends TaintFunction, ArrayFunction {
|
||||
InetAton() { hasGlobalName("inet_aton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameterPointer(0) and
|
||||
output.isOutParameterPointer(1)
|
||||
input.isParameterDeref(0) and
|
||||
output.isParameterDeref(1)
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
@@ -34,8 +34,8 @@ class InetAddr extends TaintFunction, ArrayFunction {
|
||||
InetAddr() { hasGlobalName("inet_addr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameterPointer(0) and
|
||||
output.isOutReturnValue()
|
||||
input.isParameterDeref(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
@@ -47,8 +47,8 @@ class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
InetNetwork() { hasGlobalName("inet_network") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameterPointer(1) and
|
||||
output.isOutReturnValue()
|
||||
input.isParameterDeref(1) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
@@ -61,10 +61,10 @@ class InetMakeaddr extends TaintFunction {
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
input.isInParameter(0) or
|
||||
input.isInParameter(1)
|
||||
input.isParameter(0) or
|
||||
input.isParameter(1)
|
||||
) and
|
||||
output.isOutReturnValue()
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ class InetLnaof extends TaintFunction {
|
||||
InetLnaof() { hasGlobalName("inet_lnaof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameter(0) and
|
||||
output.isOutReturnValue()
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +81,8 @@ class InetNetof extends TaintFunction {
|
||||
InetNetof() { hasGlobalName("inet_netof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameter(0) and
|
||||
output.isOutReturnValue()
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,10 +91,10 @@ class InetPton extends TaintFunction, ArrayFunction {
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
input.isInParameter(0) or
|
||||
input.isInParameterPointer(1)
|
||||
input.isParameter(0) or
|
||||
input.isParameterDeref(1)
|
||||
) and
|
||||
output.isOutParameterPointer(2)
|
||||
output.isParameterDeref(2)
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
|
||||
@@ -110,8 +110,8 @@ class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
Gethostbyname() { hasGlobalName("gethostbyname") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameterPointer(0) and
|
||||
output.isOutReturnPointer()
|
||||
input.isParameterDeref(0) and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
@@ -124,11 +124,11 @@ class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
input.isInParameterPointer(0) or
|
||||
input.isInParameter(1) or
|
||||
input.isInParameter(2)
|
||||
input.isParameterDeref(0) or
|
||||
input.isParameter(1) or
|
||||
input.isParameter(2)
|
||||
) and
|
||||
output.isOutReturnPointer()
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* The standard functions `memcpy` and `memmove`, and the gcc variant
|
||||
* `__builtin___memcpy_chk`
|
||||
*/
|
||||
class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
||||
class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction, TaintFunction {
|
||||
MemcpyFunction() {
|
||||
this.hasName("memcpy") or
|
||||
this.hasName("memmove") or
|
||||
@@ -19,22 +20,22 @@ class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameterPointer(1) and
|
||||
output.isOutParameterPointer(0)
|
||||
input.isParameterDeref(1) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isInParameterPointer(1) and
|
||||
output.isOutReturnPointer()
|
||||
input.isParameterDeref(1) and
|
||||
output.isReturnValueDeref()
|
||||
or
|
||||
input.isInParameter(0) and
|
||||
output.isOutReturnValue()
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameter(2) and
|
||||
output.isOutParameterPointer(0)
|
||||
input.isParameter(2) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isInParameter(2) and
|
||||
output.isOutReturnPointer()
|
||||
input.isParameter(2) and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
@@ -44,4 +45,24 @@ class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
||||
) and
|
||||
countParam = 2
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = true and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 1 and buffer = true
|
||||
}
|
||||
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
|
||||
result = 2 and
|
||||
(
|
||||
i = 0 or
|
||||
i = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
55
cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll
Normal file
55
cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll
Normal file
@@ -0,0 +1,55 @@
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function `memset` and its assorted variants
|
||||
*/
|
||||
class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction, SideEffectFunction {
|
||||
MemsetFunction() {
|
||||
hasGlobalName("memset") or
|
||||
hasGlobalName("wmemset") or
|
||||
hasGlobalName("bzero") or
|
||||
hasGlobalName("__builtin_memset") or
|
||||
hasGlobalName("__builtin_memset_chk") or
|
||||
hasQualifiedName("std", "memset") or
|
||||
hasQualifiedName("std", "wmemset")
|
||||
}
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
bufParam = 0 and
|
||||
(if hasGlobalName("bzero") then countParam = 1 else countParam = 2)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { hasGlobalName("bzero") and index = 0 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) {
|
||||
not hasGlobalName("bzero") and index = 0
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) {
|
||||
not hasGlobalName("bzero") and index = 0
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = true and mustWrite = true
|
||||
}
|
||||
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
|
||||
i = 0 and
|
||||
if hasGlobalName("bzero") then result = 1 else result = 2
|
||||
}
|
||||
}
|
||||
@@ -41,17 +41,17 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(ParameterIndex i |
|
||||
input.isInParameter(i) and
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isInParameterPointer(i) and
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
) and
|
||||
(
|
||||
output.isOutReturnPointer() and
|
||||
output.isReturnValueDeref() and
|
||||
getUnspecifiedType() instanceof PointerType
|
||||
or
|
||||
output.isOutReturnValue()
|
||||
output.isReturnValue()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,9 +67,9 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int i) { none() }
|
||||
|
||||
override predicate neverReadsMemory() { none() }
|
||||
override predicate hasOnlySpecificReadSideEffects() { none() }
|
||||
|
||||
override predicate neverWritesMemory() { any() }
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
@@ -85,13 +85,13 @@ class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(ParameterIndex i |
|
||||
input.isInParameter(i) and
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
) and
|
||||
output.isOutReturnValue()
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate neverReadsMemory() { any() }
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate neverWritesMemory() { any() }
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameter(0) and
|
||||
output.isOutReturnValue()
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -31,19 +31,19 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
|
||||
name = "_mbsncat" or
|
||||
name = "_mbsncat_l"
|
||||
) and
|
||||
input.isInParameter(2) and
|
||||
output.isOutParameterPointer(0)
|
||||
input.isParameter(2) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
name = "_mbsncat_l" and
|
||||
input.isInParameter(3) and
|
||||
output.isOutParameterPointer(0)
|
||||
input.isParameter(3) and
|
||||
output.isParameterDeref(0)
|
||||
)
|
||||
or
|
||||
input.isInParameterPointer(0) and
|
||||
output.isOutParameterPointer(0)
|
||||
input.isParameterDeref(0) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isInParameter(1) and
|
||||
output.isOutParameterPointer(0)
|
||||
input.isParameter(1) and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int param) {
|
||||
|
||||
@@ -55,15 +55,15 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
||||
this.hasName("wcscpy")
|
||||
) and
|
||||
(
|
||||
input.isInParameterPointer(1) and
|
||||
output.isOutParameterPointer(0)
|
||||
input.isParameterDeref(1) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isInParameterPointer(1) and
|
||||
output.isOutReturnPointer()
|
||||
input.isParameterDeref(1) and
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
or
|
||||
input.isInParameter(0) and
|
||||
output.isOutReturnValue()
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -78,12 +78,12 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
||||
this.hasName("_wcsncpy_l")
|
||||
) and
|
||||
(
|
||||
input.isInParameter(2) or
|
||||
input.isInParameterPointer(1)
|
||||
input.isParameter(2) or
|
||||
input.isParameterDeref(1)
|
||||
) and
|
||||
(
|
||||
output.isOutParameterPointer(0) or
|
||||
output.isOutReturnPointer()
|
||||
output.isParameterDeref(0) or
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ class Strftime extends TaintFunction, ArrayFunction {
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
input.isInParameter(1) or
|
||||
input.isInParameterPointer(2) or
|
||||
input.isInParameterPointer(3)
|
||||
input.isParameter(1) or
|
||||
input.isParameterDeref(2) or
|
||||
input.isParameterDeref(3)
|
||||
) and
|
||||
(
|
||||
output.isOutParameterPointer(0) or
|
||||
output.isOutReturnValue()
|
||||
output.isParameterDeref(0) or
|
||||
output.isReturnValue()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ class Swap extends DataFlowFunction {
|
||||
Swap() { this.hasQualifiedName("std", "swap") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isInParameterPointer(0) and
|
||||
output.isOutParameterPointer(1)
|
||||
input.isParameterDeref(0) and
|
||||
output.isParameterDeref(1)
|
||||
or
|
||||
input.isInParameterPointer(1) and
|
||||
output.isOutParameterPointer(0)
|
||||
input.isParameterDeref(1) and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/**
|
||||
* Provides an abstract class for accurate modeling of input and output buffers
|
||||
* in library functions when source code is not available. To use this QL
|
||||
* library, create a QL class extending `BufferFunction` with a characteristic
|
||||
* library, create a QL class extending `ArrayFunction` with a characteristic
|
||||
* predicate that selects the function or set of functions you are trying to
|
||||
* model. Within that class, override the predicates provided by `BufferFunction`
|
||||
* model. Within that class, override the predicates provided by `ArrayFunction`
|
||||
* to match the flow within that function. Finally, add a private import
|
||||
* statement to `CustomModels.qll`
|
||||
* statement to `Models.qll`
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
|
||||
@@ -115,7 +115,19 @@ abstract class FormattingFunction extends Function {
|
||||
* Gets the position of the first format argument, corresponding with
|
||||
* the first format specifier in the format string.
|
||||
*/
|
||||
int getFirstFormatArgumentIndex() { result = getNumberOfParameters() }
|
||||
int getFirstFormatArgumentIndex() {
|
||||
result = getNumberOfParameters() and
|
||||
// the formatting function either has a definition in the snapshot, or all
|
||||
// `DeclarationEntry`s agree on the number of parameters (otherwise we don't
|
||||
// really know the correct number)
|
||||
(
|
||||
hasDefinition()
|
||||
or
|
||||
forall(FunctionDeclarationEntry fde | fde = getADeclarationEntry() |
|
||||
result = fde.getNumberOfParameters()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the buffer size argument, if any.
|
||||
|
||||
@@ -6,26 +6,106 @@
|
||||
|
||||
import semmle.code.cpp.Parameter
|
||||
|
||||
/**
|
||||
* An `int` that is a parameter index for some function. This is needed for binding in certain cases.
|
||||
*/
|
||||
class ParameterIndex extends int {
|
||||
ParameterIndex() { exists(Parameter p | this = p.getIndex()) }
|
||||
}
|
||||
|
||||
newtype TFunctionInput =
|
||||
private newtype TFunctionInput =
|
||||
TInParameter(ParameterIndex i) or
|
||||
TInParameterPointer(ParameterIndex i) or
|
||||
TInQualifier()
|
||||
TInParameterDeref(ParameterIndex i) or
|
||||
TInQualifierObject() or
|
||||
TInQualifierAddress()
|
||||
|
||||
/**
|
||||
* An input to a function. This can be:
|
||||
* - The value of one of the function's parameters
|
||||
* - The value pointed to by one of function's pointer or reference parameters
|
||||
* - The value of the function's `this` pointer
|
||||
* - The value pointed to by the function's `this` pointer
|
||||
*/
|
||||
class FunctionInput extends TFunctionInput {
|
||||
abstract string toString();
|
||||
|
||||
predicate isInParameter(ParameterIndex index) { none() }
|
||||
/**
|
||||
* Holds if this is the input value of the parameter with index `index`.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* void func(int n, char* p, float& r);
|
||||
* ```
|
||||
* - `isParameter(0)` holds for the `FunctionInput` that represents the value of `n` (with type
|
||||
* `int`) on entry to the function.
|
||||
* - `isParameter(1)` holds for the `FunctionInput` that represents the value of `p` (with type
|
||||
* `char*`) on entry to the function.
|
||||
* - `isParameter(2)` holds for the `FunctionInput` that represents the "value" of the reference
|
||||
* `r` (with type `float&`) on entry to the function, _not_ the value of the referred-to
|
||||
* `float`.
|
||||
*/
|
||||
predicate isParameter(ParameterIndex index) { none() }
|
||||
|
||||
predicate isInParameterPointer(ParameterIndex index) { none() }
|
||||
/**
|
||||
* Holds if this is the input value of the parameter with index `index`.
|
||||
* DEPRECATED: Use `isParameter(index)` instead.
|
||||
*/
|
||||
deprecated final predicate isInParameter(ParameterIndex index) { isParameter(index) }
|
||||
|
||||
predicate isInQualifier() { none() }
|
||||
/**
|
||||
* Holds if this is the input value pointed to by a pointer parameter to a function, or the input
|
||||
* value referred to by a reference parameter to a function, where the parameter has index
|
||||
* `index`.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* void func(int n, char* p, float& r);
|
||||
* ```
|
||||
* - `isParameterDeref(1)` holds for the `FunctionInput` that represents the value of `*p` (with
|
||||
* type `char`) on entry to the function.
|
||||
* - `isParameterDeref(2)` holds for the `FunctionInput` that represents the value of `r` (with type
|
||||
* `float`) on entry to the function.
|
||||
* - There is no `FunctionInput` for which `isParameterDeref(0)` holds, because `n` is neither a
|
||||
* pointer nor a reference.
|
||||
*/
|
||||
predicate isParameterDeref(ParameterIndex index) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this is the input value pointed to by a pointer parameter to a function, or the input
|
||||
* value referred to by a reference parameter to a function, where the parameter has index
|
||||
* `index`.
|
||||
* DEPRECATED: Use `isParameterDeref(index)` instead.
|
||||
*/
|
||||
deprecated final predicate isInParameterPointer(ParameterIndex index) { isParameterDeref(index) }
|
||||
|
||||
/**
|
||||
* Holds if this is the input value pointed to by the `this` pointer of an instance member
|
||||
* function.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* struct C {
|
||||
* void mfunc(int n, char* p, float& r) const;
|
||||
* };
|
||||
* ```
|
||||
* - `isQualifierObject()` holds for the `FunctionInput` that represents the value of `*this`
|
||||
* (with type `C const`) on entry to the function.
|
||||
*/
|
||||
predicate isQualifierObject() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this is the input value pointed to by the `this` pointer of an instance member
|
||||
* function.
|
||||
* DEPRECATED: Use `isQualifierObject()` instead.
|
||||
*/
|
||||
deprecated final predicate isInQualifier() { isQualifierObject() }
|
||||
|
||||
/**
|
||||
* Holds if this is the input value of the `this` pointer of an instance member function.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* struct C {
|
||||
* void mfunc(int n, char* p, float& r) const;
|
||||
* };
|
||||
* ```
|
||||
* - `isQualifierAddress()` holds for the `FunctionInput` that represents the value of `this`
|
||||
* (with type `C const *`) on entry to the function.
|
||||
*/
|
||||
predicate isQualifierAddress() { none() }
|
||||
}
|
||||
|
||||
class InParameter extends FunctionInput, TInParameter {
|
||||
@@ -35,73 +115,182 @@ class InParameter extends FunctionInput, TInParameter {
|
||||
|
||||
override string toString() { result = "InParameter " + index.toString() }
|
||||
|
||||
/** Gets the zero-based index of the parameter. */
|
||||
ParameterIndex getIndex() { result = index }
|
||||
|
||||
override predicate isInParameter(ParameterIndex i) { i = index }
|
||||
override predicate isParameter(ParameterIndex i) { i = index }
|
||||
}
|
||||
|
||||
class InParameterPointer extends FunctionInput, TInParameterPointer {
|
||||
class InParameterDeref extends FunctionInput, TInParameterDeref {
|
||||
ParameterIndex index;
|
||||
|
||||
InParameterPointer() { this = TInParameterPointer(index) }
|
||||
InParameterDeref() { this = TInParameterDeref(index) }
|
||||
|
||||
override string toString() { result = "InParameterPointer " + index.toString() }
|
||||
override string toString() { result = "InParameterDeref " + index.toString() }
|
||||
|
||||
/** Gets the zero-based index of the parameter. */
|
||||
ParameterIndex getIndex() { result = index }
|
||||
|
||||
override predicate isInParameterPointer(ParameterIndex i) { i = index }
|
||||
override predicate isParameterDeref(ParameterIndex i) { i = index }
|
||||
}
|
||||
|
||||
class InQualifier extends FunctionInput, TInQualifier {
|
||||
override string toString() { result = "InQualifier" }
|
||||
class InQualifierObject extends FunctionInput, TInQualifierObject {
|
||||
override string toString() { result = "InQualifierObject" }
|
||||
|
||||
override predicate isInQualifier() { any() }
|
||||
override predicate isQualifierObject() { any() }
|
||||
}
|
||||
|
||||
newtype TFunctionOutput =
|
||||
TOutParameterPointer(ParameterIndex i) or
|
||||
TOutQualifier() or
|
||||
class InQualifierAddress extends FunctionInput, TInQualifierAddress {
|
||||
override string toString() { result = "InQualifierAddress" }
|
||||
|
||||
override predicate isQualifierAddress() { any() }
|
||||
}
|
||||
|
||||
private newtype TFunctionOutput =
|
||||
TOutParameterDeref(ParameterIndex i) or
|
||||
TOutQualifierObject() or
|
||||
TOutReturnValue() or
|
||||
TOutReturnPointer()
|
||||
TOutReturnValueDeref()
|
||||
|
||||
/**
|
||||
* An output from a function. This can be:
|
||||
* - The value pointed to by one of function's pointer or reference parameters
|
||||
* - The value pointed to by the function's `this` pointer
|
||||
* - The function's return value
|
||||
* - The value pointed to by the function's return value, if the return value is a pointer or
|
||||
* reference
|
||||
*/
|
||||
class FunctionOutput extends TFunctionOutput {
|
||||
abstract string toString();
|
||||
|
||||
predicate isOutParameterPointer(ParameterIndex i) { none() }
|
||||
/**
|
||||
* Holds if this is the output value pointed to by a pointer parameter to a function, or the
|
||||
* output value referred to by a reference parameter to a function, where the parameter has
|
||||
* index `index`.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* void func(int n, char* p, float& r);
|
||||
* ```
|
||||
* - `isParameterDeref(1)` holds for the `FunctionOutput` that represents the value of `*p` (with
|
||||
* type `char`) on return from the function.
|
||||
* - `isParameterDeref(2)` holds for the `FunctionOutput` that represents the value of `r` (with
|
||||
* type `float`) on return from the function.
|
||||
* - There is no `FunctionOutput` for which `isParameterDeref(0)` holds, because `n` is neither a
|
||||
* pointer nor a reference.
|
||||
*/
|
||||
predicate isParameterDeref(ParameterIndex i) { none() }
|
||||
|
||||
predicate isOutQualifier() { none() }
|
||||
/**
|
||||
* Holds if this is the output value pointed to by a pointer parameter to a function, or the
|
||||
* output value referred to by a reference parameter to a function, where the parameter has
|
||||
* index `index`.
|
||||
* DEPRECATED: Use `isParameterDeref(index)` instead.
|
||||
*/
|
||||
deprecated final predicate isOutParameterPointer(ParameterIndex index) { isParameterDeref(index) }
|
||||
|
||||
predicate isOutReturnValue() { none() }
|
||||
/**
|
||||
* Holds if this is the output value pointed to by the `this` pointer of an instance member
|
||||
* function.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* struct C {
|
||||
* void mfunc(int n, char* p, float& r);
|
||||
* };
|
||||
* ```
|
||||
* - `isQualifierObject()` holds for the `FunctionOutput` that represents the value of `*this`
|
||||
* (with type `C`) on return from the function.
|
||||
*/
|
||||
predicate isQualifierObject() { none() }
|
||||
|
||||
predicate isOutReturnPointer() { none() }
|
||||
/**
|
||||
* Holds if this is the output value pointed to by the `this` pointer of an instance member
|
||||
* function.
|
||||
* DEPRECATED: Use `isQualifierObject()` instead.
|
||||
*/
|
||||
deprecated final predicate isOutQualifier() { isQualifierObject() }
|
||||
|
||||
/**
|
||||
* Holds if this is the value returned by a function.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* int getInt();
|
||||
* char* getPointer();
|
||||
* float& getReference();
|
||||
* ```
|
||||
* - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by
|
||||
* `getInt()` (with type `int`).
|
||||
* - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by
|
||||
* `getPointer()` (with type `char*`).
|
||||
* - `isReturnValue()` holds for the `FunctionOutput` that represents the "value" of the reference
|
||||
* returned by `getReference()` (with type `float&`), _not_ the value of the referred-to
|
||||
* `float`.
|
||||
*/
|
||||
predicate isReturnValue() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this is the value returned by a function.
|
||||
* DEPRECATED: Use `isReturnValue()` instead.
|
||||
*/
|
||||
deprecated final predicate isOutReturnValue() { isReturnValue() }
|
||||
|
||||
/**
|
||||
* Holds if this is the output value pointed to by the return value of a function, if the function
|
||||
* returns a pointer, or the output value referred to by the return value of a function, if the
|
||||
* function returns a reference.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* char* getPointer();
|
||||
* float& getReference();
|
||||
* int getInt();
|
||||
* ```
|
||||
* - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of
|
||||
* `*getPointer()` (with type `char`).
|
||||
* - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of
|
||||
* `getReference()` (with type `float`).
|
||||
* - There is no `FunctionOutput` of `getInt()` for which `isReturnValueDeref()` holds because the
|
||||
* return type of `getInt()` is neither a pointer nor a reference.
|
||||
*/
|
||||
predicate isReturnValueDeref() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this is the output value pointed to by the return value of a function, if the function
|
||||
* returns a pointer, or the output value referred to by the return value of a function, if the
|
||||
* function returns a reference.
|
||||
* DEPRECATED: Use `isReturnValueDeref()` instead.
|
||||
*/
|
||||
deprecated final predicate isOutReturnPointer() { isReturnValueDeref() }
|
||||
}
|
||||
|
||||
class OutParameterPointer extends FunctionOutput, TOutParameterPointer {
|
||||
class OutParameterDeref extends FunctionOutput, TOutParameterDeref {
|
||||
ParameterIndex index;
|
||||
|
||||
OutParameterPointer() { this = TOutParameterPointer(index) }
|
||||
OutParameterDeref() { this = TOutParameterDeref(index) }
|
||||
|
||||
override string toString() { result = "OutParameterPointer " + index.toString() }
|
||||
override string toString() { result = "OutParameterDeref " + index.toString() }
|
||||
|
||||
ParameterIndex getIndex() { result = index }
|
||||
|
||||
override predicate isOutParameterPointer(ParameterIndex i) { i = index }
|
||||
override predicate isParameterDeref(ParameterIndex i) { i = index }
|
||||
}
|
||||
|
||||
class OutQualifier extends FunctionOutput, TOutQualifier {
|
||||
override string toString() { result = "OutQualifier" }
|
||||
class OutQualifierObject extends FunctionOutput, TOutQualifierObject {
|
||||
override string toString() { result = "OutQualifierObject" }
|
||||
|
||||
override predicate isOutQualifier() { any() }
|
||||
override predicate isQualifierObject() { any() }
|
||||
}
|
||||
|
||||
class OutReturnValue extends FunctionOutput, TOutReturnValue {
|
||||
override string toString() { result = "OutReturnValue" }
|
||||
|
||||
override predicate isOutReturnValue() { any() }
|
||||
override predicate isReturnValue() { any() }
|
||||
}
|
||||
|
||||
class OutReturnPointer extends FunctionOutput, TOutReturnPointer {
|
||||
override string toString() { result = "OutReturnPointer" }
|
||||
class OutReturnValueDeref extends FunctionOutput, TOutReturnValueDeref {
|
||||
override string toString() { result = "OutReturnValueDeref" }
|
||||
|
||||
override predicate isOutReturnPointer() { any() }
|
||||
override predicate isReturnValueDeref() { any() }
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.Models
|
||||
import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
|
||||
|
||||
/**
|
||||
* Models the side effects of a library function.
|
||||
@@ -19,12 +20,33 @@ abstract class SideEffectFunction extends Function {
|
||||
* This memory could be from global variables, or from other memory that was reachable from a
|
||||
* pointer that was passed into the function.
|
||||
*/
|
||||
abstract predicate neverReadsMemory();
|
||||
abstract predicate hasOnlySpecificReadSideEffects();
|
||||
|
||||
/**
|
||||
* Holds if the function never writes to memory that remains allocated after the function
|
||||
* returns. This memory could be from global variables, or from other memory that was reachable
|
||||
* from a pointer that was passed into the function.
|
||||
*/
|
||||
abstract predicate neverWritesMemory();
|
||||
abstract predicate hasOnlySpecificWriteSideEffects();
|
||||
|
||||
/**
|
||||
* Holds if the value pointed to by the parameter at index `i` is written to. `buffer` is true
|
||||
* if the write may be at an offset. `mustWrite` is true if the write is unconditional.
|
||||
*/
|
||||
predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value pointed to by the parameter at index `i` is read from. `buffer` is true
|
||||
* if the read may be at an offset.
|
||||
*/
|
||||
predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { none() }
|
||||
|
||||
// TODO: name?
|
||||
/**
|
||||
* Gets the index of the parameter that indicates the size of the buffer pointed to by the
|
||||
* parameter at index `i`.
|
||||
*/
|
||||
ParameterIndex getParameterSizeIndex(ParameterIndex i) { none() }
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import cpp
|
||||
|
||||
/**
|
||||
* Align the specified offset up to the specified alignment boundary.
|
||||
* The result is the smallest integer i such that (i % alignment) = 0
|
||||
* and (i >= offset)
|
||||
* The result is the smallest integer `i` such that `(i % alignment) = 0`
|
||||
* and `(i >= offset)`.
|
||||
*/
|
||||
bindingset[offset, alignment]
|
||||
private int alignUp(int offset, int alignment) {
|
||||
@@ -30,16 +30,16 @@ abstract class Architecture extends string {
|
||||
/** Gets the size of a pointer, in bits. */
|
||||
abstract int pointerSize();
|
||||
|
||||
/** Gets the size of a 'long int', in bits. */
|
||||
/** Gets the size of a `long int`, in bits. */
|
||||
abstract int longSize();
|
||||
|
||||
/** Gets the size of a 'long double', in bits. */
|
||||
/** Gets the size of a `long double`, in bits. */
|
||||
abstract int longDoubleSize();
|
||||
|
||||
/** Gets the size of a 'long long', in bits. */
|
||||
/** Gets the size of a `long long`, in bits. */
|
||||
abstract int longLongSize();
|
||||
|
||||
/** Gets the size of a 'wchar_t', in bits. */
|
||||
/** Gets the size of a `wchar_t`, in bits. */
|
||||
abstract int wideCharSize();
|
||||
|
||||
/** Gets the alignment boundary for doubles, in bits. */
|
||||
@@ -479,8 +479,10 @@ class PaddedType extends Class {
|
||||
int typeBitSize(Architecture arch) {
|
||||
if this instanceof Union
|
||||
then
|
||||
// A correct implementation for unions would be
|
||||
// A correct implementation for unions would be:
|
||||
// ```
|
||||
// result = max(fieldSize(_, arch))
|
||||
// ```
|
||||
// but that uses a recursive aggregate, which isn't supported in
|
||||
// QL. We therefore use this slightly more complex implementation
|
||||
// instead.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This library is a clone of semmle.code.cpp.controlflow.SSA, with
|
||||
* only one difference: extra phi definitions are added after
|
||||
* guards. For example:
|
||||
*
|
||||
* ```
|
||||
* x = f();
|
||||
* if (x < 10) {
|
||||
* // Block 1
|
||||
@@ -11,12 +11,12 @@
|
||||
* // Block 2
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
* In standard SSA, basic blocks 1 and 2 do not need phi definitions
|
||||
* for x, because they are dominated by the definition of x on the
|
||||
* first line. In RangeSSA, however, we add phi definitions for x at
|
||||
* for `x`, because they are dominated by the definition of `x` on the
|
||||
* first line. In RangeSSA, however, we add phi definitions for `x` at
|
||||
* the beginning of blocks 1 and 2. This is useful for range analysis
|
||||
* because it enables us to deduce a more accurate range for x in the
|
||||
* because it enables us to deduce a more accurate range for `x` in the
|
||||
* two branches of the if-statement.
|
||||
*/
|
||||
|
||||
@@ -74,19 +74,19 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
|
||||
|
||||
/**
|
||||
* A string representation of the SSA variable represented by the pair
|
||||
* (this, v).
|
||||
* `(this, v)`.
|
||||
*/
|
||||
string toString(LocalScopeVariable v) { exists(RangeSSA x | result = x.toString(this, v)) }
|
||||
|
||||
/** Gets a use of the SSA variable represented by the pair (this, v) */
|
||||
/** Gets a use of the SSA variable represented by the pair `(this, v)`. */
|
||||
VariableAccess getAUse(LocalScopeVariable v) { exists(RangeSSA x | result = x.getAUse(this, v)) }
|
||||
|
||||
/** Gets the control flow node for this definition */
|
||||
/** Gets the control flow node for this definition. */
|
||||
ControlFlowNode getDefinition() { result = this }
|
||||
|
||||
BasicBlock getBasicBlock() { result.contains(getDefinition()) }
|
||||
|
||||
/** Whether this definition is a phi node for variable v */
|
||||
/** Whether this definition is a phi node for variable `v`. */
|
||||
predicate isPhiNode(LocalScopeVariable v) {
|
||||
exists(RangeSSA x | x.phi_node(v, this.(BasicBlock)))
|
||||
}
|
||||
@@ -136,7 +136,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the expression assigned to this SsaDefinition */
|
||||
/** Gets the expression assigned to this SsaDefinition. */
|
||||
Expr getDefiningValue(LocalScopeVariable v) {
|
||||
exists(ControlFlowNode def | def = this.getDefinition() |
|
||||
def = v.getInitializer().getExpr() and def = result
|
||||
|
||||
487
cpp/ql/src/semmle/code/cpp/security/boostorg/asio/protocols.qll
Normal file
487
cpp/ql/src/semmle/code/cpp/security/boostorg/asio/protocols.qll
Normal file
@@ -0,0 +1,487 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
module BoostorgAsio {
|
||||
/**
|
||||
* Represents boost::asio::ssl::context enum
|
||||
*/
|
||||
class SslContextMethod extends Enum {
|
||||
SslContextMethod() {
|
||||
this.getName().toString() = "method" and
|
||||
this.getQualifiedName().toString().matches("boost::asio::ssl::context%")
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the value for a banned protocol
|
||||
*/
|
||||
EnumConstant getABannedProtocolConstant() {
|
||||
result = this.getAnEnumConstant() and
|
||||
(
|
||||
/// Generic SSL version 2.
|
||||
result.getName() = "sslv2"
|
||||
or
|
||||
/// SSL version 2 client.
|
||||
result.getName() = "sslv2_client"
|
||||
or
|
||||
/// SSL version 2 server.
|
||||
result.getName() = "sslv2_server"
|
||||
or
|
||||
/// Generic SSL version 3.
|
||||
result.getName() = "sslv3"
|
||||
or
|
||||
/// SSL version 3 client.
|
||||
result.getName() = "sslv3_client"
|
||||
or
|
||||
/// SSL version 3 server.
|
||||
result.getName() = "sslv3_server"
|
||||
or
|
||||
/// Generic TLS version 1.
|
||||
result.getName() = "tlsv1"
|
||||
or
|
||||
/// TLS version 1 client.
|
||||
result.getName() = "tlsv1_client"
|
||||
or
|
||||
/// TLS version 1 server.
|
||||
result.getName() = "tlsv1_server"
|
||||
or
|
||||
/// Generic TLS version 1.1.
|
||||
result.getName() = "tlsv11"
|
||||
or
|
||||
/// TLS version 1.1 client.
|
||||
result.getName() = "tlsv11_client"
|
||||
or
|
||||
/// TLS version 1.1 server.
|
||||
result.getName() = "tlsv11_server"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the value for a approved protocols, but that are hard-coded (i.e. no protocol negotiation)
|
||||
*/
|
||||
EnumConstant getAnApprovedButHardcodedProtocolConstant() {
|
||||
result = this.getATls12ProtocolConstant()
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the value for a TLS v1.2 protocol
|
||||
*/
|
||||
EnumConstant getATls12ProtocolConstant() {
|
||||
result = this.getAnEnumConstant() and
|
||||
(
|
||||
/// Generic TLS version 1.2.
|
||||
result.getName() = "tlsv12"
|
||||
or
|
||||
/// TLS version 1.2 client.
|
||||
result.getName() = "tlsv12_client"
|
||||
or
|
||||
/// TLS version 1.2 server.
|
||||
result.getName() = "tlsv12_server"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the value for a TLS v1.3 protocol
|
||||
*/
|
||||
EnumConstant getATls13ProtocolConstant() {
|
||||
result = this.getAnEnumConstant() and
|
||||
(
|
||||
/// Generic TLS version 1.3.
|
||||
result.getName() = "tlsv13"
|
||||
or
|
||||
/// TLS version 1.3 client.
|
||||
result.getName() = "tlsv13_client"
|
||||
or
|
||||
/// TLS version 1.3 server.
|
||||
result.getName() = "tlsv13_server"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the value of a generic TLS or SSL/TLS protocol
|
||||
*/
|
||||
EnumConstant getAGenericTlsProtocolConstant() {
|
||||
result = this.getAnEnumConstant() and
|
||||
(
|
||||
/// Generic TLS
|
||||
result.getName() = "tls"
|
||||
or
|
||||
/// TLS client.
|
||||
result.getName() = "tls_client"
|
||||
or
|
||||
/// TLS server.
|
||||
result.getName() = "tls_server"
|
||||
)
|
||||
or
|
||||
result = getASslv23ProtocolConstant()
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the value of a generic SSL/TLS protocol
|
||||
*/
|
||||
EnumConstant getASslv23ProtocolConstant() {
|
||||
result = this.getAnEnumConstant() and
|
||||
(
|
||||
/// OpenSSL - SSLv23 == A TLS/SSL connection established with these methods may understand the SSLv2, SSLv3, TLSv1, TLSv1.1 and TLSv1.2 protocols.
|
||||
/// Generic SSL/TLS.
|
||||
result.getName() = "sslv23"
|
||||
or
|
||||
/// SSL/TLS client.
|
||||
result.getName() = "sslv23_client"
|
||||
or
|
||||
/// SSL/TLS server.
|
||||
result.getName() = "sslv23_server"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: ignore - Modern versions of OpenSSL do not support SSL v2 anymore, so this option is for backwards compatibility only
|
||||
*/
|
||||
int getShiftedSslOptionsNoSsl2() {
|
||||
// SSL_OP_NO_SSLv2 was removed from modern OpenSSL versions
|
||||
result = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* RightShift(16) value for no_sslv3 constant
|
||||
*/
|
||||
int getShiftedSslOptionsNoSsl3() {
|
||||
// SSL_OP_NO_SSLv3 == 0x02000000U
|
||||
result = 512
|
||||
}
|
||||
|
||||
/**
|
||||
* RightShift(16) value for no_tlsv1 constant
|
||||
*/
|
||||
int getShiftedSslOptionsNoTls1() {
|
||||
// SSL_OP_NO_TLSv1 == 0x04000000U
|
||||
result = 1024
|
||||
}
|
||||
|
||||
/**
|
||||
* RightShift(16) value for no_tlsv1_1 constant
|
||||
*/
|
||||
int getShiftedSslOptionsNoTls1_1() {
|
||||
// SSL_OP_NO_TLSv1_1 == 0x10000000U
|
||||
result = 4096
|
||||
}
|
||||
|
||||
/**
|
||||
* RightShift(16) value for no_tlsv1_2 constant
|
||||
*/
|
||||
int getShiftedSslOptionsNoTls1_2() {
|
||||
// SSL_OP_NO_TLSv1_2 == 0x08000000U
|
||||
result = 2048
|
||||
}
|
||||
|
||||
/**
|
||||
* RightShift(16) value for no_tlsv1_3 constant
|
||||
*/
|
||||
int getShiftedSslOptionsNoTls1_3() {
|
||||
// SSL_OP_NO_TLSv1_2 == 0x20000000U
|
||||
result = 8192
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents boost::asio::ssl::context class
|
||||
*/
|
||||
class SslContextClass extends Class {
|
||||
SslContextClass() { this.getQualifiedName() = "boost::asio::ssl::context" }
|
||||
|
||||
ConstructorCall getAContructorCall() {
|
||||
this.getAConstructor().getACallToThisFunction() = result and
|
||||
not result.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||
result.fromSource()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents boost::asio::ssl::context::set_options member function
|
||||
*/
|
||||
class SslSetOptionsFunction extends Function {
|
||||
SslSetOptionsFunction() {
|
||||
this.getQualifiedName().matches("boost::asio::ssl::context::set_options")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* holds if the expression represents a banned protocol
|
||||
*/
|
||||
predicate isExprBannedBoostProtocol(Expr e) {
|
||||
exists(Literal va | va = e |
|
||||
va.getValue().toInt() = 0 or
|
||||
va.getValue().toInt() = 1 or
|
||||
va.getValue().toInt() = 2 or
|
||||
va.getValue().toInt() = 3 or
|
||||
va.getValue().toInt() = 4 or
|
||||
va.getValue().toInt() = 5 or
|
||||
va.getValue().toInt() = 6 or
|
||||
va.getValue().toInt() = 7 or
|
||||
va.getValue().toInt() = 8 or
|
||||
va.getValue().toInt() = 12 or
|
||||
va.getValue().toInt() = 13 or
|
||||
va.getValue().toInt() = 14
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va | va = e |
|
||||
va.getValue().toInt() = 0 or
|
||||
va.getValue().toInt() = 1 or
|
||||
va.getValue().toInt() = 2 or
|
||||
va.getValue().toInt() = 3 or
|
||||
va.getValue().toInt() = 4 or
|
||||
va.getValue().toInt() = 5 or
|
||||
va.getValue().toInt() = 6 or
|
||||
va.getValue().toInt() = 7 or
|
||||
va.getValue().toInt() = 8 or
|
||||
va.getValue().toInt() = 12 or
|
||||
va.getValue().toInt() = 13 or
|
||||
va.getValue().toInt() = 14
|
||||
)
|
||||
or
|
||||
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||
enum.getABannedProtocolConstant().getAnAccess() = eca
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* holds if the expression represents a TLS v1.2 protocol
|
||||
*/
|
||||
predicate isExprTls12BoostProtocol(Expr e) {
|
||||
exists(Literal va | va = e |
|
||||
(
|
||||
va.getValue().toInt() = 15 or /// Generic TLS version 1.2.
|
||||
va.getValue().toInt() = 16 or /// TLS version 1.2 client.
|
||||
va.getValue().toInt() = 17 /// TLS version 1.2 server.
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va | va = e |
|
||||
(
|
||||
va.getValue().toInt() = 15 or /// Generic TLS version 1.2.
|
||||
va.getValue().toInt() = 16 or /// TLS version 1.2 client.
|
||||
va.getValue().toInt() = 17 /// TLS version 1.2 server.
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||
enum.getATls12ProtocolConstant().getAnAccess() = eca
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* holds if the expression represents a protocol that requires Crypto Board approval
|
||||
*/
|
||||
predicate isExprTls13BoostProtocol(Expr e) {
|
||||
exists(Literal va | va = e |
|
||||
(
|
||||
va.getValue().toInt() = 18 or
|
||||
va.getValue().toInt() = 19 or
|
||||
va.getValue().toInt() = 20
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va | va = e |
|
||||
(
|
||||
va.getValue().toInt() = 18 or
|
||||
va.getValue().toInt() = 19 or
|
||||
va.getValue().toInt() = 20
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||
enum.getATls13ProtocolConstant().getAnAccess() = eca
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* holds if the expression represents a generic TLS or SSL/TLS protocol
|
||||
*/
|
||||
predicate isExprTlsBoostProtocol(Expr e) {
|
||||
exists(Literal va | va = e |
|
||||
(
|
||||
va.getValue().toInt() = 9 or /// Generic SSL/TLS.
|
||||
va.getValue().toInt() = 10 or /// SSL/TLS client.
|
||||
va.getValue().toInt() = 11 or /// SSL/TLS server.
|
||||
va.getValue().toInt() = 21 or /// Generic TLS.
|
||||
va.getValue().toInt() = 22 or /// TLS client.
|
||||
va.getValue().toInt() = 23 /// TLS server.
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va | va = e |
|
||||
(
|
||||
va.getValue().toInt() = 9 or /// Generic SSL/TLS.
|
||||
va.getValue().toInt() = 10 or /// SSL/TLS client.
|
||||
va.getValue().toInt() = 11 or /// SSL/TLS server.
|
||||
va.getValue().toInt() = 21 or /// Generic TLS.
|
||||
va.getValue().toInt() = 22 or /// TLS client.
|
||||
va.getValue().toInt() = 23 /// TLS server.
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||
enum.getAGenericTlsProtocolConstant().getAnAccess() = eca
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* holds if the expression represents a generic SSl/TLS protocol
|
||||
*/
|
||||
predicate isExprSslV23BoostProtocol(Expr e) {
|
||||
exists(Literal va | va = e |
|
||||
(
|
||||
va.getValue().toInt() = 9 or /// Generic SSL/TLS.
|
||||
va.getValue().toInt() = 10 or /// SSL/TLS client.
|
||||
va.getValue().toInt() = 11 /// SSL/TLS server.
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va | va = e |
|
||||
(
|
||||
va.getValue().toInt() = 9 or /// Generic SSL/TLS.
|
||||
va.getValue().toInt() = 10 or /// SSL/TLS client.
|
||||
va.getValue().toInt() = 11 /// SSL/TLS server.
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||
enum.getASslv23ProtocolConstant().getAnAccess() = eca
|
||||
)
|
||||
}
|
||||
|
||||
//////////////////////// Dataflow /////////////////////
|
||||
/**
|
||||
* Abstract - Protocol value Flows to the first argument of the context constructor
|
||||
*/
|
||||
abstract class SslContextCallAbstractConfig extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
SslContextCallAbstractConfig() { any() }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(ConstructorCall cc, SslContextClass c, Expr e | e = sink.asExpr() |
|
||||
c.getAContructorCall() = cc and
|
||||
cc.getArgument(0) = e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* any Protocol value Flows to the first argument of the context constructor
|
||||
*/
|
||||
class SslContextCallConfig extends SslContextCallAbstractConfig {
|
||||
SslContextCallConfig() { this = "SslContextCallConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(Expr e | e = source.asExpr() |
|
||||
e.fromSource() and
|
||||
not e.getLocation().getFile().toString().matches("%/boost/asio/%")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* a banned protocol value Flows to the first argument of the context constructor
|
||||
*/
|
||||
class SslContextCallBannedProtocolConfig extends SslContextCallAbstractConfig {
|
||||
SslContextCallBannedProtocolConfig() { this = "SslContextCallBannedProtocolConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(Expr e | e = source.asExpr() |
|
||||
e.fromSource() and
|
||||
not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||
isExprBannedBoostProtocol(e)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* a TLS 1.2 protocol value Flows to the first argument of the context constructor
|
||||
*/
|
||||
class SslContextCallTls12ProtocolConfig extends SslContextCallAbstractConfig {
|
||||
SslContextCallTls12ProtocolConfig() { this = "SslContextCallTls12ProtocolConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(Expr e | e = source.asExpr() |
|
||||
e.fromSource() and
|
||||
not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||
isExprTls12BoostProtocol(e)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* a TLS 1.3 protocol value Flows to the first argument of the context constructor
|
||||
*/
|
||||
class SslContextCallTls13ProtocolConfig extends SslContextCallAbstractConfig {
|
||||
SslContextCallTls13ProtocolConfig() { this = "SslContextCallTls12ProtocolConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(Expr e | e = source.asExpr() |
|
||||
e.fromSource() and
|
||||
not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||
isExprTls13BoostProtocol(e)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* a generic TLS protocol value Flows to the first argument of the context constructor
|
||||
*/
|
||||
class SslContextCallTlsProtocolConfig extends SslContextCallAbstractConfig {
|
||||
SslContextCallTlsProtocolConfig() { this = "SslContextCallTlsProtocolConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(Expr e | e = source.asExpr() |
|
||||
e.fromSource() and
|
||||
not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||
isExprTlsBoostProtocol(e)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* a context constructor call flows to a call calling SetOptions()
|
||||
*/
|
||||
class SslContextFlowsToSetOptionConfig extends DataFlow::Configuration {
|
||||
SslContextFlowsToSetOptionConfig() { this = "SslContextFlowsToSetOptionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(SslContextClass c, ConstructorCall cc |
|
||||
cc = source.asExpr() and
|
||||
c.getAContructorCall() = cc
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall fc, SslSetOptionsFunction f, Variable v, VariableAccess va |
|
||||
va = sink.asExpr()
|
||||
|
|
||||
f.getACallToThisFunction() = fc and
|
||||
v.getAnAccess() = va and
|
||||
va = fc.getQualifier()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* an option value flows to the 1st parameter of SetOptions()
|
||||
*/
|
||||
class SslOptionConfig extends DataFlow::Configuration {
|
||||
SslOptionConfig() { this = "SslOptionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(Expr e | e = source.asExpr() |
|
||||
e.fromSource() and
|
||||
not e.getLocation().getFile().toString().matches("%/boost/asio/%")
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(SslSetOptionsFunction f, FunctionCall call |
|
||||
sink.asExpr() = call.getArgument(0) and
|
||||
f.getACallToThisFunction() = call and
|
||||
not sink.getLocation().getFile().toString().matches("%/boost/asio/%")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,6 +294,8 @@ class IfStmt extends ConditionalStmt, @stmt_if {
|
||||
* ```
|
||||
*/
|
||||
class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
|
||||
override string getCanonicalQLClass() { result = "ConstexprIfStmt" }
|
||||
|
||||
/**
|
||||
* Gets the condition expression of this 'constexpr if' statement.
|
||||
*
|
||||
@@ -1645,6 +1647,7 @@ class EnumSwitch extends SwitchStmt {
|
||||
* } catch (std::exception &e) {
|
||||
* g();
|
||||
* }
|
||||
* ```
|
||||
* there is a handler that's associated with the `catch` block and controls
|
||||
* entry to it.
|
||||
*/
|
||||
|
||||
@@ -729,6 +729,18 @@ private predicate mk_AlignofExpr(HashCons child, AlignofExprOperator e) {
|
||||
child = hashCons(e.getAChild())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hash cons of field initializer expressions [0..i), where i > 0, for
|
||||
* the class aggregate literal `cal` of type `c`, where `head` is the hash cons
|
||||
* of the i'th initializer expression.
|
||||
*/
|
||||
HC_Fields aggInitExprsUpTo(ClassAggregateLiteral cal, Class c, int i) {
|
||||
exists(Field f, HashCons head, HC_Fields tail |
|
||||
result = HC_FieldCons(c, i - 1, f, head, tail) and
|
||||
mk_FieldCons(c, i - 1, f, head, tail, cal)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate mk_FieldCons(
|
||||
Class c, int i, Field f, HashCons hc, HC_Fields hcf, ClassAggregateLiteral cal
|
||||
) {
|
||||
@@ -738,12 +750,8 @@ private predicate mk_FieldCons(
|
||||
e = cal.getFieldExpr(f).getFullyConverted() and
|
||||
f.getInitializationOrder() = i and
|
||||
(
|
||||
exists(HashCons head, Field f2, HC_Fields tail |
|
||||
hc = hashCons(e) and
|
||||
hcf = HC_FieldCons(c, i - 1, f2, head, tail) and
|
||||
f2.getInitializationOrder() = i - 1 and
|
||||
mk_FieldCons(c, i - 1, f2, head, tail, cal)
|
||||
)
|
||||
hc = hashCons(e) and
|
||||
hcf = aggInitExprsUpTo(cal, c, i)
|
||||
or
|
||||
hc = hashCons(e) and
|
||||
i = 0 and
|
||||
@@ -766,14 +774,7 @@ private predicate mk_ClassAggregateLiteral(Class c, HC_Fields hcf, ClassAggregat
|
||||
analyzableClassAggregateLiteral(cal) and
|
||||
c = cal.getUnspecifiedType() and
|
||||
(
|
||||
exists(HC_Fields tail, Expr e, Field f, int numChildren, HashCons eCons |
|
||||
f.getInitializationOrder() = cal.getNumChild() - 1 and
|
||||
e = cal.getFieldExpr(f).getFullyConverted() and
|
||||
eCons = hashCons(e) and
|
||||
numChildren = cal.getNumChild() and
|
||||
hcf = HC_FieldCons(c, numChildren - 1, f, eCons, tail) and
|
||||
mk_FieldCons(c, numChildren - 1, f, eCons, tail, cal)
|
||||
)
|
||||
hcf = aggInitExprsUpTo(cal, c, cal.getNumChild())
|
||||
or
|
||||
cal.getNumChild() = 0 and
|
||||
hcf = HC_EmptyFields(c)
|
||||
@@ -785,15 +786,23 @@ private predicate analyzableArrayAggregateLiteral(ArrayAggregateLiteral aal) {
|
||||
strictcount(aal.getUnspecifiedType()) = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hash cons of array elements in [0..i), where i > 0, for
|
||||
* the array aggregate literal `aal` of type `t`.
|
||||
*/
|
||||
private HC_Array arrayElemsUpTo(ArrayAggregateLiteral aal, Type t, int i) {
|
||||
exists(HC_Array tail, HashCons head |
|
||||
result = HC_ArrayCons(t, i - 1, head, tail) and
|
||||
mk_ArrayCons(t, i - 1, head, tail, aal)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate mk_ArrayCons(Type t, int i, HashCons hc, HC_Array hca, ArrayAggregateLiteral aal) {
|
||||
analyzableArrayAggregateLiteral(aal) and
|
||||
t = aal.getUnspecifiedType() and
|
||||
hc = hashCons(aal.getChild(i)) and
|
||||
(
|
||||
exists(HC_Array tail, HashCons head |
|
||||
hca = HC_ArrayCons(t, i - 1, head, tail) and
|
||||
mk_ArrayCons(t, i - 1, head, tail, aal)
|
||||
)
|
||||
hca = arrayElemsUpTo(aal, t, i)
|
||||
or
|
||||
i = 0 and
|
||||
hca = HC_EmptyArray(t)
|
||||
|
||||
@@ -105,7 +105,7 @@ compilation_time(
|
||||
*/
|
||||
#keyset[compilation, file_number, file_number_diagnostic_number]
|
||||
diagnostic_for(
|
||||
unique int diagnostic : @diagnostic ref,
|
||||
int diagnostic : @diagnostic ref,
|
||||
int compilation : @compilation ref,
|
||||
int file_number : int ref,
|
||||
int file_number_diagnostic_number : int ref
|
||||
@@ -422,8 +422,9 @@ function_defaulted(unique int id: @function ref);
|
||||
|
||||
|
||||
|
||||
#keyset[id, type_id]
|
||||
fun_decls(
|
||||
unique int id: @fun_decl,
|
||||
int id: @fun_decl,
|
||||
int function: @function ref,
|
||||
int type_id: @type ref,
|
||||
string name: string ref,
|
||||
@@ -460,8 +461,9 @@ param_decl_bind(
|
||||
int fun_decl: @fun_decl ref
|
||||
);
|
||||
|
||||
#keyset[id, type_id]
|
||||
var_decls(
|
||||
unique int id: @var_decl,
|
||||
int id: @var_decl,
|
||||
int variable: @variable ref,
|
||||
int type_id: @type ref,
|
||||
string name: string ref,
|
||||
@@ -521,18 +523,21 @@ params(
|
||||
|
||||
overrides(int new: @function ref, int old: @function ref);
|
||||
|
||||
#keyset[id, type_id]
|
||||
membervariables(
|
||||
unique int id: @membervariable,
|
||||
int id: @membervariable,
|
||||
int type_id: @type ref,
|
||||
string name: string ref
|
||||
);
|
||||
|
||||
#keyset[id, type_id]
|
||||
globalvariables(
|
||||
unique int id: @globalvariable,
|
||||
int id: @globalvariable,
|
||||
int type_id: @type ref,
|
||||
string name: string ref
|
||||
);
|
||||
|
||||
#keyset[id, type_id]
|
||||
localvariables(
|
||||
int id: @localvariable,
|
||||
int type_id: @type ref,
|
||||
@@ -675,7 +680,7 @@ decltypes(
|
||||
| 2 = class
|
||||
| 3 = union
|
||||
| 4 = enum
|
||||
| 5 = typedef
|
||||
| 5 = typedef // classic C: typedef typedef type name
|
||||
| 6 = template
|
||||
| 7 = template_parameter
|
||||
| 8 = template_template_parameter
|
||||
@@ -684,6 +689,7 @@ decltypes(
|
||||
// ... 11 objc_protocol deprecated
|
||||
// ... 12 objc_category deprecated
|
||||
| 13 = scoped_enum
|
||||
| 14 = using_alias // a using name = type style typedef
|
||||
;
|
||||
*/
|
||||
usertypes(
|
||||
@@ -1060,10 +1066,12 @@ compgenerated(unique int id: @element ref);
|
||||
* destructed in reverse construction order, so for a given `element`
|
||||
* these should be called from highest to lowest `i`.
|
||||
*/
|
||||
#keyset[element, destructor_call]
|
||||
#keyset[element, i]
|
||||
synthetic_destructor_call(
|
||||
int element: @element ref,
|
||||
int i: int ref,
|
||||
unique int destructor_call: @routineexpr ref
|
||||
int destructor_call: @routineexpr ref
|
||||
);
|
||||
|
||||
namespaces(
|
||||
@@ -1509,6 +1517,7 @@ case @expr.kind of
|
||||
| 322 = @builtinaddressof
|
||||
| 323 = @vec_fill
|
||||
| 324 = @builtinconvertvector
|
||||
| 325 = @builtincomplex
|
||||
;
|
||||
|
||||
new_allocated_type(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user