Merge branch 'main' into identity-consistency-check

This commit is contained in:
Mathias Vorreiter Pedersen
2023-05-03 22:01:06 +01:00
555 changed files with 21638 additions and 9738 deletions

View File

@@ -40,7 +40,6 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",

View File

@@ -1,3 +1,7 @@
## 0.7.1
No user-facing changes.
## 0.7.0
### Breaking Changes

View File

@@ -0,0 +1,4 @@
---
category: feature
---
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.

View File

@@ -0,0 +1,3 @@
## 0.7.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.7.0
lastReleaseVersion: 0.7.1

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 0.7.1-dev
version: 0.7.2-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

File diff suppressed because it is too large Load Diff

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -607,13 +607,21 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
result.getReturnKind() = kind
}
/** A variable that behaves like a global variable. */
class GlobalLikeVariable extends Variable {
GlobalLikeVariable() {
this instanceof Cpp::GlobalOrNamespaceVariable or
this instanceof Cpp::StaticLocalVariable
}
}
/**
* Holds if data can flow from `node1` to `node2` in a way that loses the
* calling context. For example, this would happen with flow through a
* global or static variable.
*/
predicate jumpStep(Node n1, Node n2) {
exists(Cpp::GlobalOrNamespaceVariable v |
exists(GlobalLikeVariable v |
exists(Ssa::GlobalUse globalUse |
v = globalUse.getVariable() and
n1.(FinalGlobalValue).getGlobalUse() = globalUse

View File

@@ -1903,7 +1903,38 @@ signature predicate guardChecksSig(IRGuardCondition g, Expr e, boolean branch);
* in data flow and taint tracking.
*/
module BarrierGuard<guardChecksSig/3 guardChecks> {
/** Gets a node that is safely guarded by the given guard check. */
/**
* Gets an expression node that is safely guarded by the given guard check.
*
* For example, given the following code:
* ```cpp
* int x = source();
* // ...
* if(is_safe_int(x)) {
* sink(x);
* }
* ```
* and the following barrier guard predicate:
* ```ql
* predicate myGuardChecks(IRGuardCondition g, Expr e, boolean branch) {
* exists(Call call |
* g.getUnconvertedResultExpression() = call and
* call.getTarget().hasName("is_safe_int") and
* e = call.getAnArgument() and
* branch = true
* )
* }
* ```
* implementing `isBarrier` as:
* ```ql
* predicate isBarrier(DataFlow::Node barrier) {
* barrier = DataFlow::BarrierGuard<myGuardChecks/3>::getABarrierNode()
* }
* ```
* will block flow from `x = source()` to `sink(x)`.
*
* NOTE: If an indirect expression is tracked, use `getAnIndirectBarrierNode` instead.
*/
ExprNode getABarrierNode() {
exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge |
e = value.getAnInstruction().getConvertedResultExpression() and
@@ -1912,6 +1943,84 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
g.controls(result.getBasicBlock(), edge)
)
}
/**
* Gets an indirect expression node that is safely guarded by the given guard check.
*
* For example, given the following code:
* ```cpp
* int* p;
* // ...
* *p = source();
* if(is_safe_pointer(p)) {
* sink(*p);
* }
* ```
* and the following barrier guard check:
* ```ql
* predicate myGuardChecks(IRGuardCondition g, Expr e, boolean branch) {
* exists(Call call |
* g.getUnconvertedResultExpression() = call and
* call.getTarget().hasName("is_safe_pointer") and
* e = call.getAnArgument() and
* branch = true
* )
* }
* ```
* implementing `isBarrier` as:
* ```ql
* predicate isBarrier(DataFlow::Node barrier) {
* barrier = DataFlow::BarrierGuard<myGuardChecks/3>::getAnIndirectBarrierNode()
* }
* ```
* will block flow from `x = source()` to `sink(x)`.
*
* NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead.
*/
IndirectExprNode getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) }
/**
* Gets an indirect expression node with indirection index `indirectionIndex` that is
* safely guarded by the given guard check.
*
* For example, given the following code:
* ```cpp
* int* p;
* // ...
* *p = source();
* if(is_safe_pointer(p)) {
* sink(*p);
* }
* ```
* and the following barrier guard check:
* ```ql
* predicate myGuardChecks(IRGuardCondition g, Expr e, boolean branch) {
* exists(Call call |
* g.getUnconvertedResultExpression() = call and
* call.getTarget().hasName("is_safe_pointer") and
* e = call.getAnArgument() and
* branch = true
* )
* }
* ```
* implementing `isBarrier` as:
* ```ql
* predicate isBarrier(DataFlow::Node barrier) {
* barrier = DataFlow::BarrierGuard<myGuardChecks/3>::getAnIndirectBarrierNode(1)
* }
* ```
* will block flow from `x = source()` to `sink(x)`.
*
* NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead.
*/
IndirectExprNode getAnIndirectBarrierNode(int indirectionIndex) {
exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge |
e = value.getAnInstruction().getConvertedResultExpression() and
result.getConvertedExpr(indirectionIndex) = e and
guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and
g.controls(result.getBasicBlock(), edge)
)
}
}
/**

View File

@@ -145,14 +145,14 @@ private newtype TDefOrUseImpl =
or
// Since the pruning stage doesn't know about global variables we can't use the above check to
// rule out dead assignments to globals.
base.(VariableAddressInstruction).getAstVariable() instanceof Cpp::GlobalOrNamespaceVariable
base.(VariableAddressInstruction).getAstVariable() instanceof GlobalLikeVariable
)
} or
TUseImpl(Operand operand, int indirectionIndex) {
isUse(_, operand, _, _, indirectionIndex) and
not isDef(_, _, operand, _, _, _)
} or
TGlobalUse(Cpp::GlobalOrNamespaceVariable v, IRFunction f, int indirectionIndex) {
TGlobalUse(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
// Represents a final "use" of a global variable to ensure that
// the assignment to a global variable isn't ruled out as dead.
exists(VariableAddressInstruction vai, int defIndex |
@@ -162,7 +162,7 @@ private newtype TDefOrUseImpl =
indirectionIndex = [0 .. defIndex] + 1
)
} or
TGlobalDefImpl(Cpp::GlobalOrNamespaceVariable v, IRFunction f, int indirectionIndex) {
TGlobalDefImpl(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
// Represents the initial "definition" of a global variable when entering
// a function body.
exists(VariableAddressInstruction vai |
@@ -458,7 +458,7 @@ class FinalParameterUse extends UseImpl, TFinalParameterUse {
}
class GlobalUse extends UseImpl, TGlobalUse {
Cpp::GlobalOrNamespaceVariable global;
GlobalLikeVariable global;
IRFunction f;
GlobalUse() { this = TGlobalUse(global, f, ind) }
@@ -468,7 +468,7 @@ class GlobalUse extends UseImpl, TGlobalUse {
override int getIndirection() { result = ind + 1 }
/** Gets the global variable associated with this use. */
Cpp::GlobalOrNamespaceVariable getVariable() { result = global }
GlobalLikeVariable getVariable() { result = global }
/** Gets the `IRFunction` whose body is exited from after this use. */
IRFunction getIRFunction() { result = f }
@@ -496,14 +496,14 @@ class GlobalUse extends UseImpl, TGlobalUse {
}
class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl {
Cpp::GlobalOrNamespaceVariable global;
GlobalLikeVariable global;
IRFunction f;
int indirectionIndex;
GlobalDefImpl() { this = TGlobalDefImpl(global, f, indirectionIndex) }
/** Gets the global variable associated with this definition. */
Cpp::GlobalOrNamespaceVariable getVariable() { result = global }
GlobalLikeVariable getVariable() { result = global }
/** Gets the `IRFunction` whose body is evaluated after this definition. */
IRFunction getIRFunction() { result = f }
@@ -760,13 +760,14 @@ private predicate variableWriteCand(IRBlock bb, int i, SourceVariable v) {
}
private predicate sourceVariableIsGlobal(
SourceVariable sv, Cpp::GlobalOrNamespaceVariable global, IRFunction func, int indirectionIndex
SourceVariable sv, GlobalLikeVariable global, IRFunction func, int indirectionIndex
) {
exists(IRVariable irVar, BaseIRVariable base |
sourceVariableHasBaseAndIndex(sv, base, indirectionIndex) and
irVar = base.getIRVariable() and
irVar.getEnclosingIRFunction() = func and
global = irVar.getAst()
global = irVar.getAst() and
not irVar instanceof IRDynamicInitializationFlag
)
}
@@ -919,7 +920,7 @@ class GlobalDef extends TGlobalDef, SsaDefOrUse {
IRFunction getIRFunction() { result = global.getIRFunction() }
/** Gets the global variable associated with this definition. */
Cpp::GlobalOrNamespaceVariable getVariable() { result = global.getVariable() }
GlobalLikeVariable getVariable() { result = global.getVariable() }
}
class Phi extends TPhi, SsaDefOrUse {

View File

@@ -6,7 +6,7 @@ private import IRFunctionBaseInternal
private newtype TIRFunction =
TFunctionIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } or
TVarInitIRFunction(Language::GlobalVariable var) { IRConstruction::Raw::varHasIRFunc(var) }
TVarInitIRFunction(Language::Variable var) { IRConstruction::Raw::varHasIRFunc(var) }
/**
* The IR for a function. This base class contains only the predicates that are the same between all

View File

@@ -37,7 +37,13 @@ module Raw {
predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) }
cached
predicate varHasIRFunc(GlobalOrNamespaceVariable var) {
predicate varHasIRFunc(Variable var) {
(
var instanceof GlobalOrNamespaceVariable
or
not var.isFromUninstantiatedTemplate(_) and
var instanceof StaticInitializedStaticLocalVariable
) and
var.hasInitializer() and
(
not var.getType().isDeeplyConst()
@@ -75,9 +81,10 @@ module Raw {
}
cached
predicate hasDynamicInitializationFlag(Function func, StaticLocalVariable var, CppType type) {
predicate hasDynamicInitializationFlag(
Function func, RuntimeInitializedStaticLocalVariable var, CppType type
) {
var.getFunction() = func and
var.hasDynamicInitialization() and
type = getBoolType()
}

View File

@@ -180,7 +180,7 @@ abstract class TranslatedSideEffects extends TranslatedElement {
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
final override Declaration getFunction() { result = getExpr().getEnclosingDeclaration() }
final override Declaration getFunction() { result = getEnclosingDeclaration(getExpr()) }
final override TranslatedElement getChild(int i) {
result =

View File

@@ -28,7 +28,11 @@ abstract class TranslatedCondition extends TranslatedElement {
final Expr getExpr() { result = expr }
final override Function getFunction() { result = expr.getEnclosingFunction() }
final override Declaration getFunction() {
result = getEnclosingFunction(expr) or
result = getEnclosingVariable(expr).(GlobalOrNamespaceVariable) or
result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable)
}
final Type getResultType() { result = expr.getUnspecifiedType() }
}

View File

@@ -28,9 +28,14 @@ abstract class TranslatedDeclarationEntry extends TranslatedElement, TTranslated
TranslatedDeclarationEntry() { this = TTranslatedDeclarationEntry(entry) }
final override Function getFunction() {
exists(DeclStmt stmt |
stmt = entry.getStmt() and
final override Declaration getFunction() {
exists(DeclStmt stmt | stmt = entry.getStmt() |
result = entry.getDeclaration().(StaticInitializedStaticLocalVariable)
or
result = entry.getDeclaration().(GlobalOrNamespaceVariable)
or
not entry.getDeclaration() instanceof StaticInitializedStaticLocalVariable and
not entry.getDeclaration() instanceof GlobalOrNamespaceVariable and
result = stmt.getEnclosingFunction()
)
}
@@ -237,7 +242,7 @@ class TranslatedStaticLocalVariableInitialization extends TranslatedElement,
final override LocalVariable getVariable() { result = var }
final override Function getFunction() { result = var.getFunction() }
final override Declaration getFunction() { result = var.getFunction() }
}
TranslatedConditionDecl getTranslatedConditionDecl(ConditionDeclExpr expr) {
@@ -264,7 +269,7 @@ class TranslatedConditionDecl extends TranslatedLocalVariableDeclaration, TTrans
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
override Function getFunction() { result = conditionDeclExpr.getEnclosingFunction() }
override Declaration getFunction() { result = getEnclosingFunction(conditionDeclExpr) }
override LocalVariable getVariable() { result = conditionDeclExpr.getVariable() }
}

View File

@@ -62,15 +62,6 @@ private predicate ignoreExprAndDescendants(Expr expr) {
// constant value.
isIRConstant(getRealParent(expr))
or
// Only translate the initializer of a static local if it uses run-time data.
// Otherwise the initializer does not run in function scope.
exists(Initializer init, StaticStorageDurationVariable var |
init = var.getInitializer() and
not var.hasDynamicInitialization() and
expr = init.getExpr().getFullyConverted() and
not var instanceof GlobalOrNamespaceVariable
)
or
// Ignore descendants of `__assume` expressions, since we translated these to `NoOp`.
getRealParent(expr) instanceof AssumeExpr
or
@@ -118,8 +109,8 @@ private predicate ignoreExprOnly(Expr expr) {
// should not be translated.
exists(NewOrNewArrayExpr new | expr = new.getAllocatorCall().getArgument(0))
or
not translateFunction(expr.getEnclosingFunction()) and
not Raw::varHasIRFunc(expr.getEnclosingVariable())
not translateFunction(getEnclosingFunction(expr)) and
not Raw::varHasIRFunc(getEnclosingVariable(expr))
or
// We do not yet translate destructors properly, so for now we ignore the
// destructor call. We do, however, translate the expression being
@@ -438,6 +429,17 @@ predicate hasTranslatedSyntheticTemporaryObject(Expr expr) {
not expr.hasLValueToRValueConversion()
}
class StaticInitializedStaticLocalVariable extends StaticLocalVariable {
StaticInitializedStaticLocalVariable() {
this.hasInitializer() and
not this.hasDynamicInitialization()
}
}
class RuntimeInitializedStaticLocalVariable extends StaticLocalVariable {
RuntimeInitializedStaticLocalVariable() { this.hasDynamicInitialization() }
}
/**
* Holds if the specified `DeclarationEntry` needs an IR translation. An IR translation is only
* necessary for automatic local variables, or for static local variables with dynamic
@@ -453,7 +455,7 @@ private predicate translateDeclarationEntry(IRDeclarationEntry entry) {
not var.isStatic()
or
// Ignore static variables unless they have a dynamic initializer.
var.(StaticLocalVariable).hasDynamicInitialization()
var instanceof RuntimeInitializedStaticLocalVariable
)
)
}
@@ -755,7 +757,7 @@ newtype TTranslatedElement =
} or
// The side effect that initializes newly-allocated memory.
TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) } or
TTranslatedGlobalOrNamespaceVarInit(GlobalOrNamespaceVariable var) { Raw::varHasIRFunc(var) }
TTranslatedStaticStorageDurationVarInit(Variable var) { Raw::varHasIRFunc(var) }
/**
* Gets the index of the first explicitly initialized element in `initList`
@@ -819,7 +821,7 @@ abstract class TranslatedElement extends TTranslatedElement {
abstract Locatable getAst();
/** DEPRECATED: Alias for getAst */
deprecated Locatable getAST() { result = getAst() }
deprecated Locatable getAST() { result = this.getAst() }
/**
* Get the first instruction to be executed in the evaluation of this element.
@@ -829,7 +831,7 @@ abstract class TranslatedElement extends TTranslatedElement {
/**
* Get the immediate child elements of this element.
*/
final TranslatedElement getAChild() { result = getChild(_) }
final TranslatedElement getAChild() { result = this.getChild(_) }
/**
* Gets the immediate child element of this element. The `id` is unique
@@ -842,25 +844,29 @@ abstract class TranslatedElement extends TTranslatedElement {
* Gets the an identifier string for the element. This id is unique within
* the scope of the element's function.
*/
final int getId() { result = getUniqueId() }
final int getId() { result = this.getUniqueId() }
private TranslatedElement getChildByRank(int rankIndex) {
result =
rank[rankIndex + 1](TranslatedElement child, int id | child = getChild(id) | child order by id)
rank[rankIndex + 1](TranslatedElement child, int id |
child = this.getChild(id)
|
child order by id
)
}
language[monotonicAggregates]
private int getDescendantCount() {
result =
1 + sum(TranslatedElement child | child = getChildByRank(_) | child.getDescendantCount())
1 + sum(TranslatedElement child | child = this.getChildByRank(_) | child.getDescendantCount())
}
private int getUniqueId() {
if not exists(getParent())
if not exists(this.getParent())
then result = 0
else
exists(TranslatedElement parent |
parent = getParent() and
parent = this.getParent() and
if this = parent.getChildByRank(0)
then result = 1 + parent.getUniqueId()
else
@@ -906,7 +912,7 @@ abstract class TranslatedElement extends TTranslatedElement {
* there is no enclosing `try`.
*/
Instruction getExceptionSuccessorInstruction() {
result = getParent().getExceptionSuccessorInstruction()
result = this.getParent().getExceptionSuccessorInstruction()
}
/**
@@ -1020,14 +1026,14 @@ abstract class TranslatedElement extends TTranslatedElement {
exists(Locatable ast |
result.getAst() = ast and
result.getTag() = tag and
hasTempVariableAndAst(tag, ast)
this.hasTempVariableAndAst(tag, ast)
)
}
pragma[noinline]
private predicate hasTempVariableAndAst(TempVariableTag tag, Locatable ast) {
hasTempVariable(tag, _) and
ast = getAst()
this.hasTempVariable(tag, _) and
ast = this.getAst()
}
/**
@@ -1043,6 +1049,6 @@ abstract class TranslatedRootElement extends TranslatedElement {
TranslatedRootElement() {
this instanceof TTranslatedFunction
or
this instanceof TTranslatedGlobalOrNamespaceVarInit
this instanceof TTranslatedStaticStorageDurationVarInit
}
}

View File

@@ -79,7 +79,7 @@ abstract class TranslatedExpr extends TranslatedElement {
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = this.getAst() }
final override Declaration getFunction() { result = expr.getEnclosingDeclaration() }
final override Declaration getFunction() { result = getEnclosingDeclaration(expr) }
/**
* Gets the expression from which this `TranslatedExpr` is generated.
@@ -90,12 +90,57 @@ abstract class TranslatedExpr extends TranslatedElement {
* Gets the `TranslatedFunction` containing this expression.
*/
final TranslatedRootElement getEnclosingFunction() {
result = getTranslatedFunction(expr.getEnclosingFunction())
result = getTranslatedFunction(getEnclosingFunction(expr))
or
result = getTranslatedVarInit(expr.getEnclosingVariable())
result = getTranslatedVarInit(getEnclosingVariable(expr))
}
}
Function getEnclosingFunction(Expr e) {
not exists(getEnclosingVariable(e)) and
result = e.getEnclosingFunction()
}
Declaration getEnclosingDeclaration0(Expr e) {
result = getEnclosingDeclaration0(e.getParentWithConversions())
or
exists(Initializer i, Variable v |
i.getExpr().getFullyConverted() = e and
v = i.getDeclaration()
|
if v instanceof StaticInitializedStaticLocalVariable or v instanceof GlobalOrNamespaceVariable
then result = v
else result = e.getEnclosingDeclaration()
)
}
Declaration getEnclosingDeclaration(Expr e) {
result = getEnclosingDeclaration0(e)
or
not exists(getEnclosingDeclaration0(e)) and
result = e.getEnclosingDeclaration()
}
Variable getEnclosingVariable0(Expr e) {
result = getEnclosingVariable0(e.getParentWithConversions())
or
exists(Initializer i, Variable v |
i.getExpr().getFullyConverted() = e and
v = i.getDeclaration()
|
if v instanceof StaticInitializedStaticLocalVariable or v instanceof GlobalOrNamespaceVariable
then result = v
else result = e.getEnclosingVariable()
)
}
Variable getEnclosingVariable(Expr e) {
result = getEnclosingVariable0(e)
or
not exists(getEnclosingVariable0(e)) and
result = e.getEnclosingVariable()
}
/**
* The IR translation of the "core" part of an expression. This is the part of
* the expression that produces the result value of the expression, before any
@@ -843,10 +888,21 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess {
override IRVariable getInstructionVariable(InstructionTag tag) {
tag = OnlyInstructionTag() and
result = getIRUserVariable(expr.getEnclosingDeclaration(), expr.getTarget())
exists(Declaration d, Variable v |
accessHasEnclosingDeclarationAndVariable(d, v, expr) and
result = getIRUserVariable(d, v)
)
}
}
pragma[nomagic]
private predicate accessHasEnclosingDeclarationAndVariable(
Declaration d, Variable v, VariableAccess va
) {
d = getEnclosingDeclaration(va) and
v = va.getTarget()
}
class TranslatedFieldAccess extends TranslatedVariableAccess {
override FieldAccess expr;
@@ -2000,7 +2056,7 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, St
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getTranslatedFunction(expr.getEnclosingFunction()).getInitializeThisInstruction()
result = getTranslatedFunction(getEnclosingFunction(expr)).getInitializeThisInstruction()
}
final override Field getInstructionField(InstructionTag tag) {

View File

@@ -322,11 +322,13 @@ class TranslatedFunction extends TranslatedRootElement, TTranslatedFunction {
(
var instanceof GlobalOrNamespaceVariable
or
var instanceof StaticLocalVariable
or
var instanceof MemberVariable and not var instanceof Field
) and
exists(VariableAccess access |
access.getTarget() = var and
access.getEnclosingFunction() = func
getEnclosingFunction(access) = func
)
or
var.(LocalScopeVariable).getFunction() = func

View File

@@ -1,4 +1,5 @@
import semmle.code.cpp.ir.implementation.raw.internal.TranslatedElement
private import TranslatedExpr
private import cpp
private import semmle.code.cpp.ir.implementation.IRType
private import semmle.code.cpp.ir.implementation.Opcode
@@ -8,16 +9,16 @@ private import TranslatedInitialization
private import InstructionTag
private import semmle.code.cpp.ir.internal.IRUtilities
class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement,
TTranslatedGlobalOrNamespaceVarInit, InitializationContext
class TranslatedStaticStorageDurationVarInit extends TranslatedRootElement,
TTranslatedStaticStorageDurationVarInit, InitializationContext
{
GlobalOrNamespaceVariable var;
Variable var;
TranslatedGlobalOrNamespaceVarInit() { this = TTranslatedGlobalOrNamespaceVarInit(var) }
TranslatedStaticStorageDurationVarInit() { this = TTranslatedStaticStorageDurationVarInit(var) }
override string toString() { result = var.toString() }
final override GlobalOrNamespaceVariable getAst() { result = var }
final override Variable getAst() { result = var }
final override Declaration getFunction() { result = var }
@@ -111,11 +112,13 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement,
(
varUsed instanceof GlobalOrNamespaceVariable
or
varUsed instanceof StaticLocalVariable
or
varUsed instanceof MemberVariable and not varUsed instanceof Field
) and
exists(VariableAccess access |
access.getTarget() = varUsed and
access.getEnclosingVariable() = var
getEnclosingVariable(access) = var
)
or
var = varUsed
@@ -128,6 +131,4 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement,
}
}
TranslatedGlobalOrNamespaceVarInit getTranslatedVarInit(GlobalOrNamespaceVariable var) {
result.getAst() = var
}
TranslatedStaticStorageDurationVarInit getTranslatedVarInit(Variable var) { result.getAst() = var }

View File

@@ -35,64 +35,64 @@ abstract class InitializationContext extends TranslatedElement {
* declarations, `return` statements, and `throw` expressions.
*/
abstract class TranslatedVariableInitialization extends TranslatedElement, InitializationContext {
final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
final override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() }
final override Instruction getFirstInstruction() {
result = getInstruction(InitializerVariableAddressTag())
result = this.getInstruction(InitializerVariableAddressTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
resultType = getTypeForGLValue(getTargetType())
resultType = getTypeForGLValue(this.getTargetType())
or
hasUninitializedInstruction() and
this.hasUninitializedInstruction() and
tag = InitializerStoreTag() and
opcode instanceof Opcode::Uninitialized and
resultType = getTypeForPRValue(getTargetType())
resultType = getTypeForPRValue(this.getTargetType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
(
tag = InitializerVariableAddressTag() and
kind instanceof GotoEdge and
if hasUninitializedInstruction()
then result = getInstruction(InitializerStoreTag())
else result = getInitialization().getFirstInstruction()
if this.hasUninitializedInstruction()
then result = this.getInstruction(InitializerStoreTag())
else result = this.getInitialization().getFirstInstruction()
)
or
hasUninitializedInstruction() and
this.hasUninitializedInstruction() and
kind instanceof GotoEdge and
tag = InitializerStoreTag() and
(
result = getInitialization().getFirstInstruction()
result = this.getInitialization().getFirstInstruction()
or
not exists(getInitialization()) and result = getInitializationSuccessor()
not exists(this.getInitialization()) and result = this.getInitializationSuccessor()
)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and result = getInitializationSuccessor()
child = this.getInitialization() and result = this.getInitializationSuccessor()
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
hasUninitializedInstruction() and
this.hasUninitializedInstruction() and
tag = InitializerStoreTag() and
operandTag instanceof AddressOperandTag and
result = getInstruction(InitializerVariableAddressTag())
result = this.getInstruction(InitializerVariableAddressTag())
}
final override IRVariable getInstructionVariable(InstructionTag tag) {
(
tag = InitializerVariableAddressTag()
or
hasUninitializedInstruction() and tag = InitializerStoreTag()
this.hasUninitializedInstruction() and tag = InitializerStoreTag()
) and
result = getIRVariable()
result = this.getIRVariable()
}
final override Instruction getTargetAddress() {
result = getInstruction(InitializerVariableAddressTag())
result = this.getInstruction(InitializerVariableAddressTag())
}
/**
@@ -116,13 +116,13 @@ abstract class TranslatedVariableInitialization extends TranslatedElement, Initi
*/
final predicate hasUninitializedInstruction() {
(
not exists(getInitialization()) or
getInitialization() instanceof TranslatedListInitialization or
getInitialization() instanceof TranslatedConstructorInitialization or
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
not exists(this.getInitialization()) or
this.getInitialization() instanceof TranslatedListInitialization or
this.getInitialization() instanceof TranslatedConstructorInitialization or
this.getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
) and
// Variables with static or thread-local storage duration are zero-initialized at program startup.
getIRVariable() instanceof IRAutomaticVariable
this.getIRVariable() instanceof IRAutomaticVariable
}
}
@@ -138,14 +138,15 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn
final override string toString() { result = "init: " + expr.toString() }
final override Declaration getFunction() {
result = expr.getEnclosingFunction() or
result = expr.getEnclosingVariable().(GlobalOrNamespaceVariable)
result = getEnclosingFunction(expr) or
result = getEnclosingVariable(expr).(GlobalOrNamespaceVariable) or
result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable)
}
final override Locatable getAst() { result = expr }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
/**
* Gets the expression that is doing the initialization.
@@ -156,10 +157,10 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn
* Gets the initialization context that describes the location being
* initialized.
*/
final InitializationContext getContext() { result = getParent() }
final InitializationContext getContext() { result = this.getParent() }
final TranslatedFunction getEnclosingFunction() {
result = getTranslatedFunction(expr.getEnclosingFunction())
result = getTranslatedFunction(this.getFunction())
}
}
@@ -168,17 +169,17 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn
*/
abstract class TranslatedListInitialization extends TranslatedInitialization, InitializationContext {
override Instruction getFirstInstruction() {
result = getChild(0).getFirstInstruction()
result = this.getChild(0).getFirstInstruction()
or
not exists(getChild(0)) and result = getParent().getChildSuccessor(this)
not exists(this.getChild(0)) and result = this.getParent().getChildSuccessor(this)
}
override Instruction getChildSuccessor(TranslatedElement child) {
exists(int index |
child = getChild(index) and
if exists(getChild(index + 1))
then result = getChild(index + 1).getFirstInstruction()
else result = getParent().getChildSuccessor(this)
child = this.getChild(index) and
if exists(this.getChild(index + 1))
then result = this.getChild(index + 1).getFirstInstruction()
else result = this.getParent().getChildSuccessor(this)
)
}
@@ -188,9 +189,9 @@ abstract class TranslatedListInitialization extends TranslatedInitialization, In
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getTargetAddress() { result = getContext().getTargetAddress() }
override Instruction getTargetAddress() { result = this.getContext().getTargetAddress() }
override Type getTargetType() { result = getContext().getTargetType() }
override Type getTargetType() { result = this.getContext().getTargetType() }
}
/**
@@ -236,9 +237,11 @@ class TranslatedArrayListInitialization extends TranslatedListInitialization {
abstract class TranslatedDirectInitialization extends TranslatedInitialization {
TranslatedDirectInitialization() { not expr instanceof AggregateLiteral }
override TranslatedElement getChild(int id) { id = 0 and result = getInitializer() }
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitializer() }
override Instruction getFirstInstruction() { result = getInitializer().getFirstInstruction() }
override Instruction getFirstInstruction() {
result = this.getInitializer().getFirstInstruction()
}
final TranslatedExpr getInitializer() { result = getTranslatedExpr(expr) }
}
@@ -257,27 +260,27 @@ class TranslatedSimpleDirectInitialization extends TranslatedDirectInitializatio
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(getContext().getTargetType())
resultType = getTypeForPRValue(this.getContext().getTargetType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = InitializerStoreTag() and
result = getParent().getChildSuccessor(this) and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitializer() and result = getInstruction(InitializerStoreTag())
child = this.getInitializer() and result = this.getInstruction(InitializerStoreTag())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = InitializerStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getContext().getTargetAddress()
result = this.getContext().getTargetAddress()
or
operandTag instanceof StoreValueOperandTag and
result = getInitializer().getResult()
result = this.getInitializer().getResult()
)
}
}
@@ -304,13 +307,13 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
// If the initializer string isn't large enough to fill the target, then
// we have to generate another instruction sequence to store a constant
// zero into the remainder of the array.
zeroInitRange(_, elementCount) and
this.zeroInitRange(_, elementCount) and
(
// Create a constant zero whose size is the size of the remaining
// space in the target array.
tag = ZeroPadStringConstantTag() and
opcode instanceof Opcode::Constant and
resultType = getUnknownOpaqueType(elementCount * getElementType().getSize())
resultType = getUnknownOpaqueType(elementCount * this.getElementType().getSize())
or
// The index of the first element to be zero initialized.
tag = ZeroPadStringElementIndexTag() and
@@ -320,12 +323,12 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
// Compute the address of the first element to be zero initialized.
tag = ZeroPadStringElementAddressTag() and
opcode instanceof Opcode::PointerAdd and
resultType = getTypeForGLValue(getElementType())
resultType = getTypeForGLValue(this.getElementType())
or
// Store the constant zero into the remainder of the string.
tag = ZeroPadStringStoreTag() and
opcode instanceof Opcode::Store and
resultType = getUnknownOpaqueType(elementCount * getElementType().getSize())
resultType = getUnknownOpaqueType(elementCount * this.getElementType().getSize())
)
)
}
@@ -334,78 +337,78 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
kind instanceof GotoEdge and
(
tag = InitializerLoadStringTag() and
result = getInstruction(InitializerStoreTag())
result = this.getInstruction(InitializerStoreTag())
or
if zeroInitRange(_, _)
if this.zeroInitRange(_, _)
then (
tag = InitializerStoreTag() and
result = getInstruction(ZeroPadStringConstantTag())
result = this.getInstruction(ZeroPadStringConstantTag())
or
tag = ZeroPadStringConstantTag() and
result = getInstruction(ZeroPadStringElementIndexTag())
result = this.getInstruction(ZeroPadStringElementIndexTag())
or
tag = ZeroPadStringElementIndexTag() and
result = getInstruction(ZeroPadStringElementAddressTag())
result = this.getInstruction(ZeroPadStringElementAddressTag())
or
tag = ZeroPadStringElementAddressTag() and
result = getInstruction(ZeroPadStringStoreTag())
result = this.getInstruction(ZeroPadStringStoreTag())
or
tag = ZeroPadStringStoreTag() and
result = getParent().getChildSuccessor(this)
result = this.getParent().getChildSuccessor(this)
) else (
tag = InitializerStoreTag() and
result = getParent().getChildSuccessor(this)
result = this.getParent().getChildSuccessor(this)
)
)
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitializer() and result = getInstruction(InitializerLoadStringTag())
child = this.getInitializer() and result = this.getInstruction(InitializerLoadStringTag())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = InitializerLoadStringTag() and
(
operandTag instanceof AddressOperandTag and
result = getInitializer().getResult()
result = this.getInitializer().getResult()
)
or
tag = InitializerStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getContext().getTargetAddress()
result = this.getContext().getTargetAddress()
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(InitializerLoadStringTag())
result = this.getInstruction(InitializerLoadStringTag())
)
or
tag = ZeroPadStringElementAddressTag() and
(
operandTag instanceof LeftOperandTag and
result = getContext().getTargetAddress()
result = this.getContext().getTargetAddress()
or
operandTag instanceof RightOperandTag and
result = getInstruction(ZeroPadStringElementIndexTag())
result = this.getInstruction(ZeroPadStringElementIndexTag())
)
or
tag = ZeroPadStringStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(ZeroPadStringElementAddressTag())
result = this.getInstruction(ZeroPadStringElementAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(ZeroPadStringConstantTag())
result = this.getInstruction(ZeroPadStringConstantTag())
)
}
override int getInstructionElementSize(InstructionTag tag) {
tag = ZeroPadStringElementAddressTag() and
result = max(getElementType().getSize())
result = max(this.getElementType().getSize())
}
override string getInstructionConstantValue(InstructionTag tag) {
exists(int startIndex |
zeroInitRange(startIndex, _) and
this.zeroInitRange(startIndex, _) and
(
tag = ZeroPadStringConstantTag() and
result = "0"
@@ -418,13 +421,13 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
override predicate needsUnknownOpaqueType(int byteSize) {
exists(int elementCount |
zeroInitRange(_, elementCount) and
byteSize = elementCount * getElementType().getSize()
this.zeroInitRange(_, elementCount) and
byteSize = elementCount * this.getElementType().getSize()
)
}
private Type getElementType() {
result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
result = this.getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
}
/**
@@ -434,7 +437,8 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
predicate zeroInitRange(int startIndex, int elementCount) {
exists(int targetCount |
startIndex = expr.getUnspecifiedType().(ArrayType).getArraySize() and
targetCount = getContext().getTargetType().getUnspecifiedType().(ArrayType).getArraySize() and
targetCount =
this.getContext().getTargetType().getUnspecifiedType().(ArrayType).getArraySize() and
elementCount = targetCount - startIndex and
elementCount > 0
)
@@ -453,14 +457,14 @@ class TranslatedConstructorInitialization extends TranslatedDirectInitialization
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitializer() and result = getParent().getChildSuccessor(this)
child = this.getInitializer() and result = this.getParent().getChildSuccessor(this)
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
none()
}
override Instruction getReceiver() { result = getContext().getTargetAddress() }
override Instruction getReceiver() { result = this.getContext().getTargetAddress() }
}
/**
@@ -490,14 +494,17 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
final override Locatable getAst() { result = ast }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override Declaration getFunction() {
result = ast.getEnclosingFunction() or
result = ast.getEnclosingVariable().(GlobalOrNamespaceVariable)
result = getEnclosingFunction(ast) or
result = getEnclosingVariable(ast).(GlobalOrNamespaceVariable) or
result = getEnclosingVariable(ast).(StaticInitializedStaticLocalVariable)
}
final override Instruction getFirstInstruction() { result = getInstruction(getFieldAddressTag()) }
final override Instruction getFirstInstruction() {
result = this.getInstruction(this.getFieldAddressTag())
}
/**
* Gets the zero-based index describing the order in which this field is to be
@@ -506,19 +513,19 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
final int getOrder() { result = field.getInitializationOrder() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = getFieldAddressTag() and
tag = this.getFieldAddressTag() and
opcode instanceof Opcode::FieldAddress and
resultType = getTypeForGLValue(field.getType())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = getFieldAddressTag() and
tag = this.getFieldAddressTag() and
operandTag instanceof UnaryOperandTag and
result = getParent().(InitializationContext).getTargetAddress()
result = this.getParent().(InitializationContext).getTargetAddress()
}
override Field getInstructionField(InstructionTag tag) {
tag = getFieldAddressTag() and result = field
tag = this.getFieldAddressTag() and result = field
}
final InstructionTag getFieldAddressTag() { result = InitializerFieldAddressTag() }
@@ -543,21 +550,23 @@ class TranslatedExplicitFieldInitialization extends TranslatedFieldInitializatio
this = TTranslatedExplicitFieldInitialization(ast, field, expr, position)
}
override Instruction getTargetAddress() { result = getInstruction(getFieldAddressTag()) }
override Instruction getTargetAddress() {
result = this.getInstruction(this.getFieldAddressTag())
}
override Type getTargetType() { result = field.getUnspecifiedType() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = getFieldAddressTag() and
result = getInitialization().getFirstInstruction() and
tag = this.getFieldAddressTag() and
result = this.getInitialization().getFirstInstruction() and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and result = getParent().getChildSuccessor(this)
child = this.getInitialization() and result = this.getParent().getChildSuccessor(this)
}
override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() }
private TranslatedInitialization getInitialization() {
result = getTranslatedInitialization(expr)
@@ -582,11 +591,11 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType)
or
tag = getFieldDefaultValueTag() and
tag = this.getFieldDefaultValueTag() and
opcode instanceof Opcode::Constant and
resultType = getTypeForPRValue(field.getType())
or
tag = getFieldDefaultValueStoreTag() and
tag = this.getFieldDefaultValueStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(field.getUnspecifiedType())
}
@@ -594,32 +603,32 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
(
tag = getFieldAddressTag() and
result = getInstruction(getFieldDefaultValueTag())
tag = this.getFieldAddressTag() and
result = this.getInstruction(this.getFieldDefaultValueTag())
or
tag = getFieldDefaultValueTag() and
result = getInstruction(getFieldDefaultValueStoreTag())
tag = this.getFieldDefaultValueTag() and
result = this.getInstruction(this.getFieldDefaultValueStoreTag())
or
tag = getFieldDefaultValueStoreTag() and
result = getParent().getChildSuccessor(this)
tag = this.getFieldDefaultValueStoreTag() and
result = this.getParent().getChildSuccessor(this)
)
}
override string getInstructionConstantValue(InstructionTag tag) {
tag = getFieldDefaultValueTag() and
tag = this.getFieldDefaultValueTag() and
result = getZeroValue(field.getUnspecifiedType())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
result = TranslatedFieldInitialization.super.getInstructionRegisterOperand(tag, operandTag)
or
tag = getFieldDefaultValueStoreTag() and
tag = this.getFieldDefaultValueStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(getFieldAddressTag())
result = this.getInstruction(this.getFieldAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(getFieldDefaultValueTag())
result = this.getInstruction(this.getFieldDefaultValueTag())
)
}
@@ -642,57 +651,61 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
ArrayOrVectorAggregateLiteral initList;
final override string toString() {
result = initList.toString() + "[" + getElementIndex().toString() + "]"
result = initList.toString() + "[" + this.getElementIndex().toString() + "]"
}
final override Locatable getAst() { result = initList }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override Declaration getFunction() {
result = initList.getEnclosingFunction()
result = getEnclosingFunction(initList)
or
result = initList.getEnclosingVariable().(GlobalOrNamespaceVariable)
result = getEnclosingVariable(initList).(GlobalOrNamespaceVariable)
or
result = getEnclosingVariable(initList).(StaticInitializedStaticLocalVariable)
}
final override Instruction getFirstInstruction() { result = getInstruction(getElementIndexTag()) }
final override Instruction getFirstInstruction() {
result = this.getInstruction(this.getElementIndexTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = getElementIndexTag() and
tag = this.getElementIndexTag() and
opcode instanceof Opcode::Constant and
resultType = getIntType()
or
tag = getElementAddressTag() and
tag = this.getElementAddressTag() and
opcode instanceof Opcode::PointerAdd and
resultType = getTypeForGLValue(getElementType())
resultType = getTypeForGLValue(this.getElementType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = getElementIndexTag() and
result = getInstruction(getElementAddressTag()) and
tag = this.getElementIndexTag() and
result = this.getInstruction(this.getElementAddressTag()) and
kind instanceof GotoEdge
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = getElementAddressTag() and
tag = this.getElementAddressTag() and
(
operandTag instanceof LeftOperandTag and
result = getParent().(InitializationContext).getTargetAddress()
result = this.getParent().(InitializationContext).getTargetAddress()
or
operandTag instanceof RightOperandTag and
result = getInstruction(getElementIndexTag())
result = this.getInstruction(this.getElementIndexTag())
)
}
override int getInstructionElementSize(InstructionTag tag) {
tag = getElementAddressTag() and
result = max(getElementType().getSize())
tag = this.getElementAddressTag() and
result = max(this.getElementType().getSize())
}
override string getInstructionConstantValue(InstructionTag tag) {
tag = getElementIndexTag() and
result = getElementIndex().toString()
tag = this.getElementIndexTag() and
result = this.getElementIndex().toString()
}
abstract int getElementIndex();
@@ -722,23 +735,25 @@ class TranslatedExplicitElementInitialization extends TranslatedElementInitializ
this = TTranslatedExplicitElementInitialization(initList, elementIndex, position)
}
override Instruction getTargetAddress() { result = getInstruction(getElementAddressTag()) }
override Instruction getTargetAddress() {
result = this.getInstruction(this.getElementAddressTag())
}
override Type getTargetType() { result = getElementType() }
override Type getTargetType() { result = this.getElementType() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind)
or
tag = getElementAddressTag() and
result = getInitialization().getFirstInstruction() and
tag = this.getElementAddressTag() and
result = this.getInitialization().getFirstInstruction() and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and result = getParent().getChildSuccessor(this)
child = this.getInitialization() and result = this.getParent().getChildSuccessor(this)
}
override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() }
override int getElementIndex() { result = elementIndex }
@@ -769,13 +784,13 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType)
or
tag = getElementDefaultValueTag() and
tag = this.getElementDefaultValueTag() and
opcode instanceof Opcode::Constant and
resultType = getDefaultValueType()
resultType = this.getDefaultValueType()
or
tag = getElementDefaultValueStoreTag() and
tag = this.getElementDefaultValueStoreTag() and
opcode instanceof Opcode::Store and
resultType = getDefaultValueType()
resultType = this.getDefaultValueType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -783,34 +798,34 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
or
kind instanceof GotoEdge and
(
tag = getElementAddressTag() and
result = getInstruction(getElementDefaultValueTag())
tag = this.getElementAddressTag() and
result = this.getInstruction(this.getElementDefaultValueTag())
or
tag = getElementDefaultValueTag() and
result = getInstruction(getElementDefaultValueStoreTag())
tag = this.getElementDefaultValueTag() and
result = this.getInstruction(this.getElementDefaultValueStoreTag())
or
tag = getElementDefaultValueStoreTag() and
result = getParent().getChildSuccessor(this)
tag = this.getElementDefaultValueStoreTag() and
result = this.getParent().getChildSuccessor(this)
)
}
override string getInstructionConstantValue(InstructionTag tag) {
result = TranslatedElementInitialization.super.getInstructionConstantValue(tag)
or
tag = getElementDefaultValueTag() and
result = getZeroValue(getElementType())
tag = this.getElementDefaultValueTag() and
result = getZeroValue(this.getElementType())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
result = TranslatedElementInitialization.super.getInstructionRegisterOperand(tag, operandTag)
or
tag = getElementDefaultValueStoreTag() and
tag = this.getElementDefaultValueStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(getElementAddressTag())
result = this.getInstruction(this.getElementAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(getElementDefaultValueTag())
result = this.getInstruction(this.getElementDefaultValueTag())
)
}
@@ -821,7 +836,7 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
override int getElementIndex() { result = elementIndex }
override predicate needsUnknownOpaqueType(int byteSize) {
elementCount != 0 and byteSize = elementCount * getElementType().getSize()
elementCount != 0 and byteSize = elementCount * this.getElementType().getSize()
}
private InstructionTag getElementDefaultValueTag() {
@@ -834,8 +849,8 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
private CppType getDefaultValueType() {
if elementCount = 1
then result = getTypeForPRValue(getElementType())
else result = getUnknownOpaqueType(elementCount * getElementType().getSize())
then result = getTypeForPRValue(this.getElementType())
else result = getUnknownOpaqueType(elementCount * this.getElementType().getSize())
}
}
@@ -845,18 +860,18 @@ abstract class TranslatedStructorCallFromStructor extends TranslatedElement, Str
final override Locatable getAst() { result = call }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override TranslatedElement getChild(int id) {
id = 0 and
result = getStructorCall()
result = this.getStructorCall()
}
final override Function getFunction() { result = call.getEnclosingFunction() }
final override Function getFunction() { result = getEnclosingFunction(call) }
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getStructorCall() and
result = getParent().getChildSuccessor(this)
child = this.getStructorCall() and
result = this.getParent().getChildSuccessor(this)
}
final TranslatedExpr getStructorCall() { result = getTranslatedExpr(call) }
@@ -867,7 +882,9 @@ abstract class TranslatedStructorCallFromStructor extends TranslatedElement, Str
* destructor from within a derived class constructor or destructor.
*/
abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStructor {
final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
final override Instruction getFirstInstruction() {
result = this.getInstruction(OnlyInstructionTag())
}
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -878,15 +895,15 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
kind instanceof GotoEdge and
result = getStructorCall().getFirstInstruction()
result = this.getStructorCall().getFirstInstruction()
}
final override Instruction getReceiver() { result = getInstruction(OnlyInstructionTag()) }
final override Instruction getReceiver() { result = this.getInstruction(OnlyInstructionTag()) }
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getTranslatedFunction(getFunction()).getInitializeThisInstruction()
result = getTranslatedFunction(this.getFunction()).getInitializeThisInstruction()
}
final override predicate getInstructionInheritance(
@@ -894,7 +911,7 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
) {
tag = OnlyInstructionTag() and
baseClass = call.getTarget().getDeclaringType().getUnspecifiedType() and
derivedClass = getFunction().getDeclaringType().getUnspecifiedType()
derivedClass = this.getFunction().getDeclaringType().getUnspecifiedType()
}
}
@@ -920,7 +937,7 @@ class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromC
final override string toString() { result = "delegation construct: " + call.toString() }
final override Instruction getFirstInstruction() {
result = getStructorCall().getFirstInstruction()
result = this.getStructorCall().getFirstInstruction()
}
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -930,7 +947,7 @@ class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromC
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
final override Instruction getReceiver() {
result = getTranslatedFunction(getFunction()).getInitializeThisInstruction()
result = getTranslatedFunction(this.getFunction()).getInitializeThisInstruction()
}
}
@@ -977,11 +994,11 @@ class TranslatedConstructorBareInit extends TranslatedElement, TTranslatedConstr
override Locatable getAst() { result = init }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override string toString() { result = "construct base (no constructor)" }
override Instruction getFirstInstruction() { result = getParent().getChildSuccessor(this) }
override Instruction getFirstInstruction() { result = this.getParent().getChildSuccessor(this) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
@@ -989,7 +1006,7 @@ class TranslatedConstructorBareInit extends TranslatedElement, TTranslatedConstr
override TranslatedElement getChild(int id) { none() }
override Function getFunction() { result = getParent().getFunction() }
override Declaration getFunction() { result = this.getParent().getFunction() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }

View File

@@ -240,7 +240,7 @@ abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt {
final override Locatable getAst() { result = stmt }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override Function getFunction() { result = stmt.getEnclosingFunction() }
}
@@ -254,7 +254,7 @@ class TranslatedEmptyStmt extends TranslatedStmt {
override TranslatedElement getChild(int id) { none() }
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -264,7 +264,7 @@ class TranslatedEmptyStmt extends TranslatedStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = getParent().getChildSuccessor(this) and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
@@ -279,19 +279,19 @@ class TranslatedEmptyStmt extends TranslatedStmt {
class TranslatedDeclStmt extends TranslatedStmt {
override DeclStmt stmt;
override TranslatedElement getChild(int id) { result = getDeclarationEntry(id) }
override TranslatedElement getChild(int id) { result = this.getDeclarationEntry(id) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
override Instruction getFirstInstruction() {
result = getDeclarationEntry(0).getFirstInstruction()
result = this.getDeclarationEntry(0).getFirstInstruction()
or
not exists(getDeclarationEntry(0)) and result = getParent().getChildSuccessor(this)
not exists(this.getDeclarationEntry(0)) and result = this.getParent().getChildSuccessor(this)
}
private int getChildCount() { result = count(getDeclarationEntry(_)) }
private int getChildCount() { result = count(this.getDeclarationEntry(_)) }
IRDeclarationEntry getIRDeclarationEntry(int index) {
result.hasIndex(index) and
@@ -319,10 +319,10 @@ class TranslatedDeclStmt extends TranslatedStmt {
override Instruction getChildSuccessor(TranslatedElement child) {
exists(int index |
child = getDeclarationEntry(index) and
if index = (getChildCount() - 1)
then result = getParent().getChildSuccessor(this)
else result = getDeclarationEntry(index + 1).getFirstInstruction()
child = this.getDeclarationEntry(index) and
if index = (this.getChildCount() - 1)
then result = this.getParent().getChildSuccessor(this)
else result = this.getDeclarationEntry(index + 1).getFirstInstruction()
)
}
}
@@ -332,19 +332,19 @@ class TranslatedExprStmt extends TranslatedStmt {
TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr().getFullyConverted()) }
override TranslatedElement getChild(int id) { id = 0 and result = getExpr() }
override TranslatedElement getChild(int id) { id = 0 and result = this.getExpr() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
override Instruction getFirstInstruction() { result = getExpr().getFirstInstruction() }
override Instruction getFirstInstruction() { result = this.getExpr().getFirstInstruction() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getExpr() and
result = getParent().getChildSuccessor(this)
child = this.getExpr() and
result = this.getParent().getChildSuccessor(this)
}
}
@@ -363,16 +363,18 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, TranslatedVariable
TranslatedReturnValueStmt() { stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction()) }
final override Instruction getInitializationSuccessor() {
result = getEnclosingFunction().getReturnSuccessorInstruction()
result = this.getEnclosingFunction().getReturnSuccessorInstruction()
}
final override Type getTargetType() { result = getEnclosingFunction().getReturnType() }
final override Type getTargetType() { result = this.getEnclosingFunction().getReturnType() }
final override TranslatedInitialization getInitialization() {
result = getTranslatedInitialization(stmt.getExpr().getFullyConverted())
}
final override IRVariable getIRVariable() { result = getEnclosingFunction().getReturnVariable() }
final override IRVariable getIRVariable() {
result = this.getEnclosingFunction().getReturnVariable()
}
}
/**
@@ -385,10 +387,10 @@ class TranslatedReturnVoidExpressionStmt extends TranslatedReturnStmt {
override TranslatedElement getChild(int id) {
id = 0 and
result = getExpr()
result = this.getExpr()
}
override Instruction getFirstInstruction() { result = getExpr().getFirstInstruction() }
override Instruction getFirstInstruction() { result = this.getExpr().getFirstInstruction() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -398,13 +400,13 @@ class TranslatedReturnVoidExpressionStmt extends TranslatedReturnStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = getEnclosingFunction().getReturnSuccessorInstruction() and
result = this.getEnclosingFunction().getReturnSuccessorInstruction() and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getExpr() and
result = getInstruction(OnlyInstructionTag())
child = this.getExpr() and
result = this.getInstruction(OnlyInstructionTag())
}
private TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr()) }
@@ -421,7 +423,7 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
override TranslatedElement getChild(int id) { none() }
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -431,7 +433,7 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = getEnclosingFunction().getReturnSuccessorInstruction() and
result = this.getEnclosingFunction().getReturnSuccessorInstruction() and
kind instanceof GotoEdge
}
@@ -452,7 +454,7 @@ class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
override TranslatedElement getChild(int id) { none() }
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -511,9 +513,9 @@ class TranslatedTryStmt extends TranslatedStmt {
override TryOrMicrosoftTryStmt stmt;
override TranslatedElement getChild(int id) {
id = 0 and result = getBody()
id = 0 and result = this.getBody()
or
result = getHandler(id - 1)
result = this.getHandler(id - 1)
or
id = stmt.getNumberOfCatchClauses() + 1 and
result = this.getFinally()
@@ -525,7 +527,7 @@ class TranslatedTryStmt extends TranslatedStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getFirstInstruction() { result = getBody().getFirstInstruction() }
override Instruction getFirstInstruction() { result = this.getBody().getFirstInstruction() }
override Instruction getChildSuccessor(TranslatedElement child) {
// All non-finally children go to the successor of the `try` if
@@ -546,19 +548,19 @@ class TranslatedTryStmt extends TranslatedStmt {
final Instruction getNextHandler(TranslatedHandler handler) {
exists(int index |
handler = getHandler(index) and
result = getHandler(index + 1).getFirstInstruction()
handler = this.getHandler(index) and
result = this.getHandler(index + 1).getFirstInstruction()
)
or
// The last catch clause flows to the exception successor of the parent
// of the `try`, because the exception successor of the `try` itself is
// the first catch clause.
handler = getHandler(stmt.getNumberOfCatchClauses() - 1) and
result = getParent().getExceptionSuccessorInstruction()
handler = this.getHandler(stmt.getNumberOfCatchClauses() - 1) and
result = this.getParent().getExceptionSuccessorInstruction()
}
final override Instruction getExceptionSuccessorInstruction() {
result = getHandler(0).getFirstInstruction()
result = this.getHandler(0).getFirstInstruction()
}
private TranslatedElement getHandler(int index) { result = stmt.getTranslatedHandler(index) }
@@ -571,19 +573,19 @@ class TranslatedTryStmt extends TranslatedStmt {
class TranslatedBlock extends TranslatedStmt {
override BlockStmt stmt;
override TranslatedElement getChild(int id) { result = getStmt(id) }
override TranslatedElement getChild(int id) { result = this.getStmt(id) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
isEmpty() and
this.isEmpty() and
opcode instanceof Opcode::NoOp and
tag = OnlyInstructionTag() and
resultType = getVoidType()
}
override Instruction getFirstInstruction() {
if isEmpty()
then result = getInstruction(OnlyInstructionTag())
else result = getStmt(0).getFirstInstruction()
if this.isEmpty()
then result = this.getInstruction(OnlyInstructionTag())
else result = this.getStmt(0).getFirstInstruction()
}
private predicate isEmpty() { not exists(stmt.getStmt(0)) }
@@ -594,16 +596,16 @@ class TranslatedBlock extends TranslatedStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = getParent().getChildSuccessor(this) and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
exists(int index |
child = getStmt(index) and
if index = (getStmtCount() - 1)
then result = getParent().getChildSuccessor(this)
else result = getStmt(index + 1).getFirstInstruction()
child = this.getStmt(index) and
if index = (this.getStmtCount() - 1)
then result = this.getParent().getChildSuccessor(this)
else result = this.getStmt(index + 1).getFirstInstruction()
)
}
}
@@ -614,18 +616,18 @@ class TranslatedBlock extends TranslatedStmt {
abstract class TranslatedHandler extends TranslatedStmt {
override Handler stmt;
override TranslatedElement getChild(int id) { id = 1 and result = getBlock() }
override TranslatedElement getChild(int id) { id = 1 and result = this.getBlock() }
override Instruction getFirstInstruction() { result = getInstruction(CatchTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(CatchTag()) }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getBlock() and result = getParent().getChildSuccessor(this)
child = this.getBlock() and result = this.getParent().getChildSuccessor(this)
}
override Instruction getExceptionSuccessorInstruction() {
// A throw from within a `catch` block flows to the handler for the parent of
// the `try`.
result = getParent().getParent().getExceptionSuccessorInstruction()
result = this.getParent().getParent().getExceptionSuccessorInstruction()
}
TranslatedStmt getBlock() { result = getTranslatedStmt(stmt.getBlock()) }
@@ -647,23 +649,23 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
override TranslatedElement getChild(int id) {
result = super.getChild(id)
or
id = 0 and result = getParameter()
id = 0 and result = this.getParameter()
}
override Instruction getChildSuccessor(TranslatedElement child) {
result = super.getChildSuccessor(child)
or
child = getParameter() and result = getBlock().getFirstInstruction()
child = this.getParameter() and result = this.getBlock().getFirstInstruction()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = CatchTag() and
(
kind instanceof GotoEdge and
result = getParameter().getFirstInstruction()
result = this.getParameter().getFirstInstruction()
or
kind instanceof ExceptionEdge and
result = getParent().(TranslatedTryStmt).getNextHandler(this)
result = this.getParent().(TranslatedTryStmt).getNextHandler(this)
)
}
@@ -692,7 +694,7 @@ class TranslatedCatchAnyHandler extends TranslatedHandler {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = CatchTag() and
kind instanceof GotoEdge and
result = getBlock().getFirstInstruction()
result = this.getBlock().getFirstInstruction()
}
}
@@ -700,19 +702,19 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
override IfStmt stmt;
override Instruction getFirstInstruction() {
if hasInitialization()
then result = getInitialization().getFirstInstruction()
else result = getFirstConditionInstruction()
if this.hasInitialization()
then result = this.getInitialization().getFirstInstruction()
else result = this.getFirstConditionInstruction()
}
override TranslatedElement getChild(int id) {
id = 0 and result = getInitialization()
id = 0 and result = this.getInitialization()
or
id = 1 and result = getCondition()
id = 1 and result = this.getCondition()
or
id = 2 and result = getThen()
id = 2 and result = this.getThen()
or
id = 3 and result = getElse()
id = 3 and result = this.getElse()
}
private predicate hasInitialization() { exists(stmt.getInitialization()) }
@@ -726,7 +728,7 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
}
private Instruction getFirstConditionInstruction() {
result = getCondition().getFirstInstruction()
result = this.getCondition().getFirstInstruction()
}
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
@@ -738,23 +740,23 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
child = getCondition() and
result = getThen().getFirstInstruction()
child = this.getCondition() and
result = this.getThen().getFirstInstruction()
}
override Instruction getChildFalseSuccessor(TranslatedCondition child) {
child = getCondition() and
if hasElse()
then result = getElse().getFirstInstruction()
else result = getParent().getChildSuccessor(this)
child = this.getCondition() and
if this.hasElse()
then result = this.getElse().getFirstInstruction()
else result = this.getParent().getChildSuccessor(this)
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and
result = getFirstConditionInstruction()
child = this.getInitialization() and
result = this.getFirstConditionInstruction()
or
(child = getThen() or child = getElse()) and
result = getParent().getChildSuccessor(this)
(child = this.getThen() or child = this.getElse()) and
result = this.getParent().getChildSuccessor(this)
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -772,17 +774,17 @@ abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
final TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getStmt()) }
final Instruction getFirstConditionInstruction() {
if hasCondition()
then result = getCondition().getFirstInstruction()
else result = getBody().getFirstInstruction()
if this.hasCondition()
then result = this.getCondition().getFirstInstruction()
else result = this.getBody().getFirstInstruction()
}
final predicate hasCondition() { exists(stmt.getCondition()) }
override TranslatedElement getChild(int id) {
id = 0 and result = getCondition()
id = 0 and result = this.getCondition()
or
id = 1 and result = getBody()
id = 1 and result = this.getBody()
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -792,31 +794,31 @@ abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
final override Instruction getChildTrueSuccessor(TranslatedCondition child) {
child = getCondition() and result = getBody().getFirstInstruction()
child = this.getCondition() and result = this.getBody().getFirstInstruction()
}
final override Instruction getChildFalseSuccessor(TranslatedCondition child) {
child = getCondition() and result = getParent().getChildSuccessor(this)
child = this.getCondition() and result = this.getParent().getChildSuccessor(this)
}
}
class TranslatedWhileStmt extends TranslatedLoop {
TranslatedWhileStmt() { stmt instanceof WhileStmt }
override Instruction getFirstInstruction() { result = getFirstConditionInstruction() }
override Instruction getFirstInstruction() { result = this.getFirstConditionInstruction() }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getBody() and result = getFirstConditionInstruction()
child = this.getBody() and result = this.getFirstConditionInstruction()
}
}
class TranslatedDoStmt extends TranslatedLoop {
TranslatedDoStmt() { stmt instanceof DoStmt }
override Instruction getFirstInstruction() { result = getBody().getFirstInstruction() }
override Instruction getFirstInstruction() { result = this.getBody().getFirstInstruction() }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getBody() and result = getFirstConditionInstruction()
child = this.getBody() and result = this.getFirstConditionInstruction()
}
}
@@ -824,13 +826,13 @@ class TranslatedForStmt extends TranslatedLoop {
override ForStmt stmt;
override TranslatedElement getChild(int id) {
id = 0 and result = getInitialization()
id = 0 and result = this.getInitialization()
or
id = 1 and result = getCondition()
id = 1 and result = this.getCondition()
or
id = 2 and result = getUpdate()
id = 2 and result = this.getUpdate()
or
id = 3 and result = getBody()
id = 3 and result = this.getBody()
}
private TranslatedStmt getInitialization() {
@@ -844,23 +846,23 @@ class TranslatedForStmt extends TranslatedLoop {
private predicate hasUpdate() { exists(stmt.getUpdate()) }
override Instruction getFirstInstruction() {
if hasInitialization()
then result = getInitialization().getFirstInstruction()
else result = getFirstConditionInstruction()
if this.hasInitialization()
then result = this.getInitialization().getFirstInstruction()
else result = this.getFirstConditionInstruction()
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and
result = getFirstConditionInstruction()
child = this.getInitialization() and
result = this.getFirstConditionInstruction()
or
(
child = getBody() and
if hasUpdate()
then result = getUpdate().getFirstInstruction()
else result = getFirstConditionInstruction()
child = this.getBody() and
if this.hasUpdate()
then result = this.getUpdate().getFirstInstruction()
else result = this.getFirstConditionInstruction()
)
or
child = getUpdate() and result = getFirstConditionInstruction()
child = this.getUpdate() and result = this.getFirstConditionInstruction()
}
}
@@ -875,39 +877,39 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
override RangeBasedForStmt stmt;
override TranslatedElement getChild(int id) {
id = 0 and result = getRangeVariableDeclStmt()
id = 0 and result = this.getRangeVariableDeclStmt()
or
// Note: `__begin` and `__end` are declared by the same `DeclStmt`
id = 1 and result = getBeginEndVariableDeclStmt()
id = 1 and result = this.getBeginEndVariableDeclStmt()
or
id = 2 and result = getCondition()
id = 2 and result = this.getCondition()
or
id = 3 and result = getUpdate()
id = 3 and result = this.getUpdate()
or
id = 4 and result = getVariableDeclStmt()
id = 4 and result = this.getVariableDeclStmt()
or
id = 5 and result = getBody()
id = 5 and result = this.getBody()
}
override Instruction getFirstInstruction() {
result = getRangeVariableDeclStmt().getFirstInstruction()
result = this.getRangeVariableDeclStmt().getFirstInstruction()
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getRangeVariableDeclStmt() and
result = getBeginEndVariableDeclStmt().getFirstInstruction()
child = this.getRangeVariableDeclStmt() and
result = this.getBeginEndVariableDeclStmt().getFirstInstruction()
or
child = getBeginEndVariableDeclStmt() and
result = getCondition().getFirstInstruction()
child = this.getBeginEndVariableDeclStmt() and
result = this.getCondition().getFirstInstruction()
or
child = getVariableDeclStmt() and
result = getBody().getFirstInstruction()
child = this.getVariableDeclStmt() and
result = this.getBody().getFirstInstruction()
or
child = getBody() and
result = getUpdate().getFirstInstruction()
child = this.getBody() and
result = this.getUpdate().getFirstInstruction()
or
child = getUpdate() and
result = getCondition().getFirstInstruction()
child = this.getUpdate() and
result = this.getCondition().getFirstInstruction()
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -917,11 +919,11 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
child = getCondition() and result = getVariableDeclStmt().getFirstInstruction()
child = this.getCondition() and result = this.getVariableDeclStmt().getFirstInstruction()
}
override Instruction getChildFalseSuccessor(TranslatedCondition child) {
child = getCondition() and result = getParent().getChildSuccessor(this)
child = this.getCondition() and result = this.getParent().getChildSuccessor(this)
}
private TranslatedDeclStmt getRangeVariableDeclStmt() {
@@ -961,7 +963,7 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
class TranslatedJumpStmt extends TranslatedStmt {
override JumpStmt stmt;
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
override TranslatedElement getChild(int id) { none() }
@@ -996,22 +998,22 @@ class TranslatedSwitchStmt extends TranslatedStmt {
result = getTranslatedExpr(stmt.getExpr().getFullyConverted())
}
private Instruction getFirstExprInstruction() { result = getExpr().getFirstInstruction() }
private Instruction getFirstExprInstruction() { result = this.getExpr().getFirstInstruction() }
private TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getStmt()) }
override Instruction getFirstInstruction() {
if hasInitialization()
then result = getInitialization().getFirstInstruction()
else result = getFirstExprInstruction()
if this.hasInitialization()
then result = this.getInitialization().getFirstInstruction()
else result = this.getFirstExprInstruction()
}
override TranslatedElement getChild(int id) {
id = 0 and result = getInitialization()
id = 0 and result = this.getInitialization()
or
id = 1 and result = getExpr()
id = 1 and result = this.getExpr()
or
id = 2 and result = getBody()
id = 2 and result = this.getBody()
}
private predicate hasInitialization() { exists(stmt.getInitialization()) }
@@ -1029,7 +1031,7 @@ class TranslatedSwitchStmt extends TranslatedStmt {
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = SwitchBranchTag() and
operandTag instanceof ConditionOperandTag and
result = getExpr().getResult()
result = this.getExpr().getResult()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -1043,15 +1045,15 @@ class TranslatedSwitchStmt extends TranslatedStmt {
not stmt.hasDefaultCase() and
tag = SwitchBranchTag() and
kind instanceof DefaultEdge and
result = getParent().getChildSuccessor(this)
result = this.getParent().getChildSuccessor(this)
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and result = getFirstExprInstruction()
child = this.getInitialization() and result = this.getFirstExprInstruction()
or
child = getExpr() and result = getInstruction(SwitchBranchTag())
child = this.getExpr() and result = this.getInstruction(SwitchBranchTag())
or
child = getBody() and result = getParent().getChildSuccessor(this)
child = this.getBody() and result = this.getParent().getChildSuccessor(this)
}
}
@@ -1063,9 +1065,9 @@ class TranslatedAsmStmt extends TranslatedStmt {
}
override Instruction getFirstInstruction() {
if exists(getChild(0))
then result = getChild(0).getFirstInstruction()
else result = getInstruction(AsmTag())
if exists(this.getChild(0))
then result = this.getChild(0).getFirstInstruction()
else result = this.getInstruction(AsmTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -1078,7 +1080,7 @@ class TranslatedAsmStmt extends TranslatedStmt {
exists(int index |
tag = AsmTag() and
operandTag = asmOperand(index) and
result = getChild(index).getResult()
result = this.getChild(index).getResult()
)
}
@@ -1092,16 +1094,16 @@ class TranslatedAsmStmt extends TranslatedStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = AsmTag() and
result = getParent().getChildSuccessor(this) and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
exists(int index |
child = getChild(index) and
if exists(getChild(index + 1))
then result = getChild(index + 1).getFirstInstruction()
else result = getInstruction(AsmTag())
child = this.getChild(index) and
if exists(this.getChild(index + 1))
then result = this.getChild(index + 1).getFirstInstruction()
else result = this.getInstruction(AsmTag())
)
}
}

View File

@@ -47,7 +47,7 @@ class Variable = Cpp::Variable;
class AutomaticVariable = Cpp::StackVariable;
class StaticVariable = Cpp::Variable;
class StaticVariable = Cpp::StaticStorageDurationVariable;
class GlobalVariable = Cpp::GlobalOrNamespaceVariable;

View File

@@ -1,3 +1,9 @@
private import cpp
private import semmle.code.cpp.Print as Print
predicate getIdentityString = Print::getIdentityString/1;
string getIdentityString(Declaration decl) {
if decl instanceof StaticLocalVariable
then
exists(StaticLocalVariable v | v = decl | result = v.getType().toString() + " " + v.getName())
else result = Print::getIdentityString(decl)
}

View File

@@ -1,3 +1,10 @@
## 0.6.1
### New Queries
* A new query `cpp/double-free` has been added. The query finds possible cases of deallocating the same pointer twice. The precision of the query has been set to "medium".
* The query `cpp/use-after-free` has been modernized and assigned the precision "medium". The query finds cases of where a pointer is dereferenced after its memory has been deallocated.
## 0.6.0
### New Queries

View File

@@ -1,4 +0,0 @@
---
category: newQuery
---
* A new query `cpp/double-free` has been added. The query finds possible cases of deallocating the same pointer twice. The precision of the query has been set to "medium".

View File

@@ -1,4 +0,0 @@
---
category: newQuery
---
* The query `cpp/use-after-free` has been modernized and assigned the precision "medium". The query finds cases of where a pointer is dereferenced after its memory has been deallocated.

View File

@@ -0,0 +1,6 @@
## 0.6.1
### New Queries
* A new query `cpp/double-free` has been added. The query finds possible cases of deallocating the same pointer twice. The precision of the query has been set to "medium".
* The query `cpp/use-after-free` has been modernized and assigned the precision "medium". The query finds cases of where a pointer is dereferenced after its memory has been deallocated.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.0
lastReleaseVersion: 0.6.1

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.6.1-dev
version: 0.6.2-dev
groups:
- cpp
- queries

View File

@@ -209,6 +209,9 @@ edges
| test.cpp:207:17:207:19 | str indirection [string] | test.cpp:207:22:207:27 | string |
| test.cpp:207:17:207:19 | str indirection [string] | test.cpp:207:22:207:27 | string indirection |
| test.cpp:207:22:207:27 | string indirection | test.cpp:207:22:207:27 | string |
| test.cpp:214:24:214:24 | p | test.cpp:216:10:216:10 | p |
| test.cpp:220:43:220:48 | call to malloc | test.cpp:222:15:222:20 | buffer |
| test.cpp:222:15:222:20 | buffer | test.cpp:214:24:214:24 | p |
nodes
| test.cpp:16:11:16:21 | mk_string_t indirection [string] | semmle.label | mk_string_t indirection [string] |
| test.cpp:18:5:18:30 | ... = ... | semmle.label | ... = ... |
@@ -374,6 +377,10 @@ nodes
| test.cpp:207:17:207:19 | str indirection [string] | semmle.label | str indirection [string] |
| test.cpp:207:22:207:27 | string | semmle.label | string |
| test.cpp:207:22:207:27 | string indirection | semmle.label | string indirection |
| test.cpp:214:24:214:24 | p | semmle.label | p |
| test.cpp:216:10:216:10 | p | semmle.label | p |
| test.cpp:220:43:220:48 | call to malloc | semmle.label | call to malloc |
| test.cpp:222:15:222:20 | buffer | semmle.label | buffer |
subpaths
#select
| test.cpp:42:5:42:11 | call to strncpy | test.cpp:18:19:18:24 | call to malloc | test.cpp:42:18:42:23 | string | This write may overflow $@ by 1 element. | test.cpp:42:18:42:23 | string | string |
@@ -391,3 +398,4 @@ subpaths
| test.cpp:199:9:199:15 | call to strncpy | test.cpp:147:19:147:24 | call to malloc | test.cpp:199:22:199:27 | string | This write may overflow $@ by 2 elements. | test.cpp:199:22:199:27 | string | string |
| test.cpp:203:9:203:15 | call to strncpy | test.cpp:147:19:147:24 | call to malloc | test.cpp:203:22:203:27 | string | This write may overflow $@ by 2 elements. | test.cpp:203:22:203:27 | string | string |
| test.cpp:207:9:207:15 | call to strncpy | test.cpp:147:19:147:24 | call to malloc | test.cpp:207:22:207:27 | string | This write may overflow $@ by 3 elements. | test.cpp:207:22:207:27 | string | string |
| test.cpp:216:3:216:8 | call to memset | test.cpp:220:43:220:48 | call to malloc | test.cpp:216:10:216:10 | p | This write may overflow $@ by 5 elements. | test.cpp:216:10:216:10 | p | p |

View File

@@ -208,3 +208,16 @@ void test5(unsigned size, char *buf, unsigned anotherSize) {
}
}
void *memset(void *, int, unsigned);
void call_memset(void *p, unsigned size)
{
memset(p, 0, size); // GOOD [FALSE POSITIVE]
}
void test_missing_call_context(unsigned char *unrelated_buffer, unsigned size) {
unsigned char* buffer = (unsigned char*)malloc(size);
call_memset(unrelated_buffer, size + 5);
call_memset(buffer, size);
}

View File

@@ -586,6 +586,118 @@ edges
| test.cpp:248:24:248:30 | call to realloc | test.cpp:254:9:254:9 | p |
| test.cpp:254:9:254:9 | p | test.cpp:254:9:254:12 | access to array |
| test.cpp:254:9:254:12 | access to array | test.cpp:254:9:254:16 | Store: ... = ... |
| test.cpp:260:13:260:24 | new[] | test.cpp:261:14:261:15 | xs |
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:15 | xs | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:15 | xs | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:15 | xs | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:15 | xs | test.cpp:262:31:262:31 | x |
| test.cpp:261:14:261:15 | xs | test.cpp:262:31:262:33 | ... ++ |
| test.cpp:261:14:261:15 | xs | test.cpp:262:31:262:33 | ... ++ |
| test.cpp:261:14:261:15 | xs | test.cpp:264:14:264:14 | x |
| test.cpp:261:14:261:15 | xs | test.cpp:264:14:264:14 | x |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:261:14:261:21 | ... + ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:262:26:262:28 | end |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:261:14:261:21 | ... + ... | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:262:21:262:21 | x | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:262:26:262:28 | end | test.cpp:262:26:262:28 | end |
| test.cpp:262:26:262:28 | end | test.cpp:262:26:262:28 | end |
| test.cpp:262:26:262:28 | end | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:262:26:262:28 | end | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:262:31:262:31 | x | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:262:31:262:33 | ... ++ | test.cpp:262:21:262:21 | x |
| test.cpp:262:31:262:33 | ... ++ | test.cpp:262:21:262:21 | x |
| test.cpp:262:31:262:33 | ... ++ | test.cpp:262:31:262:31 | x |
| test.cpp:262:31:262:33 | ... ++ | test.cpp:262:31:262:31 | x |
| test.cpp:262:31:262:33 | ... ++ | test.cpp:264:14:264:14 | x |
| test.cpp:262:31:262:33 | ... ++ | test.cpp:264:14:264:14 | x |
| test.cpp:262:31:262:33 | ... ++ | test.cpp:264:14:264:14 | x |
| test.cpp:262:31:262:33 | ... ++ | test.cpp:264:14:264:14 | x |
| test.cpp:264:14:264:14 | x | test.cpp:262:31:262:31 | x |
| test.cpp:264:14:264:14 | x | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:264:14:264:14 | x | test.cpp:264:13:264:14 | Load: * ... |
| test.cpp:270:13:270:24 | new[] | test.cpp:271:14:271:15 | xs |
| test.cpp:270:13:270:24 | new[] | test.cpp:272:31:272:31 | x |
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:15 | xs | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:15 | xs | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:15 | xs | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:15 | xs | test.cpp:272:31:272:31 | x |
| test.cpp:271:14:271:15 | xs | test.cpp:272:31:272:33 | ... ++ |
| test.cpp:271:14:271:15 | xs | test.cpp:272:31:272:33 | ... ++ |
| test.cpp:271:14:271:15 | xs | test.cpp:274:5:274:6 | * ... |
| test.cpp:271:14:271:15 | xs | test.cpp:274:6:274:6 | x |
| test.cpp:271:14:271:15 | xs | test.cpp:274:6:274:6 | x |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:272:26:272:28 | end |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:272:21:272:21 | x | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:272:26:272:28 | end | test.cpp:272:26:272:28 | end |
| test.cpp:272:26:272:28 | end | test.cpp:272:26:272:28 | end |
| test.cpp:272:26:272:28 | end | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:272:26:272:28 | end | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:272:31:272:31 | x | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:272:21:272:21 | x |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:272:21:272:21 | x |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:272:31:272:31 | x |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:272:31:272:31 | x |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:5:274:6 | * ... |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:5:274:6 | * ... |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:6:274:6 | x |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:6:274:6 | x |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:6:274:6 | x |
| test.cpp:272:31:272:33 | ... ++ | test.cpp:274:6:274:6 | x |
| test.cpp:274:5:274:6 | * ... | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:274:6:274:6 | x | test.cpp:272:31:272:31 | x |
| test.cpp:274:6:274:6 | x | test.cpp:274:5:274:6 | * ... |
| test.cpp:274:6:274:6 | x | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:274:6:274:6 | x | test.cpp:274:5:274:10 | Store: ... = ... |
| test.cpp:280:13:280:24 | new[] | test.cpp:281:14:281:15 | xs |
| test.cpp:281:14:281:15 | xs | test.cpp:282:30:282:32 | ... ++ |
| test.cpp:281:14:281:15 | xs | test.cpp:282:30:282:32 | ... ++ |
| test.cpp:282:21:282:21 | x | test.cpp:284:13:284:14 | Load: * ... |
| test.cpp:282:30:282:30 | x | test.cpp:284:13:284:14 | Load: * ... |
| test.cpp:282:30:282:32 | ... ++ | test.cpp:282:21:282:21 | x |
| test.cpp:282:30:282:32 | ... ++ | test.cpp:282:21:282:21 | x |
| test.cpp:282:30:282:32 | ... ++ | test.cpp:282:30:282:30 | x |
| test.cpp:282:30:282:32 | ... ++ | test.cpp:282:30:282:30 | x |
| test.cpp:282:30:282:32 | ... ++ | test.cpp:284:14:284:14 | x |
| test.cpp:282:30:282:32 | ... ++ | test.cpp:284:14:284:14 | x |
| test.cpp:284:14:284:14 | x | test.cpp:284:13:284:14 | Load: * ... |
| test.cpp:290:13:290:24 | new[] | test.cpp:291:14:291:15 | xs |
| test.cpp:290:13:290:24 | new[] | test.cpp:292:30:292:30 | x |
| test.cpp:291:14:291:15 | xs | test.cpp:292:30:292:32 | ... ++ |
| test.cpp:291:14:291:15 | xs | test.cpp:292:30:292:32 | ... ++ |
| test.cpp:292:21:292:21 | x | test.cpp:294:5:294:10 | Store: ... = ... |
| test.cpp:292:30:292:30 | x | test.cpp:294:5:294:10 | Store: ... = ... |
| test.cpp:292:30:292:32 | ... ++ | test.cpp:292:21:292:21 | x |
| test.cpp:292:30:292:32 | ... ++ | test.cpp:292:21:292:21 | x |
| test.cpp:292:30:292:32 | ... ++ | test.cpp:292:30:292:30 | x |
| test.cpp:292:30:292:32 | ... ++ | test.cpp:292:30:292:30 | x |
| test.cpp:292:30:292:32 | ... ++ | test.cpp:294:5:294:6 | * ... |
| test.cpp:292:30:292:32 | ... ++ | test.cpp:294:5:294:6 | * ... |
| test.cpp:292:30:292:32 | ... ++ | test.cpp:294:6:294:6 | x |
| test.cpp:292:30:292:32 | ... ++ | test.cpp:294:6:294:6 | x |
| test.cpp:294:5:294:6 | * ... | test.cpp:294:5:294:10 | Store: ... = ... |
| test.cpp:294:6:294:6 | x | test.cpp:294:5:294:10 | Store: ... = ... |
#select
| test.cpp:6:14:6:15 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
| test.cpp:8:14:8:21 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:8:14:8:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
@@ -607,3 +719,9 @@ edges
| test.cpp:232:3:232:20 | Store: ... = ... | test.cpp:231:18:231:30 | new[] | test.cpp:232:3:232:20 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:231:18:231:30 | new[] | new[] | test.cpp:232:11:232:15 | index | index |
| test.cpp:239:5:239:22 | Store: ... = ... | test.cpp:238:20:238:32 | new[] | test.cpp:239:5:239:22 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:238:20:238:32 | new[] | new[] | test.cpp:239:13:239:17 | index | index |
| test.cpp:254:9:254:16 | Store: ... = ... | test.cpp:248:24:248:30 | call to realloc | test.cpp:254:9:254:16 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:248:24:248:30 | call to realloc | call to realloc | test.cpp:254:11:254:11 | i | i |
| test.cpp:264:13:264:14 | Load: * ... | test.cpp:260:13:260:24 | new[] | test.cpp:264:13:264:14 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:260:13:260:24 | new[] | new[] | test.cpp:261:19:261:21 | len | len |
| test.cpp:264:13:264:14 | Load: * ... | test.cpp:260:13:260:24 | new[] | test.cpp:264:13:264:14 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:260:13:260:24 | new[] | new[] | test.cpp:261:19:261:21 | len | len |
| test.cpp:274:5:274:10 | Store: ... = ... | test.cpp:270:13:270:24 | new[] | test.cpp:274:5:274:10 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:270:13:270:24 | new[] | new[] | test.cpp:271:19:271:21 | len | len |
| test.cpp:274:5:274:10 | Store: ... = ... | test.cpp:270:13:270:24 | new[] | test.cpp:274:5:274:10 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:270:13:270:24 | new[] | new[] | test.cpp:271:19:271:21 | len | len |
| test.cpp:284:13:284:14 | Load: * ... | test.cpp:280:13:280:24 | new[] | test.cpp:284:13:284:14 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:280:13:280:24 | new[] | new[] | test.cpp:281:19:281:21 | len | len |
| test.cpp:294:5:294:10 | Store: ... = ... | test.cpp:290:13:290:24 | new[] | test.cpp:294:5:294:10 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:290:13:290:24 | new[] | new[] | test.cpp:291:19:291:21 | len | len |

View File

@@ -254,3 +254,43 @@ void test17(unsigned *p, unsigned x, unsigned k) {
p[i] = x; // GOOD [FALSE POSITIVE]
}
}
void test17(unsigned len)
{
int *xs = new int[len];
int *end = xs + len;
for (int *x = xs; x <= end; x++)
{
int i = *x; // BAD
}
}
void test18(unsigned len)
{
int *xs = new int[len];
int *end = xs + len;
for (int *x = xs; x <= end; x++)
{
*x = 0; // BAD
}
}
void test19(unsigned len)
{
int *xs = new int[len];
int *end = xs + len;
for (int *x = xs; x < end; x++)
{
int i = *x; // GOOD [FALSE POSITIVE]
}
}
void test20(unsigned len)
{
int *xs = new int[len];
int *end = xs + len;
for (int *x = xs; x < end; x++)
{
*x = 0; // GOOD [FALSE POSITIVE]
}
}

View File

@@ -1,5 +1,5 @@
int source();
void sink(int);
void sink(...);
bool guarded(int);
void bg_basic(int source) {
@@ -66,3 +66,13 @@ void bg_structptr(XY *p1, XY *p2) { // $ ast-def=p1 ast-def=p2
sink(p1->x); // $ ast,ir
}
}
int* indirect_source();
bool guarded(const int*);
void bg_indirect_expr() {
int *buf = indirect_source();
if (guarded(buf)) {
sink(buf);
}
}

View File

@@ -115,6 +115,16 @@ postWithInFlow
| test.cpp:602:3:602:7 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:608:3:608:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:608:4:608:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:639:3:639:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:646:3:646:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:652:3:652:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:653:3:653:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:659:3:659:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:660:3:660:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:671:3:671:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:681:3:681:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:689:3:689:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:690:3:690:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -627,4 +627,66 @@ void test_def_via_phi_read(bool b)
}
intPointerSource(buffer);
sink(buffer); // $ ast,ir
}
}
void test_static_local_1() {
static int x = source();
sink(x); // $ ast,ir
}
void test_static_local_2() {
static int x = source();
x = 0;
sink(x); // clean
}
void test_static_local_3() {
static int x = 0;
sink(x); // $ ir MISSING: ast
x = source();
}
void test_static_local_4() {
static int x = 0;
sink(x); // clean
x = source();
x = 0;
}
void test_static_local_5() {
static int x = 0;
sink(x); // $ ir MISSING: ast
x = 0;
x = source();
}
void test_static_local_6() {
static int s = source();
static int* ptr_to_s = &s;
sink(*ptr_to_s); // $ ir MISSING: ast
}
void test_static_local_7() {
static int s = source();
s = 0;
static int* ptr_to_s = &s;
sink(*ptr_to_s); // clean
}
void test_static_local_8() {
static int s;
static int* ptr_to_s = &s;
sink(*ptr_to_s); // $ ir MISSING: ast
s = source();
}
void test_static_local_9() {
static int s;
static int* ptr_to_s = &s;
sink(*ptr_to_s); // clean
s = source();
s = 0;
}

View File

@@ -47,6 +47,7 @@ module AstTest {
}
module IRTest {
private import cpp
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
@@ -56,10 +57,13 @@ module IRTest {
* S in `if (guarded(x)) S`.
*/
// This is tested in `BarrierGuard.cpp`.
predicate testBarrierGuard(IRGuardCondition g, Instruction checked, boolean isTrue) {
g.(CallInstruction).getStaticCallTarget().getName() = "guarded" and
checked = g.(CallInstruction).getPositionalArgument(0) and
isTrue = true
predicate testBarrierGuard(IRGuardCondition g, Expr checked, boolean isTrue) {
exists(Call call |
call = g.getUnconvertedResultExpression() and
call.getTarget().hasName("guarded") and
checked = call.getArgument(0) and
isTrue = true
)
}
/** Common data flow configuration to be used by tests. */
@@ -90,7 +94,9 @@ module IRTest {
barrierExpr.(VariableAccess).getTarget().hasName("barrier")
)
or
barrier = DataFlow::InstructionBarrierGuard<testBarrierGuard/3>::getABarrierNode()
barrier = DataFlow::BarrierGuard<testBarrierGuard/3>::getABarrierNode()
or
barrier = DataFlow::BarrierGuard<testBarrierGuard/3>::getAnIndirectBarrierNode()
}
}
}

View File

@@ -14377,6 +14377,37 @@ ir.cpp:
# 1885| Type = [ClassTemplateInstantiation,Struct] Bar2<int>
# 1885| ValueCategory = lvalue
# 1886| getStmt(2): [ReturnStmt] return ...
# 1891| [TopLevelFunction] int test_global_template_int()
# 1891| <params>:
# 1891| getEntryPoint(): [BlockStmt] { ... }
# 1892| getStmt(0): [DeclStmt] declaration
# 1892| getDeclarationEntry(0): [VariableDeclarationEntry] definition of local_int
# 1892| Type = [IntType] int
# 1892| getVariable().getInitializer(): [Initializer] initializer for local_int
# 1892| getExpr(): [VariableAccess] global_template
# 1892| Type = [IntType] int
# 1892| ValueCategory = prvalue(load)
# 1893| getStmt(1): [DeclStmt] declaration
# 1893| getDeclarationEntry(0): [VariableDeclarationEntry] definition of local_char
# 1893| Type = [PlainCharType] char
# 1893| getVariable().getInitializer(): [Initializer] initializer for local_char
# 1893| getExpr(): [VariableAccess] global_template
# 1893| Type = [PlainCharType] char
# 1893| ValueCategory = prvalue(load)
# 1894| getStmt(2): [ReturnStmt] return ...
# 1894| getExpr(): [AddExpr] ... + ...
# 1894| Type = [IntType] int
# 1894| ValueCategory = prvalue
# 1894| getLeftOperand(): [VariableAccess] local_int
# 1894| Type = [IntType] int
# 1894| ValueCategory = prvalue(load)
# 1894| getRightOperand(): [VariableAccess] local_char
# 1894| Type = [PlainCharType] char
# 1894| ValueCategory = prvalue(load)
# 1894| getRightOperand().getFullyConverted(): [CStyleCast] (int)...
# 1894| Conversion = [IntegralConversion] integral conversion
# 1894| Type = [IntType] int
# 1894| ValueCategory = prvalue
perf-regression.cpp:
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
# 4| <params>:

View File

@@ -18,5 +18,7 @@ predicate shouldDumpFunction(Declaration decl) {
decl instanceof Function
or
decl.(GlobalOrNamespaceVariable).hasInitializer()
or
decl.(StaticLocalVariable).hasInitializer()
)
}

View File

@@ -1886,4 +1886,12 @@ namespace missing_declaration_entries {
}
}
template<typename T> T global_template = 42;
int test_global_template_int() {
int local_int = global_template<int>;
char local_char = global_template<char>;
return local_int + (int)local_char;
}
// semmle-extractor-options: -std=c++17 --clang

View File

@@ -5754,6 +5754,16 @@
| ir.cpp:1231:5:1231:19 | Load | m1237_15 |
| ir.cpp:1231:5:1231:19 | SideEffect | ~m1237_2 |
| ir.cpp:1231:25:1231:25 | Address | &:r1231_5 |
| ir.cpp:1232:16:1232:16 | Address | &:r1232_3 |
| ir.cpp:1232:16:1232:16 | SideEffect | ~m1232_6 |
| ir.cpp:1232:20:1232:20 | ChiPartial | partial:m1232_5 |
| ir.cpp:1232:20:1232:20 | ChiTotal | total:m1232_2 |
| ir.cpp:1232:20:1232:20 | StoreValue | r1232_4 |
| ir.cpp:1233:16:1233:16 | Address | &:r1233_3 |
| ir.cpp:1233:16:1233:16 | SideEffect | ~m1233_6 |
| ir.cpp:1233:20:1233:28 | ChiPartial | partial:m1233_5 |
| ir.cpp:1233:20:1233:28 | ChiTotal | total:m1233_2 |
| ir.cpp:1233:20:1233:28 | StoreValue | r1233_4 |
| ir.cpp:1234:16:1234:16 | Address | &:r1234_1 |
| ir.cpp:1234:16:1234:16 | Address | &:r1234_1 |
| ir.cpp:1234:16:1234:16 | Address | &:r1234_4 |
@@ -8741,6 +8751,38 @@
| ir.cpp:1885:11:1885:50 | ChiPartial | partial:m1885_4 |
| ir.cpp:1885:11:1885:50 | ChiTotal | total:m1883_4 |
| ir.cpp:1885:11:1885:50 | SideEffect | ~m1883_4 |
| ir.cpp:1889:24:1889:24 | Address | &:r1889_3 |
| ir.cpp:1889:24:1889:24 | Address | &:r1889_3 |
| ir.cpp:1889:24:1889:24 | SideEffect | ~m1889_6 |
| ir.cpp:1889:24:1889:24 | SideEffect | ~m1889_6 |
| ir.cpp:1889:42:1889:43 | ChiPartial | partial:m1889_5 |
| ir.cpp:1889:42:1889:43 | ChiPartial | partial:m1889_5 |
| ir.cpp:1889:42:1889:43 | ChiTotal | total:m1889_2 |
| ir.cpp:1889:42:1889:43 | ChiTotal | total:m1889_2 |
| ir.cpp:1889:42:1889:43 | StoreValue | r1889_4 |
| ir.cpp:1889:42:1889:43 | StoreValue | r1889_4 |
| ir.cpp:1891:5:1891:28 | Address | &:r1891_5 |
| ir.cpp:1891:5:1891:28 | ChiPartial | partial:m1891_3 |
| ir.cpp:1891:5:1891:28 | ChiTotal | total:m1891_2 |
| ir.cpp:1891:5:1891:28 | Load | m1894_8 |
| ir.cpp:1891:5:1891:28 | SideEffect | m1891_3 |
| ir.cpp:1892:9:1892:17 | Address | &:r1892_1 |
| ir.cpp:1892:21:1892:40 | Address | &:r1892_2 |
| ir.cpp:1892:21:1892:40 | Load | ~m1891_3 |
| ir.cpp:1892:21:1892:40 | StoreValue | r1892_3 |
| ir.cpp:1893:10:1893:19 | Address | &:r1893_1 |
| ir.cpp:1893:23:1893:43 | Address | &:r1893_2 |
| ir.cpp:1893:23:1893:43 | Load | ~m1891_3 |
| ir.cpp:1893:23:1893:43 | StoreValue | r1893_3 |
| ir.cpp:1894:5:1894:39 | Address | &:r1894_1 |
| ir.cpp:1894:12:1894:20 | Address | &:r1894_2 |
| ir.cpp:1894:12:1894:20 | Left | r1894_3 |
| ir.cpp:1894:12:1894:20 | Load | m1892_4 |
| ir.cpp:1894:12:1894:38 | StoreValue | r1894_7 |
| ir.cpp:1894:24:1894:38 | Right | r1894_6 |
| ir.cpp:1894:29:1894:38 | Address | &:r1894_4 |
| ir.cpp:1894:29:1894:38 | Load | m1893_4 |
| ir.cpp:1894:29:1894:38 | Unary | r1894_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_7 |
@@ -9030,6 +9072,34 @@
| struct_init.cpp:20:6:20:25 | ChiPartial | partial:m20_3 |
| struct_init.cpp:20:6:20:25 | ChiTotal | total:m20_2 |
| struct_init.cpp:20:6:20:25 | SideEffect | ~m25_9 |
| struct_init.cpp:21:17:21:28 | Left | r21_3 |
| struct_init.cpp:21:17:21:28 | Left | r21_3 |
| struct_init.cpp:21:17:21:28 | SideEffect | ~m23_10 |
| struct_init.cpp:21:34:24:5 | Right | r21_4 |
| struct_init.cpp:21:34:24:5 | Right | r21_6 |
| struct_init.cpp:21:34:24:5 | Unary | r21_5 |
| struct_init.cpp:21:34:24:5 | Unary | r21_5 |
| struct_init.cpp:21:34:24:5 | Unary | r21_7 |
| struct_init.cpp:21:34:24:5 | Unary | r21_7 |
| struct_init.cpp:22:9:22:25 | Address | &:r22_1 |
| struct_init.cpp:22:9:22:25 | Address | &:r22_6 |
| struct_init.cpp:22:11:22:13 | ChiPartial | partial:m22_4 |
| struct_init.cpp:22:11:22:13 | ChiTotal | total:m21_2 |
| struct_init.cpp:22:11:22:13 | StoreValue | r22_3 |
| struct_init.cpp:22:11:22:13 | Unary | r22_2 |
| struct_init.cpp:22:16:22:23 | ChiPartial | partial:m22_8 |
| struct_init.cpp:22:16:22:23 | ChiTotal | total:m22_5 |
| struct_init.cpp:22:16:22:23 | StoreValue | r22_7 |
| struct_init.cpp:23:9:23:26 | Address | &:r23_1 |
| struct_init.cpp:23:9:23:26 | Address | &:r23_6 |
| struct_init.cpp:23:11:23:13 | ChiPartial | partial:m23_4 |
| struct_init.cpp:23:11:23:13 | ChiTotal | total:m22_9 |
| struct_init.cpp:23:11:23:13 | StoreValue | r23_3 |
| struct_init.cpp:23:11:23:13 | Unary | r23_2 |
| struct_init.cpp:23:16:23:24 | ChiPartial | partial:m23_9 |
| struct_init.cpp:23:16:23:24 | ChiTotal | total:m23_5 |
| struct_init.cpp:23:16:23:24 | StoreValue | r23_8 |
| struct_init.cpp:23:17:23:24 | Unary | r23_7 |
| struct_init.cpp:25:5:25:19 | CallTarget | func:r25_1 |
| struct_init.cpp:25:5:25:19 | ChiPartial | partial:m25_5 |
| struct_init.cpp:25:5:25:19 | ChiTotal | total:m20_4 |

View File

@@ -6841,6 +6841,28 @@ ir.cpp:
# 1231| v1231_8(void) = AliasedUse : ~m?
# 1231| v1231_9(void) = ExitFunction :
# 1232| int a
# 1232| Block 0
# 1232| v1232_1(void) = EnterFunction :
# 1232| mu1232_2(unknown) = AliasedDefinition :
# 1232| r1232_3(glval<int>) = VariableAddress[a] :
# 1232| r1232_4(int) = Constant[0] :
# 1232| mu1232_5(int) = Store[a] : &:r1232_3, r1232_4
# 1232| v1232_6(void) = ReturnVoid :
# 1232| v1232_7(void) = AliasedUse : ~m?
# 1232| v1232_8(void) = ExitFunction :
# 1233| int b
# 1233| Block 0
# 1233| v1233_1(void) = EnterFunction :
# 1233| mu1233_2(unknown) = AliasedDefinition :
# 1233| r1233_3(glval<int>) = VariableAddress[b] :
# 1233| r1233_4(int) = Constant[4] :
# 1233| mu1233_5(int) = Store[b] : &:r1233_3, r1233_4
# 1233| v1233_6(void) = ReturnVoid :
# 1233| v1233_7(void) = AliasedUse : ~m?
# 1233| v1233_8(void) = ExitFunction :
# 1240| void staticLocalWithConstructor(char const*)
# 1240| Block 0
# 1240| v1240_1(void) = EnterFunction :
@@ -10035,6 +10057,54 @@ ir.cpp:
# 1883| v1883_5(void) = AliasedUse : ~m?
# 1883| v1883_6(void) = ExitFunction :
# 1889| char global_template<char>
# 1889| Block 0
# 1889| v1889_1(void) = EnterFunction :
# 1889| mu1889_2(unknown) = AliasedDefinition :
# 1889| r1889_3(glval<char>) = VariableAddress[global_template] :
# 1889| r1889_4(char) = Constant[42] :
# 1889| mu1889_5(char) = Store[global_template] : &:r1889_3, r1889_4
# 1889| v1889_6(void) = ReturnVoid :
# 1889| v1889_7(void) = AliasedUse : ~m?
# 1889| v1889_8(void) = ExitFunction :
# 1889| int global_template<int>
# 1889| Block 0
# 1889| v1889_1(void) = EnterFunction :
# 1889| mu1889_2(unknown) = AliasedDefinition :
# 1889| r1889_3(glval<int>) = VariableAddress[global_template] :
# 1889| r1889_4(int) = Constant[42] :
# 1889| mu1889_5(int) = Store[global_template] : &:r1889_3, r1889_4
# 1889| v1889_6(void) = ReturnVoid :
# 1889| v1889_7(void) = AliasedUse : ~m?
# 1889| v1889_8(void) = ExitFunction :
# 1891| int test_global_template_int()
# 1891| Block 0
# 1891| v1891_1(void) = EnterFunction :
# 1891| mu1891_2(unknown) = AliasedDefinition :
# 1891| mu1891_3(unknown) = InitializeNonLocal :
# 1892| r1892_1(glval<int>) = VariableAddress[local_int] :
# 1892| r1892_2(glval<int>) = VariableAddress[global_template] :
# 1892| r1892_3(int) = Load[global_template] : &:r1892_2, ~m?
# 1892| mu1892_4(int) = Store[local_int] : &:r1892_1, r1892_3
# 1893| r1893_1(glval<char>) = VariableAddress[local_char] :
# 1893| r1893_2(glval<char>) = VariableAddress[global_template] :
# 1893| r1893_3(char) = Load[global_template] : &:r1893_2, ~m?
# 1893| mu1893_4(char) = Store[local_char] : &:r1893_1, r1893_3
# 1894| r1894_1(glval<int>) = VariableAddress[#return] :
# 1894| r1894_2(glval<int>) = VariableAddress[local_int] :
# 1894| r1894_3(int) = Load[local_int] : &:r1894_2, ~m?
# 1894| r1894_4(glval<char>) = VariableAddress[local_char] :
# 1894| r1894_5(char) = Load[local_char] : &:r1894_4, ~m?
# 1894| r1894_6(int) = Convert : r1894_5
# 1894| r1894_7(int) = Add : r1894_3, r1894_6
# 1894| mu1894_8(int) = Store[#return] : &:r1894_1, r1894_7
# 1891| r1891_4(glval<int>) = VariableAddress[#return] :
# 1891| v1891_5(void) = ReturnValue : &:r1891_4, ~m?
# 1891| v1891_6(void) = AliasedUse : ~m?
# 1891| v1891_7(void) = ExitFunction :
perf-regression.cpp:
# 6| void Big::Big()
# 6| Block 0
@@ -10320,6 +10390,34 @@ struct_init.cpp:
# 20| v20_5(void) = AliasedUse : ~m?
# 20| v20_6(void) = ExitFunction :
# 21| Info[] static_infos
# 21| Block 0
# 21| v21_1(void) = EnterFunction :
# 21| mu21_2(unknown) = AliasedDefinition :
# 21| r21_3(glval<Info[]>) = VariableAddress[static_infos] :
# 21| r21_4(int) = Constant[0] :
# 21| r21_5(glval<Info>) = PointerAdd[16] : r21_3, r21_4
# 22| r22_1(glval<char *>) = FieldAddress[name] : r21_5
# 22| r22_2(glval<char[2]>) = StringConstant["1"] :
# 22| r22_3(char *) = Convert : r22_2
# 22| mu22_4(char *) = Store[?] : &:r22_1, r22_3
# 22| r22_5(glval<..(*)(..)>) = FieldAddress[handler] : r21_5
# 22| r22_6(..(*)(..)) = FunctionAddress[handler1] :
# 22| mu22_7(..(*)(..)) = Store[?] : &:r22_5, r22_6
# 21| r21_6(int) = Constant[1] :
# 21| r21_7(glval<Info>) = PointerAdd[16] : r21_3, r21_6
# 23| r23_1(glval<char *>) = FieldAddress[name] : r21_7
# 23| r23_2(glval<char[2]>) = StringConstant["2"] :
# 23| r23_3(char *) = Convert : r23_2
# 23| mu23_4(char *) = Store[?] : &:r23_1, r23_3
# 23| r23_5(glval<..(*)(..)>) = FieldAddress[handler] : r21_7
# 23| r23_6(glval<..()(..)>) = FunctionAddress[handler2] :
# 23| r23_7(..(*)(..)) = CopyValue : r23_6
# 23| mu23_8(..(*)(..)) = Store[?] : &:r23_5, r23_7
# 21| v21_8(void) = ReturnVoid :
# 21| v21_9(void) = AliasedUse : ~m?
# 21| v21_10(void) = ExitFunction :
# 28| void declare_local_infos()
# 28| Block 0
# 28| v28_1(void) = EnterFunction :

View File

@@ -4,7 +4,9 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
| cpp11.cpp:50:15:50:16 | (no string representation) | Node should have one toString but has 0. |
missingToString
| Nodes without toString: 1 |
parameterCallable
localFlowIsLocal
readStepIsLocal

View File

@@ -233,3 +233,14 @@ void f_if_ternary_1(int b, int x, int y) {
if (b ? x : y) {
}
}
struct _A
{
unsigned int x;
const char *y;
} as[];
void regression_test(void)
{
static const void *a[] = {0 ? 0 : as};
}

View File

@@ -3,6 +3,7 @@
| nested.cpp:21:23:21:26 | fmt0 | The format string argument to snprintf should be constant to prevent security issues and other potential errors. |
| nested.cpp:79:32:79:38 | call to get_fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
| nested.cpp:87:18:87:20 | fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
| test.cpp:51:10:51:21 | call to make_message | The format string argument to printf should be constant to prevent security issues and other potential errors. |
| test.cpp:57:12:57:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
| test.cpp:60:12:60:21 | call to const_wash | The format string argument to printf should be constant to prevent security issues and other potential errors. |
| test.cpp:61:12:61:26 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |

View File

@@ -48,7 +48,7 @@ int main(int argc, char **argv) {
printf(choose_message(argc - 1), argc - 1); // GOOD
printf(messages[1]); // GOOD
printf(message); // GOOD
printf(make_message(argc - 1)); // BAD [NOT DETECTED]
printf(make_message(argc - 1)); // BAD
printf("Hello, World\n"); // GOOD
printf(_("Hello, World\n")); // GOOD
{

View File

@@ -1,3 +1,7 @@
## 1.5.1
No user-facing changes.
## 1.5.0
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.5.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.0
lastReleaseVersion: 1.5.1

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
version: 1.5.1-dev
version: 1.5.2-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,7 @@
## 1.5.1
No user-facing changes.
## 1.5.0
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.5.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.0
lastReleaseVersion: 1.5.1

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
version: 1.5.1-dev
version: 1.5.2-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,7 @@
## 0.6.1
No user-facing changes.
## 0.6.0
### Deprecated APIs

View File

@@ -0,0 +1,3 @@
## 0.6.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.0
lastReleaseVersion: 0.6.1

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all
version: 0.6.1-dev
version: 0.6.2-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp

View File

@@ -12,9 +12,9 @@ class Attribute extends Element, @cil_attribute {
Method getConstructor() { cil_attribute(this, _, result) }
/** Gets the type of this attribute. */
Type getType() { result = getConstructor().getDeclaringType() }
Type getType() { result = this.getConstructor().getDeclaringType() }
override string toString() { result = "[" + getType().getName() + "(...)]" }
override string toString() { result = "[" + this.getType().getName() + "(...)]" }
/** Gets the value of the `i`th argument of this attribute. */
string getArgument(int i) { cil_attribute_positional_argument(this, i, result) }
@@ -23,9 +23,9 @@ class Attribute extends Element, @cil_attribute {
string getNamedArgument(string name) { cil_attribute_named_argument(this, name, result) }
/** Gets an argument of this attribute, if any. */
string getAnArgument() { result = getArgument(_) or result = getNamedArgument(_) }
string getAnArgument() { result = this.getArgument(_) or result = this.getNamedArgument(_) }
override CS::Location getLocation() { result = getDeclaration().getLocation() }
override CS::Location getLocation() { result = this.getDeclaration().getLocation() }
}
/** A generic attribute to a declaration. */

View File

@@ -1,156 +1,183 @@
/**
* Provides classes for performing global (inter-procedural)
* content-sensitive data flow analyses.
*
* Unlike `DataFlow::Global`, we allow for data to be stored (possibly nested) inside
* contents of sources and sinks.
* We track flow paths of the form
*
* ```
* source --value-->* node
* (--read--> node --value-->* node)*
* --(non-value|value)-->* node
* (--store--> node --value-->* node)*
* --value-->* sink
* ```
*
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
* step, `--store-->` is a store step, and `--(non-value)-->` is a
* non-value-preserving flow step.
*
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
* steps, followed by 0 or more stores, with value-preserving steps allowed in
* between all other steps.
*/
private import csharp
private import codeql.util.Boolean
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Private as DataFlowPrivate
module ContentDataFlow {
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Private as DataFlowPrivate
private import DataFlowImplForContentDataFlow as DF
class Node = DF::Node;
class FlowFeature = DF::FlowFeature;
class ContentSet = DF::ContentSet;
// predicate stageStats = DF::stageStats/8;
/**
* An input configuration for content data flow.
*/
signature module ConfigSig {
/**
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
* of this abstract class. To create a configuration, extend this class with
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
* ```ql
* class MyAnalysisConfiguration extends ContentDataFlowConfiguration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isBarrier`.
* // Optionally override `isAdditionalFlowStep`.
* // Optionally override `getAFeature`.
* // Optionally override `accessPathLimit`.
* // Optionally override `isRelevantContent`.
* }
* ```
*
* Unlike `DataFlow::Configuration` (on which this class is based), we allow
* for data to be stored (possibly nested) inside contents of sources and sinks.
* We track flow paths of the form
*
* ```
* source --value-->* node
* (--read--> node --value-->* node)*
* --(non-value|value)-->* node
* (--store--> node --value-->* node)*
* --value-->* sink
* ```
*
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
* step, `--store-->` is a store step, and `--(non-value)-->` is a
* non-value-preserving flow step.
*
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
* steps, followed by 0 or more stores, with value-preserving steps allowed in
* between all other steps.
* Holds if `source` is a relevant data flow source.
*/
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
predicate isSource(DataFlow::Node source);
/**
* Holds if `source` is a relevant data flow source.
*/
abstract predicate isSource(Node source);
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(DataFlow::Node sink);
/**
* Holds if `sink` is a relevant data flow sink.
*/
abstract predicate isSink(Node sink);
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
default predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrier(DataFlow::Node node) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrier(Node node) { none() }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*/
default DataFlow::FlowFeature getAFeature() { none() }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*/
FlowFeature getAFeature() { none() }
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
default int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
default predicate isRelevantContent(DataFlow::ContentSet c) { any() }
}
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
predicate isRelevantContent(ContentSet c) { any() }
/**
* Constructs a global content data flow computation.
*/
module Global<ConfigSig ContentConfig> {
private module FlowConfig implements DataFlow::StateConfigSig {
class FlowState = State;
/**
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
* for this configuration. `preservesValue` indicates whether any of the additional
* flow steps defined by `isAdditionalFlowStep` are needed.
*
* For the source access path, `sourceAp`, the top of the stack represents the content
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow from `source.Field2.Field1`.
*
* For the sink access path, `sinkAp`, the top of the stack represents the content
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
*/
final predicate hasFlow(
Node source, AccessPath sourceAp, Node sink, AccessPath sinkAp, boolean preservesValue
) {
exists(DF::PathNode pathSource, DF::PathNode pathSink |
this.(ConfigurationAdapter).hasFlowPath(pathSource, pathSink) and
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
source = pathSource.getNode() and
sink = pathSink.getNode()
|
pathSink.getState().(InitState).decode(preservesValue)
or
pathSink.getState().(ReadState).decode(_, preservesValue)
or
pathSink.getState().(StoreState).decode(_, preservesValue)
predicate isSource(DataFlow::Node source, FlowState state) {
ContentConfig::isSource(source) and
state.(InitState).decode(true)
}
predicate isSink(DataFlow::Node sink, FlowState state) {
ContentConfig::isSink(sink) and
(
state instanceof InitState or
state instanceof StoreState or
state instanceof ReadState
)
}
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
storeStep(node1, state1, _, node2, state2) or
readStep(node1, state1, _, node2, state2) or
additionalStep(node1, state1, node2, state2)
}
predicate isAdditionalFlowStep = ContentConfig::isAdditionalFlowStep/2;
predicate isBarrier = ContentConfig::isBarrier/1;
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
DataFlow::FlowFeature getAFeature() { result = ContentConfig::getAFeature() }
// needed to record reads/stores inside summarized callables
predicate includeHiddenNodes() { any() }
}
private module Flow = DataFlow::GlobalWithState<FlowConfig>;
/**
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
* for this configuration. `preservesValue` indicates whether any of the additional
* flow steps defined by `isAdditionalFlowStep` are needed.
*
* For the source access path, `sourceAp`, the top of the stack represents the content
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow from `source.Field2.Field1`.
*
* For the sink access path, `sinkAp`, the top of the stack represents the content
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
*/
predicate flow(
DataFlow::Node source, AccessPath sourceAp, DataFlow::Node sink, AccessPath sinkAp,
boolean preservesValue
) {
exists(Flow::PathNode pathSource, Flow::PathNode pathSink |
Flow::flowPath(pathSource, pathSink) and
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
source = pathSource.getNode() and
sink = pathSink.getNode()
|
pathSink.getState().(InitState).decode(preservesValue)
or
pathSink.getState().(ReadState).decode(_, preservesValue)
or
pathSink.getState().(StoreState).decode(_, preservesValue)
)
}
private newtype TState =
TInitState(Boolean preservesValue) or
TStoreState(int size, Boolean preservesValue) {
size in [1 .. ContentConfig::accessPathLimit()]
} or
TReadState(int size, Boolean preservesValue) { size in [1 .. ContentConfig::accessPathLimit()] }
abstract private class State extends TState {
abstract string toString();
}
/** A flow state representing no reads or stores. */
private class InitState extends DF::FlowState {
private class InitState extends State, TInitState {
private boolean preservesValue_;
InitState() { this = "Init(" + preservesValue_ + ")" and preservesValue_ in [false, true] }
InitState() { this = TInitState(preservesValue_) }
override string toString() { result = "Init(" + preservesValue_ + ")" }
predicate decode(boolean preservesValue) { preservesValue = preservesValue_ }
}
/** A flow state representing that content has been stored into. */
private class StoreState extends DF::FlowState {
private class StoreState extends State, TStoreState {
private boolean preservesValue_;
private int size_;
StoreState() {
preservesValue_ in [false, true] and
size_ in [1 .. any(Configuration c).accessPathLimit()] and
this = "StoreState(" + size_ + "," + preservesValue_ + ")"
}
StoreState() { this = TStoreState(size_, preservesValue_) }
override string toString() { result = "StoreState(" + size_ + "," + preservesValue_ + ")" }
predicate decode(int size, boolean preservesValue) {
size = size_ and preservesValue = preservesValue_
@@ -158,15 +185,13 @@ module ContentDataFlow {
}
/** A flow state representing that content has been read from. */
private class ReadState extends DF::FlowState {
private class ReadState extends State, TReadState {
private boolean preservesValue_;
private int size_;
ReadState() {
preservesValue_ in [false, true] and
size_ in [1 .. any(Configuration c).accessPathLimit()] and
this = "ReadState(" + size_ + "," + preservesValue_ + ")"
}
ReadState() { this = TReadState(size_, preservesValue_) }
override string toString() { result = "ReadState(" + size_ + "," + preservesValue_ + ")" }
predicate decode(int size, boolean preservesValue) {
size = size_ and preservesValue = preservesValue_
@@ -174,12 +199,12 @@ module ContentDataFlow {
}
private predicate storeStep(
Node node1, DF::FlowState state1, ContentSet c, Node node2, StoreState state2,
Configuration config
DataFlow::Node node1, State state1, DataFlow::ContentSet c, DataFlow::Node node2,
StoreState state2
) {
exists(boolean preservesValue, int size |
storeSet(node1, c, node2, _, _) and
config.isRelevantContent(c) and
ContentConfig::isRelevantContent(c) and
state2.decode(size + 1, preservesValue)
|
state1.(InitState).decode(preservesValue) and size = 0
@@ -191,12 +216,12 @@ module ContentDataFlow {
}
private predicate readStep(
Node node1, DF::FlowState state1, ContentSet c, Node node2, ReadState state2,
Configuration config
DataFlow::Node node1, State state1, DataFlow::ContentSet c, DataFlow::Node node2,
ReadState state2
) {
exists(int size |
readSet(node1, c, node2) and
config.isRelevantContent(c) and
ContentConfig::isRelevantContent(c) and
state2.decode(size + 1, true)
|
state1.(InitState).decode(true) and
@@ -207,9 +232,9 @@ module ContentDataFlow {
}
private predicate additionalStep(
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2, Configuration config
DataFlow::Node node1, State state1, DataFlow::Node node2, State state2
) {
config.isAdditionalFlowStep(node1, node2) and
ContentConfig::isAdditionalFlowStep(node1, node2) and
(
state1 instanceof InitState and
state2.(InitState).decode(false)
@@ -221,40 +246,9 @@ module ContentDataFlow {
)
}
private class ConfigurationAdapter extends DF::Configuration instanceof Configuration {
final override predicate isSource(Node source, DF::FlowState state) {
Configuration.super.isSource(source) and
state.(InitState).decode(true)
}
final override predicate isSink(Node sink, DF::FlowState state) {
Configuration.super.isSink(sink) and
(
state instanceof InitState or
state instanceof StoreState or
state instanceof ReadState
)
}
final override predicate isAdditionalFlowStep(
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2
) {
storeStep(node1, state1, _, node2, state2, this) or
readStep(node1, state1, _, node2, state2, this) or
additionalStep(node1, state1, node2, state2, this)
}
final override predicate isBarrier(Node node) { Configuration.super.isBarrier(node) }
final override FlowFeature getAFeature() { result = Configuration.super.getAFeature() }
// needed to record reads/stores inside summarized callables
final override predicate includeHiddenNodes() { any() }
}
private newtype TAccessPath =
TAccessPathNil() or
TAccessPathCons(ContentSet head, AccessPath tail) {
TAccessPathCons(DataFlow::ContentSet head, AccessPath tail) {
nodeReachesStore(_, _, _, _, head, _, tail)
or
nodeReachesRead(_, _, _, _, head, tail, _)
@@ -263,7 +257,7 @@ module ContentDataFlow {
/** An access path. */
class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */
ContentSet getHead() { this = TAccessPathCons(result, _) }
DataFlow::ContentSet getHead() { this = TAccessPathCons(result, _) }
/** Gets the tail of this access path, if any. */
AccessPath getTail() { this = TAccessPathCons(_, result) }
@@ -278,7 +272,7 @@ module ContentDataFlow {
this = TAccessPathNil() and
result = ""
or
exists(ContentSet head, AccessPath tail |
exists(DataFlow::ContentSet head, AccessPath tail |
this = TAccessPathCons(head, tail) and
result = head + "." + tail
)
@@ -287,7 +281,7 @@ module ContentDataFlow {
// important to use `edges` and not `PathNode::getASuccessor()`, as the latter
// is not pruned for reachability
private predicate pathSucc = DF::PathGraph::edges/2;
private predicate pathSucc = Flow::PathGraph::edges/2;
/**
* Provides a big-step flow relation, where flow stops at read/store steps that
@@ -295,10 +289,10 @@ module ContentDataFlow {
* summarized callables can be recorded as well.
*/
private module BigStepFlow {
private predicate reachesSink(DF::PathNode node) {
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
private predicate reachesSink(Flow::PathNode node) {
FlowConfig::isSink(node.getNode(), node.getState())
or
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
pathSucc(node, mid) and
reachesSink(mid)
)
@@ -309,76 +303,72 @@ module ContentDataFlow {
* in the big-step relation.
*/
pragma[nomagic]
private predicate excludeStep(DF::PathNode pred, DF::PathNode succ) {
private predicate excludeStep(Flow::PathNode pred, Flow::PathNode succ) {
pathSucc(pred, succ) and
(
// we need to record reads/stores inside summarized callables
DF::PathGraph::subpaths(pred, _, _, succ)
Flow::PathGraph::subpaths(pred, _, _, succ)
or
// only allow flow into a summarized callable, as part of the big-step
// relation, when flow can reach a sink without going back out
DF::PathGraph::subpaths(pred, succ, _, _) and
Flow::PathGraph::subpaths(pred, succ, _, _) and
not reachesSink(succ)
or
// needed to record store steps
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
pred.getConfiguration())
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState())
or
// needed to record read steps
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
pred.getConfiguration())
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState())
)
}
pragma[nomagic]
private DataFlowCallable getEnclosingCallableImpl(DF::PathNode node) {
private DataFlowCallable getEnclosingCallableImpl(Flow::PathNode node) {
result = getNodeEnclosingCallable(node.getNode())
}
pragma[inline]
private DataFlowCallable getEnclosingCallable(DF::PathNode node) {
private DataFlowCallable getEnclosingCallable(Flow::PathNode node) {
pragma[only_bind_into](result) = getEnclosingCallableImpl(pragma[only_bind_out](node))
}
pragma[nomagic]
private predicate bigStepEntry(DF::PathNode node) {
node.getConfiguration() instanceof Configuration and
private predicate bigStepEntry(Flow::PathNode node) {
(
any(ConfigurationAdapter config).isSource(node.getNode(), node.getState())
FlowConfig::isSource(node.getNode(), node.getState())
or
excludeStep(_, node)
or
DF::PathGraph::subpaths(_, node, _, _)
Flow::PathGraph::subpaths(_, node, _, _)
)
}
pragma[nomagic]
private predicate bigStepExit(DF::PathNode node) {
node.getConfiguration() instanceof Configuration and
private predicate bigStepExit(Flow::PathNode node) {
(
bigStepEntry(node)
or
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
FlowConfig::isSink(node.getNode(), node.getState())
or
excludeStep(node, _)
or
DF::PathGraph::subpaths(_, _, node, _)
Flow::PathGraph::subpaths(_, _, node, _)
)
}
pragma[nomagic]
private predicate step(DF::PathNode pred, DF::PathNode succ) {
private predicate step(Flow::PathNode pred, Flow::PathNode succ) {
pathSucc(pred, succ) and
not excludeStep(pred, succ)
}
pragma[nomagic]
private predicate stepRec(DF::PathNode pred, DF::PathNode succ) {
private predicate stepRec(Flow::PathNode pred, Flow::PathNode succ) {
step(pred, succ) and
not bigStepEntry(pred)
}
private predicate stepRecPlus(DF::PathNode n1, DF::PathNode n2) = fastTC(stepRec/2)(n1, n2)
private predicate stepRecPlus(Flow::PathNode n1, Flow::PathNode n2) = fastTC(stepRec/2)(n1, n2)
/**
* Holds if there is flow `pathSucc+(pred) = succ`, and such a flow path does
@@ -386,8 +376,8 @@ module ContentDataFlow {
* steps.
*/
pragma[nomagic]
private predicate bigStep(DF::PathNode pred, DF::PathNode succ) {
exists(DF::PathNode mid |
private predicate bigStep(Flow::PathNode pred, Flow::PathNode succ) {
exists(Flow::PathNode mid |
bigStepEntry(pred) and
step(pred, mid)
|
@@ -399,13 +389,13 @@ module ContentDataFlow {
}
pragma[nomagic]
predicate bigStepNotLocal(DF::PathNode pred, DF::PathNode succ) {
predicate bigStepNotLocal(Flow::PathNode pred, Flow::PathNode succ) {
bigStep(pred, succ) and
not getEnclosingCallable(pred) = getEnclosingCallable(succ)
}
pragma[nomagic]
predicate bigStepMaybeLocal(DF::PathNode pred, DF::PathNode succ) {
predicate bigStepMaybeLocal(Flow::PathNode pred, Flow::PathNode succ) {
bigStep(pred, succ) and
getEnclosingCallable(pred) = getEnclosingCallable(succ)
}
@@ -422,55 +412,54 @@ module ContentDataFlow {
*/
pragma[nomagic]
private predicate nodeReaches(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node,
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
AccessPath reads, AccessPath stores
) {
exists(ConfigurationAdapter config |
node = source and
reads = scReads and
stores = scStores
|
config.hasFlowPath(source, _) and
node = source and
reads = scReads and
stores = scStores and
(
Flow::flowPath(source, _) and
scReads = TAccessPathNil() and
scStores = TAccessPathNil()
or
// the argument in a sub path can be reached, so we start flow from the sub path
// parameter, while recording the read/store summary context
exists(DF::PathNode arg |
exists(Flow::PathNode arg |
nodeReachesSubpathArg(_, _, _, arg, scReads, scStores) and
DF::PathGraph::subpaths(arg, source, _, _)
Flow::PathGraph::subpaths(arg, source, _, _)
)
)
or
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
BigStepFlow::bigStepMaybeLocal(mid, node)
)
or
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
BigStepFlow::bigStepNotLocal(mid, node) and
// when flow is not local, we cannot flow back out, so we may stop
// flow early when computing summary flow
any(ConfigurationAdapter config).hasFlowPath(source, _) and
Flow::flowPath(source, _) and
scReads = TAccessPathNil() and
scStores = TAccessPathNil()
)
or
// store step
exists(AccessPath storesMid, ContentSet c |
exists(AccessPath storesMid, DataFlow::ContentSet c |
nodeReachesStore(source, scReads, scStores, node, c, reads, storesMid) and
stores = TAccessPathCons(c, storesMid)
)
or
// read step
exists(AccessPath readsMid, ContentSet c |
exists(AccessPath readsMid, DataFlow::ContentSet c |
nodeReachesRead(source, scReads, scStores, node, c, readsMid, stores) and
reads = TAccessPathCons(c, readsMid)
)
or
// flow-through step; match outer stores/reads with inner store/read summary contexts
exists(DF::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
exists(Flow::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
nodeReachesSubpathArg(source, scReads, scStores, mid, innerScReads, innerScStores) and
subpathArgReachesOut(mid, innerScReads, innerScStores, node, reads, stores)
)
@@ -478,47 +467,45 @@ module ContentDataFlow {
pragma[nomagic]
private predicate nodeReachesStore(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
AccessPath reads, AccessPath stores
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
DataFlow::ContentSet c, AccessPath reads, AccessPath stores
) {
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
node.getConfiguration()) and
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState()) and
pathSucc(mid, node)
)
}
pragma[nomagic]
private predicate nodeReachesRead(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
AccessPath reads, AccessPath stores
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
DataFlow::ContentSet c, AccessPath reads, AccessPath stores
) {
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
node.getConfiguration()) and
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState()) and
pathSucc(mid, node)
)
}
pragma[nomagic]
private predicate nodeReachesSubpathArg(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode arg,
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode arg,
AccessPath reads, AccessPath stores
) {
nodeReaches(source, scReads, scStores, arg, reads, stores) and
DF::PathGraph::subpaths(arg, _, _, _)
Flow::PathGraph::subpaths(arg, _, _, _)
}
pragma[nomagic]
private predicate subpathArgReachesOut(
DF::PathNode arg, AccessPath scReads, AccessPath scStores, DF::PathNode out, AccessPath reads,
AccessPath stores
Flow::PathNode arg, AccessPath scReads, AccessPath scStores, Flow::PathNode out,
AccessPath reads, AccessPath stores
) {
exists(DF::PathNode source, DF::PathNode ret |
exists(Flow::PathNode source, Flow::PathNode ret |
nodeReaches(source, scReads, scStores, ret, reads, stores) and
DF::PathGraph::subpaths(arg, source, ret, out)
Flow::PathGraph::subpaths(arg, source, ret, out)
)
}
}

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -1,398 +0,0 @@
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides a `Configuration` class backwards-compatible interface to the data
* flow library.
*/
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
import FlowStateString
private import codeql.util.Unit
/**
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
* of this abstract class. To create a configuration, extend this class with
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
* ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isBarrier`.
* // Optionally override `isAdditionalFlowStep`.
* }
* ```
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
* the edges are those data-flow steps that preserve the value of the node
* along with any additional edges defined by `isAdditionalFlowStep`.
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
* and/or out-going edges from those nodes, respectively.
*
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
* ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
* Multiple configurations can coexist, but two classes extending
* `DataFlow::Configuration` should never depend on each other. One of them
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant data flow source.
*/
predicate isSource(Node source) { none() }
/**
* Holds if `source` is a relevant data flow source with the given initial
* `state`.
*/
predicate isSource(Node source, FlowState state) { none() }
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(Node sink) { none() }
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
* `node` from the data flow graph.
*/
predicate isBarrier(Node node) { none() }
/**
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
/**
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
*
* Holds if data flow through nodes guarded by `guard` is prohibited.
*/
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
*
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
}
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be
* taken at `node`.
*/
predicate allowImplicitRead(Node node, ContentSet c) { none() }
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
int fieldFlowBranchLimit() { result = 2 }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*
* These features are generally not relevant for typical end-to-end data flow
* queries, but should only be used for constructing paths that need to
* somehow be pluggable in another path context.
*/
FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
*/
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
*/
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
/**
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
*
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
deprecated int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
}
/**
* This class exists to prevent mutual recursion between the user-overridden
* member predicates of `Configuration` and the rest of the data-flow library.
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
override predicate hasFlow(Node source, Node sink) {
strictcount(Node n | this.isSource(n)) < 0
or
strictcount(Node n | this.isSource(n, _)) < 0
or
strictcount(Node n | this.isSink(n)) < 0
or
strictcount(Node n | this.isSink(n, _)) < 0
or
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
or
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
or
super.hasFlow(source, sink)
}
}
/** A bridge class to access the deprecated `isBarrierGuard`. */
private class BarrierGuardGuardedNodeBridge extends Unit {
abstract predicate guardedNode(Node n, Configuration config);
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
}
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
deprecated override predicate guardedNode(Node n, Configuration config) {
exists(BarrierGuard g |
config.isBarrierGuard(g) and
n = g.getAGuardedNode()
)
}
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
exists(BarrierGuard g |
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
}
}
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
config.isAdditionalFlowStep(_, result, _, _) or
config.isAdditionalFlowStep(_, _, _, result)
}
private newtype TConfigState =
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
getConfig(state).isSource(source, getState(state))
or
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
}
predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state))
or
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
}
predicate isBarrier(Node node) { none() }
predicate isBarrier(Node node, FlowState state) {
getConfig(state).isBarrier(node, getState(state)) or
getConfig(state).isBarrier(node) or
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
}
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {
any(Configuration config).allowImplicitRead(node, c)
}
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
predicate sourceGrouping(Node source, string sourceGroup) {
any(Configuration config).sourceGrouping(source, sourceGroup)
}
predicate sinkGrouping(Node sink, string sinkGroup) {
any(Configuration config).sinkGrouping(sink, sinkGroup)
}
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
final string toStringWithContext() { result = super.toStringWithContext() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
final predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
/** Holds if this node is a grouping of source nodes. */
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
/** Holds if this node is a grouping of sink nodes. */
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
sink0.getNode() = sink
)
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
predicate flowsTo = hasFlow/3;

View File

@@ -23,11 +23,15 @@ abstract class Sink extends DataFlow::Node { }
*/
abstract private class InstanceMethodSink extends Sink {
InstanceMethodSink() {
not exists(
SafeConstructorTrackingConfig safeConstructorTracking, DataFlow::Node safeTypeUsage,
MethodCall mc
|
safeConstructorTracking.hasFlow(_, safeTypeUsage) and
not exists(DataFlow::Node safeTypeUsage, MethodCall mc |
(
DataContractJsonSafeConstructorTracking::flowTo(safeTypeUsage) or
JavaScriptSerializerSafeConstructorTracking::flowTo(safeTypeUsage) or
XmlObjectSerializerDerivedConstructorTracking::flowTo(safeTypeUsage) or
XmlSerializerSafeConstructorTracking::flowTo(safeTypeUsage) or
DataContractSerializerSafeConstructorTracking::flowTo(safeTypeUsage) or
XmlMessageFormatterSafeConstructorTracking::flowTo(safeTypeUsage)
) and
mc.getQualifier() = safeTypeUsage.asExpr() and
mc.getAnArgument() = this.asExpr()
)
@@ -47,9 +51,11 @@ abstract class Sanitizer extends DataFlow::Node { }
private class RemoteSource extends Source instanceof RemoteFlowSource { }
/**
* DEPRECATED: Use `TaintToObjectMethodTracking` instead.
*
* User input to object method call deserialization flow tracking.
*/
class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
deprecated class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
TaintToObjectMethodTrackingConfig() { this = "TaintToObjectMethodTrackingConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -60,9 +66,27 @@ class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
}
/**
* User input to object method call deserialization flow tracking configuration.
*/
private module TaintToObjectMethodTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof InstanceMethodSink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/**
* User input to object method call deserialization flow tracking module.
*/
module TaintToObjectMethodTracking = TaintTracking::Global<TaintToObjectMethodTrackingConfig>;
/**
* DEPRECATED: Use `JsonConvertTracking` instead.
*
* User input to `JsonConvert` call deserialization flow tracking.
*/
class JsonConvertTrackingConfig extends TaintTracking::Configuration {
deprecated class JsonConvertTrackingConfig extends TaintTracking::Configuration {
JsonConvertTrackingConfig() { this = "JsonConvertTrackingConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -74,6 +98,24 @@ class JsonConvertTrackingConfig extends TaintTracking::Configuration {
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
}
/**
* User input to `JsonConvert` call deserialization flow tracking configuration.
*/
private module JsonConvertTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) {
sink instanceof NewtonsoftJsonConvertDeserializeObjectMethodSink
}
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/**
* User input to `JsonConvert` call deserialization flow tracking module.
*/
module JsonConvertTracking = TaintTracking::Global<JsonConvertTrackingConfig>;
/**
* DEPRECATED: Use `TypeNameTracking` instead.
*
@@ -186,9 +228,12 @@ private module TypeNameTrackingConfig implements DataFlow::ConfigSig {
module TypeNameTracking = DataFlow::Global<TypeNameTrackingConfig>;
/**
* DEPRECATED: Use `TaintToConstructorOrStaticMethodTracking` instead.
*
* User input to static method or constructor call deserialization flow tracking.
*/
class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Configuration {
deprecated class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Configuration
{
TaintToConstructorOrStaticMethodTrackingConfig() {
this = "TaintToConstructorOrStaticMethodTrackingConfig"
}
@@ -201,9 +246,28 @@ class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Conf
}
/**
* User input to static method or constructor call deserialization flow tracking configuration.
*/
private module TaintToConstructorOrStaticMethodTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof ConstructorOrStaticMethodSink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/**
* User input to static method or constructor call deserialization flow tracking module.
*/
module TaintToConstructorOrStaticMethodTracking =
TaintTracking::Global<TaintToConstructorOrStaticMethodTrackingConfig>;
/**
* DEPRECATED: Use `TaintToObjectTypeTracking` instead.
*
* User input to instance type flow tracking.
*/
class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
deprecated class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
TaintToObjectTypeTrackingConfig() { this = "TaintToObjectTypeTrackingConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -234,9 +298,47 @@ class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
}
/**
* User input to instance type flow tracking config.
*/
private module TaintToObjectTypeTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
mc.getTarget() instanceof UnsafeDeserializer and
sink.asExpr() = mc.getQualifier()
)
}
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodCall mc, Method m |
m = mc.getTarget() and
m.getDeclaringType().hasQualifiedName("System", "Type") and
m.hasName("GetType") and
m.isStatic() and
n1.asExpr() = mc.getArgument(0) and
n2.asExpr() = mc
)
or
exists(ObjectCreation oc |
n1.asExpr() = oc.getAnArgument() and
n2.asExpr() = oc and
oc.getObjectType() instanceof StrongTypeDeserializer
)
}
}
/**
* User input to instance type flow tracking module.
*/
module TaintToObjectTypeTracking = TaintTracking::Global<TaintToObjectTypeTrackingConfig>;
/**
* DEPRECATED: Use `WeakTypeCreationToUsageTracking` instead.
*
* Unsafe deserializer creation to usage tracking config.
*/
class WeakTypeCreationToUsageTrackingConfig extends TaintTracking2::Configuration {
deprecated class WeakTypeCreationToUsageTrackingConfig extends TaintTracking2::Configuration {
WeakTypeCreationToUsageTrackingConfig() { this = "DeserializerCreationToUsageTrackingConfig" }
override predicate isSource(DataFlow::Node source) {
@@ -255,13 +357,30 @@ class WeakTypeCreationToUsageTrackingConfig extends TaintTracking2::Configuratio
}
/**
* Safe deserializer creation to usage tracking config.
* Unsafe deserializer creation to usage tracking config.
*/
abstract class SafeConstructorTrackingConfig extends TaintTracking2::Configuration {
bindingset[this]
SafeConstructorTrackingConfig() { any() }
private module WeakTypeCreationToUsageTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc.getObjectType() instanceof WeakTypeDeserializer and
source.asExpr() = oc
)
}
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
mc.getTarget() instanceof UnsafeDeserializer and
sink.asExpr() = mc.getQualifier()
)
}
}
/**
* Unsafe deserializer creation to usage tracking module.
*/
module WeakTypeCreationToUsageTracking =
TaintTracking::Global<WeakTypeCreationToUsageTrackingConfig>;
/** BinaryFormatter */
private predicate isBinaryFormatterCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -367,13 +486,8 @@ private class DataContractJsonSerializerDeserializeMethodSink extends DataContra
}
}
private class DataContractJsonSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
DataContractJsonSafeConstructorTrackingConfiguration() {
this = "DataContractJsonSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module DataContractJsonSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -385,7 +499,7 @@ private class DataContractJsonSafeConstructorTrackingConfiguration extends SafeC
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isDataContractJsonSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -393,6 +507,9 @@ private class DataContractJsonSafeConstructorTrackingConfiguration extends SafeC
}
}
private module DataContractJsonSafeConstructorTracking =
TaintTracking::Global<DataContractJsonSafeConstructorTrackingConfig>;
/** JavaScriptSerializer */
private predicate isJavaScriptSerializerCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -417,13 +534,8 @@ private class JavaScriptSerializerDeserializeMethodSink extends JavaScriptSerial
}
}
private class JavaScriptSerializerSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
JavaScriptSerializerSafeConstructorTrackingConfiguration() {
this = "JavaScriptSerializerSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module JavaScriptSerializerSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -434,7 +546,7 @@ private class JavaScriptSerializerSafeConstructorTrackingConfiguration extends S
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isJavaScriptSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -442,6 +554,9 @@ private class JavaScriptSerializerSafeConstructorTrackingConfiguration extends S
}
}
private module JavaScriptSerializerSafeConstructorTracking =
TaintTracking::Global<JavaScriptSerializerSafeConstructorTrackingConfig>;
/** XmlObjectSerializer */
private predicate isXmlObjectSerializerCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -461,13 +576,8 @@ private class XmlObjectSerializerDeserializeMethodSink extends XmlObjectSerializ
}
}
private class XmlObjectSerializerDerivedConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
XmlObjectSerializerDerivedConstructorTrackingConfiguration() {
this = "XmlObjectSerializerDerivedConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module XmlObjectSerializerDerivedConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(ValueOrRefType declaringType |
@@ -481,7 +591,7 @@ private class XmlObjectSerializerDerivedConstructorTrackingConfiguration extends
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isXmlObjectSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -489,6 +599,9 @@ private class XmlObjectSerializerDerivedConstructorTrackingConfiguration extends
}
}
private module XmlObjectSerializerDerivedConstructorTracking =
TaintTracking::Global<XmlObjectSerializerDerivedConstructorTrackingConfig>;
/** XmlSerializer */
private predicate isXmlSerializerCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -507,13 +620,8 @@ private class XmlSerializerDeserializeMethodSink extends XmlSerializerSink {
}
}
private class XmlSerializerSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
XmlSerializerSafeConstructorTrackingConfiguration() {
this = "XmlSerializerSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module XmlSerializerSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -525,7 +633,7 @@ private class XmlSerializerSafeConstructorTrackingConfiguration extends SafeCons
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isXmlSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -533,6 +641,9 @@ private class XmlSerializerSafeConstructorTrackingConfiguration extends SafeCons
}
}
private module XmlSerializerSafeConstructorTracking =
TaintTracking::Global<XmlSerializerSafeConstructorTrackingConfig>;
/** DataContractSerializer */
private predicate isDataContractSerializerCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -555,13 +666,8 @@ private class DataContractSerializerDeserializeMethodSink extends DataContractSe
}
}
private class DataContractSerializerSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
DataContractSerializerSafeConstructorTrackingConfiguration() {
this = "DataContractSerializerSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module DataContractSerializerSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -573,7 +679,7 @@ private class DataContractSerializerSafeConstructorTrackingConfiguration extends
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isDataContractSerializerCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -581,6 +687,9 @@ private class DataContractSerializerSafeConstructorTrackingConfiguration extends
}
}
private module DataContractSerializerSafeConstructorTracking =
TaintTracking::Global<DataContractSerializerSafeConstructorTrackingConfig>;
/** XmlMessageFormatter */
private predicate isXmlMessageFormatterCall(MethodCall mc, Method m) {
m = mc.getTarget() and
@@ -599,13 +708,8 @@ private class XmlMessageFormatterDeserializeMethodSink extends XmlMessageFormatt
}
}
private class XmlMessageFormatterSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
{
XmlMessageFormatterSafeConstructorTrackingConfiguration() {
this = "XmlMessageFormatterSafeConstructorTrackingConfiguration"
}
override predicate isSource(DataFlow::Node source) {
private module XmlMessageFormatterSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(ObjectCreation oc |
oc = source.asExpr() and
exists(Constructor c |
@@ -617,7 +721,7 @@ private class XmlMessageFormatterSafeConstructorTrackingConfiguration extends Sa
)
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
isXmlMessageFormatterCall(mc, _) and
mc.getQualifier() = sink.asExpr()
@@ -625,6 +729,9 @@ private class XmlMessageFormatterSafeConstructorTrackingConfiguration extends Sa
}
}
private module XmlMessageFormatterSafeConstructorTracking =
TaintTracking::Global<XmlMessageFormatterSafeConstructorTrackingConfig>;
/** LosFormatter */
private predicate isLosFormatterCall(MethodCall mc, Method m) {
m = mc.getTarget() and

View File

@@ -1,3 +1,9 @@
## 0.6.1
### Minor Analysis Improvements
* Additional sinks modelling writes to unencrypted local files have been added to `ExternalLocationSink`, used by the `cs/cleartext-storage` and `cs/exposure-of-sensitive-information` queries.
## 0.6.0
### Minor Analysis Improvements

View File

@@ -13,43 +13,40 @@
import csharp
import semmle.code.csharp.security.dataflow.UnsafeDeserializationQuery
import DataFlow::PathGraph
import Flow::PathGraph
from DataFlow::PathNode userInput, DataFlow::PathNode deserializeCallArg
module Flow =
DataFlow::MergePathGraph3<TaintToObjectMethodTracking::PathNode,
TaintToConstructorOrStaticMethodTracking::PathNode, JsonConvertTracking::PathNode,
TaintToObjectMethodTracking::PathGraph, TaintToConstructorOrStaticMethodTracking::PathGraph,
JsonConvertTracking::PathGraph>;
from Flow::PathNode userInput, Flow::PathNode deserializeCallArg
where
exists(TaintToObjectMethodTrackingConfig taintTracking |
// all flows from user input to deserialization with weak and strong type serializers
taintTracking.hasFlowPath(userInput, deserializeCallArg)
) and
// all flows from user input to deserialization with weak and strong type serializers
TaintToObjectMethodTracking::flowPath(userInput.asPathNode1(), deserializeCallArg.asPathNode1()) and
// intersect with strong types, but user controlled or weak types deserialization usages
(
exists(
DataFlow::Node weakTypeUsage,
WeakTypeCreationToUsageTrackingConfig weakTypeDeserializerTracking, MethodCall mc
|
weakTypeDeserializerTracking.hasFlowTo(weakTypeUsage) and
exists(DataFlow::Node weakTypeUsage, MethodCall mc |
WeakTypeCreationToUsageTracking::flowTo(weakTypeUsage) and
mc.getQualifier() = weakTypeUsage.asExpr() and
mc.getAnArgument() = deserializeCallArg.getNode().asExpr()
)
or
exists(
TaintToObjectTypeTrackingConfig userControlledTypeTracking, DataFlow::Node taintedTypeUsage,
MethodCall mc
|
userControlledTypeTracking.hasFlowTo(taintedTypeUsage) and
exists(DataFlow::Node taintedTypeUsage, MethodCall mc |
TaintToObjectTypeTracking::flowTo(taintedTypeUsage) and
mc.getQualifier() = taintedTypeUsage.asExpr() and
mc.getAnArgument() = deserializeCallArg.getNode().asExpr()
)
)
or
// no type check needed - straightforward taint -> sink
exists(TaintToConstructorOrStaticMethodTrackingConfig taintTracking2 |
taintTracking2.hasFlowPath(userInput, deserializeCallArg)
)
TaintToConstructorOrStaticMethodTracking::flowPath(userInput.asPathNode2(),
deserializeCallArg.asPathNode2())
or
// JsonConvert static method call, but with additional unsafe typename tracking
exists(JsonConvertTrackingConfig taintTrackingJsonConvert, DataFlow::Node settingsCallArg |
taintTrackingJsonConvert.hasFlowPath(userInput, deserializeCallArg) and
exists(DataFlow::Node settingsCallArg |
JsonConvertTracking::flowPath(userInput.asPathNode3(), deserializeCallArg.asPathNode3()) and
TypeNameTracking::flow(_, settingsCallArg) and
deserializeCallArg.getNode().asExpr().getParent() = settingsCallArg.asExpr().getParent()
)

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
* Additional sinks modelling writes to unencrypted local files have been added to `ExternalLocationSink`, used by the `cs/cleartext-storage` and `cs/exposure-of-sensitive-information` queries.
## 0.6.1
### Minor Analysis Improvements
* Additional sinks modelling writes to unencrypted local files have been added to `ExternalLocationSink`, used by the `cs/cleartext-storage` and `cs/exposure-of-sensitive-information` queries.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.0
lastReleaseVersion: 0.6.1

View File

@@ -6,7 +6,7 @@ private import IRFunctionBaseInternal
private newtype TIRFunction =
TFunctionIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } or
TVarInitIRFunction(Language::GlobalVariable var) { IRConstruction::Raw::varHasIRFunc(var) }
TVarInitIRFunction(Language::Variable var) { IRConstruction::Raw::varHasIRFunc(var) }
/**
* The IR for a function. This base class contains only the predicates that are the same between all

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 0.6.1-dev
version: 0.6.2-dev
groups:
- csharp
- queries

View File

@@ -139,9 +139,9 @@ module ThroughFlowConfig implements DataFlow::StateConfigSig {
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
exists(DataFlowImplCommon::TypedContent tc |
DataFlowImplCommon::store(node1, tc, node2, _) and
isRelevantContent(tc.getContent()) and
exists(DataFlow::Content c |
DataFlowImplCommon::store(node1, c, node2, _, _) and
isRelevantContent(c) and
(
state1 instanceof TaintRead and state2.(TaintStore).getStep() = 1
or
@@ -178,7 +178,7 @@ string captureThroughFlow(DataFlowTargetApi api) {
string output
|
ThroughFlow::flow(p, returnNodeExt) and
returnNodeExt.getEnclosingCallable() = api and
returnNodeExt.(DataFlow::Node).getEnclosingCallable() = api and
input = parameterNodeAsInput(p) and
output = returnNodeAsOutput(returnNodeExt) and
input != output and

View File

@@ -121,7 +121,7 @@ class InstanceParameterNode = DataFlowPrivate::InstanceParameterNode;
pragma[nomagic]
private CS::Parameter getParameter(DataFlowImplCommon::ReturnNodeExt node, ParameterPosition pos) {
result = node.getEnclosingCallable().getParameter(pos.getPosition())
result = node.(DataFlow::Node).getEnclosingCallable().getParameter(pos.getPosition())
}
/**

View File

@@ -1,23 +1,23 @@
import csharp
import semmle.code.csharp.dataflow.internal.ContentDataFlow
import semmle.code.csharp.dataflow.internal.ContentDataFlow as ContentDataFlow
class Conf extends ContentDataFlow::Configuration {
Conf() { this = "ContentFlowConf" }
module ContentConfig implements ContentDataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation }
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation }
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
mc.getTarget().hasUndecoratedName("Sink") and
mc.getAnArgument() = sink.asExpr()
)
}
override int accessPathLimit() { result = 2 }
int accessPathLimit() { result = 2 }
}
module ContentFlow = ContentDataFlow::Global<ContentConfig>;
from
Conf conf, ContentDataFlow::Node source, ContentDataFlow::AccessPath sourceAp,
ContentDataFlow::Node sink, ContentDataFlow::AccessPath sinkAp, boolean preservesValue
where conf.hasFlow(source, sourceAp, sink, sinkAp, preservesValue)
DataFlow::Node source, ContentFlow::AccessPath sourceAp, DataFlow::Node sink,
ContentFlow::AccessPath sinkAp, boolean preservesValue
where ContentFlow::flow(source, sourceAp, sink, sinkAp, preservesValue)
select source, sourceAp, sink, sinkAp, preservesValue

View File

@@ -1,19 +1,12 @@
edges
| ../../../../resources/stubs/Newtonsoft.Json/13.0.1/Newtonsoft.Json.cs:930:20:930:20 | 4 : Int32 | Test.cs:19:32:19:52 | access to constant Auto : Int32 |
| Test.cs:9:46:9:49 | access to parameter data : TextBox | Test.cs:9:46:9:54 | access to property Text |
| Test.cs:17:46:17:49 | access to parameter data : TextBox | Test.cs:17:46:17:54 | access to property Text |
| Test.cs:19:32:19:52 | access to constant Auto : Int32 | Test.cs:17:57:20:9 | object creation of type JsonSerializerSettings |
| Test.cs:19:32:19:52 | access to constant Auto : TypeNameHandling | Test.cs:17:57:20:9 | object creation of type JsonSerializerSettings |
| Test.cs:25:46:25:49 | access to parameter data : TextBox | Test.cs:25:46:25:54 | access to property Text |
nodes
| ../../../../resources/stubs/Newtonsoft.Json/13.0.1/Newtonsoft.Json.cs:930:20:930:20 | 4 : Int32 | semmle.label | 4 : Int32 |
| Test.cs:9:46:9:49 | access to parameter data : TextBox | semmle.label | access to parameter data : TextBox |
| Test.cs:9:46:9:54 | access to property Text | semmle.label | access to property Text |
| Test.cs:17:46:17:49 | access to parameter data : TextBox | semmle.label | access to parameter data : TextBox |
| Test.cs:17:46:17:54 | access to property Text | semmle.label | access to property Text |
| Test.cs:17:57:20:9 | object creation of type JsonSerializerSettings | semmle.label | object creation of type JsonSerializerSettings |
| Test.cs:19:32:19:52 | access to constant Auto : Int32 | semmle.label | access to constant Auto : Int32 |
| Test.cs:19:32:19:52 | access to constant Auto : TypeNameHandling | semmle.label | access to constant Auto : TypeNameHandling |
| Test.cs:25:46:25:49 | access to parameter data : TextBox | semmle.label | access to parameter data : TextBox |
| Test.cs:25:46:25:54 | access to property Text | semmle.label | access to property Text |
subpaths

View File

@@ -1,5 +1,7 @@
.. _codeql-cli-reference:
:orphan:
CodeQL CLI reference
====================

View File

@@ -10,12 +10,9 @@ CodeQL CLI
- `CodeQL CLI reference <https://docs.github.com/en/code-security/codeql-cli/codeql-cli-reference>`__: Learn more about the files you can use when running CodeQL processes and the results format and exit codes that CodeQL generates.
- `CodeQL CLI manual <manual>`__: Detailed information about all the commands available with the CodeQL CLI.
- `CodeQL CLI manual <https://docs.github.com/en/code-security/codeql-cli/codeql-cli-manual>`__: Detailed information about all the commands available with the CodeQL CLI.
.. toctree::
:titlesonly:
:hidden:
using-the-codeql-cli
codeql-cli-reference
CodeQL CLI manual <https://codeql.github.com/docs/codeql-cli/manual>

View File

@@ -1,5 +1,7 @@
.. _using-the-codeql-cli:
:orphan:
Using the CodeQL CLI
====================

View File

@@ -153,7 +153,7 @@ To use standard code navigation features in the source code, you can right-click
If you're using an older database, code navigation commands such as **Go to Definition** and **Go to References** may not work.
To use code navigation, try unzipping the database and running ``codeql database cleanup <database>`` on the unzipped database using the CodeQL CLI. Then, re-add the database to Visual Studio Code.
For more information, see the `database cleanup <../../codeql-cli/manual/database-cleanup>`__ reference documentation.
For more information, see `database cleanup <https://docs.github.com/en/code-security/codeql-cli/codeql-cli-manual/database-cleanup>`__ in the documentation for CodeQL CLI.
Comparing query results
------------------------

View File

@@ -112,7 +112,7 @@ Configuring settings for testing queries locally
To increase the number of threads used for testing queries, you can update the **Running Tests > Number Of Threads** setting.
To pass additional arguments to the CodeQL CLI when running tests, you can update the **Running Tests > Additional Test Arguments** setting. For more information about the available arguments, see "`test run <https://codeql.github.com/docs/codeql-cli/manual/test-run/>`_" in the CodeQL CLI help.
To pass additional arguments to the CodeQL CLI when running tests, you can update the **Running Tests > Additional Test Arguments** setting. For more information about the available arguments, see `test run <https://docs.github.com/en/code-security/codeql-cli/codeql-cli-manual/test-run/>`_ in the documentation for CodeQL CLI.
Configuring settings for telemetry and data collection
--------------------------------------------------------

View File

@@ -1,18 +0,0 @@
.. pull-quote:: Other query-running commands
Queries run with ``database analyze`` have strict `metadata requirements
<https://docs.github.com/en/code-security/codeql-cli/using-the-codeql-cli/using-custom-queries-with-the-codeql-cli#including-query-metadata>`__. You can also execute queries using the following
plumbing-level subcommands:
- `database run-queries <../manual/database-run-queries>`__, which
outputs non-interpreted results in an intermediate binary format called
:ref:`BQRS <bqrs-file>`.
- `query run <../manual/query-run>`__, which will output BQRS files, or print
results tables directly to the command line. Viewing results directly in
the command line may be useful for iterative query development using the CLI.
Queries run with these commands don't have the same metadata requirements.
However, to save human-readable data you have to process each BQRS results
file using the `bqrs decode <../manual/bqrs-decode>`__ plumbing
subcommand. Therefore, for most use cases it's easiest to use ``database
analyze`` to directly generate interpreted results.

View File

@@ -3,7 +3,6 @@ package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"net/url"
"os"
@@ -22,28 +21,37 @@ import (
func usage() {
fmt.Fprintf(os.Stderr,
`%s is a wrapper script that installs dependencies and calls the extractor.
`%s is a wrapper script that installs dependencies and calls the extractor
When LGTM_SRC is not set, the script installs dependencies as described below, and then invokes the
extractor in the working directory.
Options:
--identify-environment
Produce an environment file specifying which Go version should be installed in the environment
so that autobuilding will be successful. The location of this file is controlled by the
environment variable CODEQL_EXTRACTOR_ENVIRONMENT_JSON, or defaults to 'environment.json' if
that is not set.
If LGTM_SRC is set, it checks for the presence of the files 'go.mod', 'Gopkg.toml', and
'glide.yaml' to determine how to install dependencies: if a 'Gopkg.toml' file is present, it uses
'dep ensure', if there is a 'glide.yaml' it uses 'glide install', and otherwise 'go get'.
Additionally, unless a 'go.mod' file is detected, it sets up a temporary GOPATH and moves all
source files into a folder corresponding to the package's import path before installing
dependencies.
Build behavior:
This behavior can be further customized using environment variables: setting LGTM_INDEX_NEED_GOPATH
to 'false' disables the GOPATH set-up, CODEQL_EXTRACTOR_GO_BUILD_COMMAND (or alternatively
LGTM_INDEX_BUILD_COMMAND), can be set to a newline-separated list of commands to run in order to
install dependencies, and LGTM_INDEX_IMPORT_PATH can be used to override the package import path,
which is otherwise inferred from the SEMMLE_REPO_URL or GITHUB_REPOSITORY environment variables.
When LGTM_SRC is not set, the script installs dependencies as described below, and then invokes the
extractor in the working directory.
In resource-constrained environments, the environment variable CODEQL_EXTRACTOR_GO_MAX_GOROUTINES
(or its legacy alias SEMMLE_MAX_GOROUTINES) can be used to limit the number of parallel goroutines
started by the extractor, which reduces CPU and memory requirements. The default value for this
variable is 32.
If LGTM_SRC is set, it checks for the presence of the files 'go.mod', 'Gopkg.toml', and
'glide.yaml' to determine how to install dependencies: if a 'Gopkg.toml' file is present, it uses
'dep ensure', if there is a 'glide.yaml' it uses 'glide install', and otherwise 'go get'.
Additionally, unless a 'go.mod' file is detected, it sets up a temporary GOPATH and moves all
source files into a folder corresponding to the package's import path before installing
dependencies.
This behavior can be further customized using environment variables: setting LGTM_INDEX_NEED_GOPATH
to 'false' disables the GOPATH set-up, CODEQL_EXTRACTOR_GO_BUILD_COMMAND (or alternatively
LGTM_INDEX_BUILD_COMMAND), can be set to a newline-separated list of commands to run in order to
install dependencies, and LGTM_INDEX_IMPORT_PATH can be used to override the package import path,
which is otherwise inferred from the SEMMLE_REPO_URL or GITHUB_REPOSITORY environment variables.
In resource-constrained environments, the environment variable CODEQL_EXTRACTOR_GO_MAX_GOROUTINES
(or its legacy alias SEMMLE_MAX_GOROUTINES) can be used to limit the number of parallel goroutines
started by the extractor, which reduces CPU and memory requirements. The default value for this
variable is 32.
`,
os.Args[0])
fmt.Fprintf(os.Stderr, "Usage:\n\n %s\n", os.Args[0])
@@ -92,6 +100,7 @@ func tryBuild(buildFile, cmd string, args ...string) bool {
return false
}
// Returns the import path of the package being built, or "" if it cannot be determined.
func getImportPath() (importpath string) {
importpath = os.Getenv("LGTM_INDEX_IMPORT_PATH")
if importpath == "" {
@@ -116,6 +125,8 @@ func getImportPath() (importpath string) {
return
}
// Returns the import path of the package being built from `repourl`, or "" if it cannot be
// determined.
func getImportPathFromRepoURL(repourl string) string {
// check for scp-like URL as in "git@github.com:github/codeql-go.git"
shorturl := regexp.MustCompile("^([^@]+@)?([^:]+):([^/].*?)(\\.git)?$")
@@ -182,6 +193,8 @@ const (
ModVendor
)
// argsForGoVersion returns the arguments to pass to the Go compiler for the given `ModMode` and
// Go version
func (m ModMode) argsForGoVersion(version string) []string {
switch m {
case ModUnset:
@@ -221,6 +234,7 @@ func checkVendor() bool {
return true
}
// Returns the directory containing the source code to be analyzed.
func getSourceDir() string {
srcdir := os.Getenv("LGTM_SRC")
if srcdir != "" {
@@ -236,6 +250,7 @@ func getSourceDir() string {
return srcdir
}
// Returns the appropriate DependencyInstallerMode for the current project
func getDepMode() DependencyInstallerMode {
if util.FileExists("go.mod") {
log.Println("Found go.mod, enabling go modules")
@@ -252,6 +267,33 @@ func getDepMode() DependencyInstallerMode {
return GoGetNoModules
}
// Tries to open `go.mod` and read a go directive, returning the version and whether it was found.
func tryReadGoDirective(depMode DependencyInstallerMode) (string, bool) {
version := ""
found := false
if depMode == GoGetWithModules {
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+)$`)
goMod, err := os.ReadFile("go.mod")
if err != nil {
log.Println("Failed to read go.mod to check for missing Go version")
} else {
matches := versionRe.FindSubmatch(goMod)
if matches != nil {
found = true
if len(matches) > 1 {
version := string(matches[1])
semverVersion := "v" + version
if semver.Compare(semverVersion, getEnvGoSemVer()) >= 0 {
diagnostics.EmitNewerGoVersionNeeded()
}
}
}
}
}
return version, found
}
// Returns the appropriate ModMode for the current project
func getModMode(depMode DependencyInstallerMode) ModMode {
if depMode == GoGetWithModules {
// if a vendor/modules.txt file exists, we assume that there are vendored Go dependencies, and
@@ -265,7 +307,8 @@ func getModMode(depMode DependencyInstallerMode) ModMode {
return ModUnset
}
func fixGoVendorIssues(modMode ModMode, depMode DependencyInstallerMode, goDirectiveFound bool) ModMode {
// fixGoVendorIssues fixes issues with go vendor for go version >= 1.14
func fixGoVendorIssues(modMode ModMode, depMode DependencyInstallerMode, goModVersionFound bool) ModMode {
if modMode == ModVendor {
// fix go vendor issues with go versions >= 1.14 when no go version is specified in the go.mod
// if this is the case, and dependencies were vendored with an old go version (and therefore
@@ -275,9 +318,9 @@ func fixGoVendorIssues(modMode ModMode, depMode DependencyInstallerMode, goDirec
// we work around this by adding an explicit go version of 1.13, which is the last version
// where this is not an issue
if depMode == GoGetWithModules {
if !goDirectiveFound {
if !goModVersionFound {
// if the go.mod does not contain a version line
modulesTxt, err := ioutil.ReadFile("vendor/modules.txt")
modulesTxt, err := os.ReadFile("vendor/modules.txt")
if err != nil {
log.Println("Failed to read vendor/modules.txt to check for mismatched Go version")
} else if explicitRe := regexp.MustCompile("(?m)^## explicit$"); !explicitRe.Match(modulesTxt) {
@@ -294,6 +337,7 @@ func fixGoVendorIssues(modMode ModMode, depMode DependencyInstallerMode, goDirec
return modMode
}
// Determines whether the project needs a GOPATH set up
func getNeedGopath(depMode DependencyInstallerMode, importpath string) bool {
needGopath := true
if depMode == GoGetWithModules {
@@ -316,6 +360,7 @@ func getNeedGopath(depMode DependencyInstallerMode, importpath string) bool {
return needGopath
}
// Try to update `go.mod` and `go.sum` if the go version is >= 1.16.
func tryUpdateGoModAndGoSum(modMode ModMode, depMode DependencyInstallerMode) {
// Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here:
if modMode != ModVendor && depMode == GoGetWithModules && semver.Compare(getEnvGoSemVer(), "v1.16") >= 0 {
@@ -361,10 +406,11 @@ type moveGopathInfo struct {
files []string
}
// Moves all files in `srcdir` to a temporary directory with the correct layout to be added to the GOPATH
func moveToTemporaryGopath(srcdir string, importpath string) moveGopathInfo {
// a temporary directory where everything is moved while the correct
// directory structure is created.
scratch, err := ioutil.TempDir(srcdir, "scratch")
scratch, err := os.MkdirTemp(srcdir, "scratch")
if err != nil {
log.Fatalf("Failed to create temporary directory %s in directory %s: %s\n",
scratch, srcdir, err.Error())
@@ -422,6 +468,8 @@ func moveToTemporaryGopath(srcdir string, importpath string) moveGopathInfo {
}
}
// Creates a path transformer file in the new directory to ensure paths in the source archive and the snapshot
// match the original source location, not the location we moved it to.
func createPathTransformerFile(newdir string) *os.File {
err := os.Chdir(newdir)
if err != nil {
@@ -430,13 +478,14 @@ func createPathTransformerFile(newdir string) *os.File {
// set up SEMMLE_PATH_TRANSFORMER to ensure paths in the source archive and the snapshot
// match the original source location, not the location we moved it to
pt, err := ioutil.TempFile("", "path-transformer")
pt, err := os.CreateTemp("", "path-transformer")
if err != nil {
log.Fatalf("Unable to create path transformer file: %s.", err.Error())
}
return pt
}
// Writes the path transformer file
func writePathTransformerFile(pt *os.File, realSrc, root, newdir string) {
_, err := pt.WriteString("#" + realSrc + "\n" + newdir + "//\n")
if err != nil {
@@ -452,6 +501,7 @@ func writePathTransformerFile(pt *os.File, realSrc, root, newdir string) {
}
}
// Adds `root` to GOPATH.
func setGopath(root string) {
// set/extend GOPATH
oldGopath := os.Getenv("GOPATH")
@@ -471,6 +521,8 @@ func setGopath(root string) {
log.Printf("GOPATH set to %s.\n", newGopath)
}
// Try to build the project without custom commands. If that fails, return a boolean indicating
// that we should install dependencies ourselves.
func buildWithoutCustomCommands(modMode ModMode) bool {
shouldInstallDependencies := false
// try to build the project
@@ -490,6 +542,7 @@ func buildWithoutCustomCommands(modMode ModMode) bool {
return shouldInstallDependencies
}
// Build the project with custom commands.
func buildWithCustomCommands(inst string) {
// write custom build commands into a script, then run it
var (
@@ -505,7 +558,7 @@ func buildWithCustomCommands(inst string) {
ext = ".sh"
header = "#! /bin/bash\nset -xe +u\n"
}
script, err := ioutil.TempFile("", "go-build-command-*"+ext)
script, err := os.CreateTemp("", "go-build-command-*"+ext)
if err != nil {
log.Fatalf("Unable to create temporary script holding custom build commands: %s\n", err.Error())
}
@@ -523,6 +576,7 @@ func buildWithCustomCommands(inst string) {
util.RunCmd(exec.Command(script.Name()))
}
// Install dependencies using the given dependency installer mode.
func installDependencies(depMode DependencyInstallerMode) {
// automatically determine command to install dependencies
var install *exec.Cmd
@@ -574,6 +628,7 @@ func installDependencies(depMode DependencyInstallerMode) {
util.RunCmd(install)
}
// Run the extractor.
func extract(depMode DependencyInstallerMode, modMode ModMode) {
extractor, err := util.GetExtractorPath()
if err != nil {
@@ -601,12 +656,8 @@ func extract(depMode DependencyInstallerMode, modMode ModMode) {
}
}
func main() {
if len(os.Args) > 1 {
usage()
os.Exit(2)
}
// Build the project and run the extractor.
func installDependenciesAndBuild() {
log.Printf("Autobuilder was built with %s, environment has %s\n", runtime.Version(), getEnvGoVersion())
srcdir := getSourceDir()
@@ -617,31 +668,14 @@ func main() {
// determine how to install dependencies and whether a GOPATH needs to be set up before
// extraction
depMode := getDepMode()
goDirectiveFound := false
if _, present := os.LookupEnv("GO111MODULE"); !present {
os.Setenv("GO111MODULE", "auto")
}
if depMode == GoGetWithModules {
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+)$`)
goMod, err := ioutil.ReadFile("go.mod")
if err != nil {
log.Println("Failed to read go.mod to check for missing Go version")
} else {
matches := versionRe.FindSubmatch(goMod)
if matches != nil {
goDirectiveFound = true
if len(matches) > 1 {
goDirectiveVersion := "v" + string(matches[1])
if semver.Compare(goDirectiveVersion, getEnvGoSemVer()) >= 0 {
diagnostics.EmitNewerGoVersionNeeded()
}
}
}
}
}
_, goModVersionFound := tryReadGoDirective(depMode)
modMode := getModMode(depMode)
modMode = fixGoVendorIssues(modMode, depMode, goDirectiveFound)
modMode = fixGoVendorIssues(modMode, depMode, goModVersionFound)
tryUpdateGoModAndGoSum(modMode, depMode)
@@ -692,3 +726,186 @@ func main() {
extract(depMode, modMode)
}
const minGoVersion = "1.11"
const maxGoVersion = "1.20"
// Check if `version` is lower than `minGoVersion` or higher than `maxGoVersion`. Note that for
// this comparison we ignore the patch part of the version, so 1.20.1 and 1.20 are considered
// equal.
func outsideSupportedRange(version string) bool {
short := semver.MajorMinor("v" + version)
return semver.Compare(short, "v"+minGoVersion) < 0 || semver.Compare(short, "v"+maxGoVersion) > 0
}
// Check if `v.goModVersion` or `v.goEnvVersion` are outside of the supported range. If so, emit
// a diagnostic and return an empty version to indicate that we should not attempt to install a
// different version of Go.
func checkForUnsupportedVersions(v versionInfo) (msg, version string) {
if v.goModVersionFound && outsideSupportedRange(v.goModVersion) {
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
") is outside of the supported range (" + minGoVersion + "-" + maxGoVersion + ")."
version = ""
diagnostics.EmitUnsupportedVersionGoMod(msg)
}
if v.goEnVersionFound && outsideSupportedRange(v.goEnvVersion) {
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
") is outside of the supported range (" + minGoVersion + "-" + maxGoVersion + ")."
version = ""
diagnostics.EmitUnsupportedVersionEnvironment(msg)
}
return msg, version
}
// Check if either `v.goEnVersionFound` or `v.goModVersionFound` are false. If so, emit
// a diagnostic and return the version to install, or the empty string if we should not attempt to
// install a version of Go. We assume that `checkForUnsupportedVersions` has already been
// called, so any versions that are found are within the supported range.
func checkForVersionsNotFound(v versionInfo) (msg, version string) {
if !v.goEnVersionFound && !v.goModVersionFound {
msg = "No version of Go installed and no `go.mod` file found. Writing an environment " +
"file specifying the maximum supported version of Go (" + maxGoVersion + ")."
version = maxGoVersion
diagnostics.EmitNoGoModAndNoGoEnv(msg)
}
if !v.goEnVersionFound && v.goModVersionFound {
msg = "No version of Go installed. Writing an environment file specifying the version " +
"of Go found in the `go.mod` file (" + v.goModVersion + ")."
version = v.goModVersion
diagnostics.EmitNoGoEnv(msg)
}
if v.goEnVersionFound && !v.goModVersionFound {
msg = "No `go.mod` file found. Version " + v.goEnvVersion + " installed in the environment."
version = ""
diagnostics.EmitNoGoMod(msg)
}
return msg, version
}
// Compare `v.goModVersion` and `v.goEnvVersion`. emit a diagnostic and return the version to
// install, or the empty string if we should not attempt to install a version of Go. We assume that
// `checkForUnsupportedVersions` and `checkForVersionsNotFound` have already been called, so both
// versions are found and are within the supported range.
func compareVersions(v versionInfo) (msg, version string) {
if semver.Compare("v"+v.goModVersion, "v"+v.goEnvVersion) > 0 {
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
") is lower than the version found in the `go.mod` file (" + v.goModVersion +
").\nWriting an environment file specifying the version of Go from the `go.mod` " +
"file (" + v.goModVersion + ")."
version = v.goModVersion
diagnostics.EmitVersionGoModHigherVersionEnvironment(msg)
} else {
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
") is high enough for the version found in the `go.mod` file (" + v.goModVersion + ")."
version = ""
diagnostics.EmitVersionGoModNotHigherVersionEnvironment(msg)
}
return msg, version
}
// Check the versions of Go found in the environment and in the `go.mod` file, and return a
// version to install. If the version is the empty string then no installation is required.
func getVersionToInstall(v versionInfo) (msg, version string) {
msg, version = checkForUnsupportedVersions(v)
if msg != "" {
return msg, version
}
msg, version = checkForVersionsNotFound(v)
if msg != "" {
return msg, version
}
msg, version = compareVersions(v)
return msg, version
}
// Write an environment file to the current directory. If `version` is the empty string then
// write an empty environment file, otherwise write an environment file specifying the version
// of Go to install. The path to the environment file is specified by the
// CODEQL_EXTRACTOR_ENVIRONMENT_JSON environment variable, or defaults to `environment.json`.
func writeEnvironmentFile(version string) {
var content string
if version == "" {
content = `{ "include": [] }`
} else {
content = `{ "include": [ { "go": { "version": "` + version + `" } } ] }`
}
filename, ok := os.LookupEnv("CODEQL_EXTRACTOR_ENVIRONMENT_JSON")
if !ok {
filename = "environment.json"
}
targetFile, err := os.Create(filename)
if err != nil {
log.Println("Failed to create environment file " + filename + ": ")
log.Println(err)
return
}
defer func() {
if err := targetFile.Close(); err != nil {
log.Println("Failed to close environment file " + filename + ":")
log.Println(err)
}
}()
_, err = targetFile.WriteString(content)
if err != nil {
log.Println("Failed to write to environment file " + filename + ": ")
log.Println(err)
}
}
type versionInfo struct {
goModVersion string // The version of Go found in the go directive in the `go.mod` file.
goModVersionFound bool // Whether a `go` directive was found in the `go.mod` file.
goEnvVersion string // The version of Go found in the environment.
goEnVersionFound bool // Whether an installation of Go was found in the environment.
}
func (v versionInfo) String() string {
return fmt.Sprintf(
"go.mod version: %s, go.mod directive found: %t, go env version: %s, go installation found: %t",
v.goModVersion, v.goModVersionFound, v.goEnvVersion, v.goEnVersionFound)
}
// Check if Go is installed in the environment.
func isGoInstalled() bool {
_, err := exec.LookPath("go")
return err == nil
}
// Get the version of Go to install and write it to an environment file.
func identifyEnvironment() {
var v versionInfo
depMode := getDepMode()
v.goModVersion, v.goModVersionFound = tryReadGoDirective(depMode)
v.goEnVersionFound = isGoInstalled()
if v.goEnVersionFound {
v.goEnvVersion = getEnvGoVersion()[2:]
}
msg, versionToInstall := getVersionToInstall(v)
log.Println(msg)
writeEnvironmentFile(versionToInstall)
}
func main() {
if len(os.Args) == 1 {
installDependenciesAndBuild()
} else if len(os.Args) == 2 && os.Args[1] == "--identify-environment" {
identifyEnvironment()
} else {
usage()
os.Exit(2)
}
}

View File

@@ -33,3 +33,56 @@ func TestParseGoVersion(t *testing.T) {
}
}
}
func TestGetVersionToInstall(t *testing.T) {
tests := map[versionInfo]string{
// checkForUnsupportedVersions()
// go.mod version below minGoVersion
{"0.0", true, "1.20.3", true}: "",
{"0.0", true, "9999.0", true}: "",
{"0.0", true, "1.2.2", true}: "",
{"0.0", true, "", false}: "",
// go.mod version above maxGoVersion
{"9999.0", true, "1.20.3", true}: "",
{"9999.0", true, "9999.0.1", true}: "",
{"9999.0", true, "1.1", true}: "",
{"9999.0", true, "", false}: "",
// Go installation found with version below minGoVersion
{"1.20", true, "1.2.2", true}: "",
{"1.11", true, "1.2.2", true}: "",
{"", false, "1.2.2", true}: "",
// Go installation found with version above maxGoVersion
{"1.20", true, "9999.0.1", true}: "",
{"1.11", true, "9999.0.1", true}: "",
{"", false, "9999.0.1", true}: "",
// checkForVersionsNotFound()
// Go installation not found, go.mod version in supported range
{"1.20", true, "", false}: "1.20",
{"1.11", true, "", false}: "1.11",
// Go installation not found, go.mod not found
{"", false, "", false}: maxGoVersion,
// Go installation found with version in supported range, go.mod not found
{"", false, "1.11.13", true}: "",
{"", false, "1.20.3", true}: "",
// compareVersions()
// Go installation found with version in supported range, go.mod version in supported range and go.mod version > go installation version
{"1.20", true, "1.11.13", true}: "1.20",
{"1.20", true, "1.12", true}: "1.20",
// Go installation found with version in supported range, go.mod version in supported range and go.mod version <= go installation version
// (Note comparisons ignore the patch version)
{"1.11", true, "1.20", true}: "",
{"1.11", true, "1.20.3", true}: "",
{"1.20", true, "1.20.3", true}: "",
}
for input, expected := range tests {
_, actual := getVersionToInstall(input)
if actual != expected {
t.Errorf("Expected getVersionToInstall(\"%s\") to be \"%s\", but got \"%s\".", input, expected, actual)
}
}
}

View File

@@ -2,7 +2,6 @@ package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
@@ -22,7 +21,7 @@ func main() {
buildSteps := os.Args[2]
haveRepo := false
content, err := ioutil.ReadFile(vars)
content, err := os.ReadFile(vars)
if err != nil {
log.Fatal(err)
}
@@ -34,7 +33,7 @@ func main() {
additionalVars += "SEMMLE_REPO_URL=${repository}\n"
}
content = append(content, []byte(additionalVars)...)
err = ioutil.WriteFile(vars, content, 0644)
err = os.WriteFile(vars, content, 0644)
if err != nil {
log.Fatal(err)
}
@@ -47,7 +46,7 @@ func main() {
<build export="%s">${semmle_dist}/language-packs/go/tools/platform/${semmle_platform}/bin/go-autobuilder</build>
</autoupdate>
`, export))
err = ioutil.WriteFile(buildSteps, content, 0644)
err = os.WriteFile(buildSteps, content, 0644)
if err != nil {
log.Fatal(err)
}

View File

@@ -6,7 +6,6 @@ import (
"fmt"
"go/scanner"
"go/token"
"io/ioutil"
"log"
"os"
"strings"
@@ -20,7 +19,7 @@ func main() {
defer csv.Flush()
for _, fileName := range flag.Args() {
src, err := ioutil.ReadFile(fileName)
src, err := os.ReadFile(fileName)
if err != nil {
log.Fatalf("Unable to read file %s.", fileName)
}

View File

@@ -30,6 +30,7 @@ type visibilityStruct struct {
}
var fullVisibility *visibilityStruct = &visibilityStruct{true, true, true}
var telemetryOnly *visibilityStruct = &visibilityStruct{false, false, true}
type locationStruct struct {
File string `json:"file,omitempty"`
@@ -192,3 +193,80 @@ func EmitRelativeImportPaths() {
noLocation,
)
}
func EmitUnsupportedVersionGoMod(msg string) {
emitDiagnostic(
"go/autobuilder/env-unsupported-version-in-go-mod",
"Unsupported Go version in `go.mod` file",
msg,
severityNote,
telemetryOnly,
noLocation,
)
}
func EmitUnsupportedVersionEnvironment(msg string) {
emitDiagnostic(
"go/autobuilder/env-unsupported-version-in-environment",
"Unsupported Go version in environment",
msg,
severityNote,
telemetryOnly,
noLocation,
)
}
func EmitNoGoModAndNoGoEnv(msg string) {
emitDiagnostic(
"go/autobuilder/env-no-go-mod-and-no-go-env",
"No `go.mod` file found and no Go version in environment",
msg,
severityNote,
telemetryOnly,
noLocation,
)
}
func EmitNoGoEnv(msg string) {
emitDiagnostic(
"go/autobuilder/env-no-go-env",
"No Go version in environment",
msg,
severityNote,
telemetryOnly,
noLocation,
)
}
func EmitNoGoMod(msg string) {
emitDiagnostic(
"go/autobuilder/env-no-go-mod",
"No `go.mod` file found",
msg,
severityNote,
telemetryOnly,
noLocation,
)
}
func EmitVersionGoModHigherVersionEnvironment(msg string) {
emitDiagnostic(
"go/autobuilder/env-version-go-mod-higher-than-go-env",
"The Go version in `go.mod` file is higher than the Go version in environment",
msg,
severityNote,
telemetryOnly,
noLocation,
)
}
func EmitVersionGoModNotHigherVersionEnvironment(msg string) {
emitDiagnostic(
"go/autobuilder/env-version-go-mod-lower-than-or-equal-to-go-env",
"The Go version in `go.mod` file is lower than or equal to the Go version in environment",
msg,
severityNote,
telemetryOnly,
noLocation,
)
}

View File

@@ -10,7 +10,6 @@ import (
"go/token"
"go/types"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
@@ -1807,7 +1806,7 @@ func extractNumLines(tw *trap.Writer, fileName string, ast *ast.File) {
// count lines of code by tokenizing
linesOfCode := 0
src, err := ioutil.ReadFile(fileName)
src, err := os.ReadFile(fileName)
if err != nil {
log.Fatalf("Unable to read file %s.", fileName)
}

View File

@@ -2,13 +2,14 @@ package extractor
import (
"fmt"
"golang.org/x/mod/modfile"
"io/ioutil"
"io"
"log"
"os"
"path/filepath"
"strings"
"golang.org/x/mod/modfile"
"github.com/github/codeql-go/extractor/dbscheme"
"github.com/github/codeql-go/extractor/srcarchive"
"github.com/github/codeql-go/extractor/trap"
@@ -45,7 +46,7 @@ func (extraction *Extraction) extractGoMod(path string) error {
if err != nil {
return fmt.Errorf("failed to open go.mod file %s: %s", path, err.Error())
}
data, err := ioutil.ReadAll(file)
data, err := io.ReadAll(file)
if err != nil {
return fmt.Errorf("failed to read go.mod file %s: %s", path, err.Error())
}

View File

@@ -1,13 +1,12 @@
package srcarchive
import (
"io/ioutil"
"os"
"testing"
)
func mkProjectLayout(projectLayoutSource string, t *testing.T) (*ProjectLayout, error) {
pt, err := ioutil.TempFile("", "path-transformer")
pt, err := os.CreateTemp("", "path-transformer")
if err != nil {
t.Fatalf("Unable to create temporary file for project layout: %s", err.Error())
}

View File

@@ -7,7 +7,6 @@ import (
"fmt"
"go/ast"
"go/types"
"io/ioutil"
"os"
"path/filepath"
"unicode/utf8"
@@ -51,7 +50,7 @@ func NewWriter(path string, pkg *packages.Package) (*Writer, error) {
if err != nil {
return nil, err
}
tmpFile, err := ioutil.TempFile(trapFileDir, filepath.Base(trapFilePath))
tmpFile, err := os.CreateTemp(trapFileDir, filepath.Base(trapFilePath))
if err != nil {
return nil, err
}

View File

@@ -1,3 +1,9 @@
## 0.5.1
### Minor Analysis Improvements
* Taking a slice is now considered a sanitizer for `SafeUrlFlow`.
## 0.5.0
### Deprecated APIs

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
## 0.5.1
### Minor Analysis Improvements
* Taking a slice is now considered a sanitizer for `SafeUrlFlow`.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.0
lastReleaseVersion: 0.5.1

View File

@@ -20,7 +20,7 @@ external string selectedSourceFile();
* A hook to customize the functions printed by this query.
*/
class Cfg extends PrintAstConfiguration {
override predicate shouldPrintFunction(FuncDecl func) { shouldPrintFile(func.getFile()) }
override predicate shouldPrintFunction(FuncDecl func) { this.shouldPrintFile(func.getFile()) }
override predicate shouldPrintFile(File file) {
file = getFileBySourceArchiveName(selectedSourceFile())

View File

@@ -1,5 +1,5 @@
name: codeql/go-all
version: 0.5.1-dev
version: 0.5.2-dev
groups: go
dbscheme: go.dbscheme
extractor: go

View File

@@ -19,7 +19,7 @@ class Error extends @error {
int getIndex() { errors(this, _, _, _, _, _, _, _, result) }
/** Gets the file in which this error was reported, if it can be determined. */
ExtractedOrExternalFile getFile() { hasLocationInfo(result.getAbsolutePath(), _, _, _, _) }
ExtractedOrExternalFile getFile() { this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) }
/**
* Holds if this element is at the specified location.
@@ -37,7 +37,7 @@ class Error extends @error {
}
/** Gets a textual representation of this error. */
string toString() { result = getMessage() }
string toString() { result = this.getMessage() }
}
/** An error reported by an unknown part of the Go frontend. */

View File

@@ -32,17 +32,19 @@ module HTML {
/**
* Holds if this is a toplevel element, that is, if it does not have a parent element.
*/
predicate isTopLevel() { not exists(getParent()) }
predicate isTopLevel() { not exists(this.getParent()) }
/**
* Gets the root HTML document element in which this element is contained.
*/
DocumentElement getDocument() { result = getRoot() }
DocumentElement getDocument() { result = this.getRoot() }
/**
* Gets the root element in which this element is contained.
*/
Element getRoot() { if isTopLevel() then result = this else result = getParent().getRoot() }
Element getRoot() {
if this.isTopLevel() then result = this else result = this.getParent().getRoot()
}
/**
* Gets the `i`th child element (0-based) of this element.
@@ -52,7 +54,7 @@ module HTML {
/**
* Gets a child element of this element.
*/
Element getChild() { result = getChild(_) }
Element getChild() { result = this.getChild(_) }
/**
* Gets the `i`th attribute (0-based) of this element.
@@ -62,13 +64,13 @@ module HTML {
/**
* Gets an attribute of this element.
*/
Attribute getAnAttribute() { result = getAttribute(_) }
Attribute getAnAttribute() { result = this.getAttribute(_) }
/**
* Gets an attribute of this element that has the given name.
*/
Attribute getAttributeByName(string name) {
result = getAnAttribute() and
result = this.getAnAttribute() and
result.getName() = name
}
@@ -77,7 +79,7 @@ module HTML {
*/
TextNode getTextNode() { result.getParent() = this }
override string toString() { result = "<" + getName() + ">...</>" }
override string toString() { result = "<" + this.getName() + ">...</>" }
}
/**
@@ -106,7 +108,7 @@ module HTML {
* Gets the root element in which the element to which this attribute
* belongs is contained.
*/
Element getRoot() { result = getElement().getRoot() }
Element getRoot() { result = this.getElement().getRoot() }
/**
* Gets the name of this attribute.
@@ -121,7 +123,7 @@ module HTML {
*/
string getValue() { xmlAttrs(this, _, _, result, _, _) }
override string toString() { result = getName() + "=" + getValue() }
override string toString() { result = this.getName() + "=" + this.getValue() }
}
/**
@@ -138,7 +140,7 @@ module HTML {
* ```
*/
class DocumentElement extends Element {
DocumentElement() { getName() = "html" }
DocumentElement() { this.getName() = "html" }
}
/**
@@ -155,7 +157,7 @@ module HTML {
class TextNode extends Locatable, @xmlcharacters {
TextNode() { exists(HtmlFile f | xmlChars(this, _, _, _, _, f)) }
override string toString() { result = getText() }
override string toString() { result = this.getText() }
/**
* Gets the content of this text node.
@@ -198,7 +200,7 @@ module HTML {
Element getParent() { xmlComments(this, _, result, _) }
/** Gets the text of this comment, not including delimiters. */
string getText() { result = toString().regexpCapture("(?s)<!--(.*)-->", 1) }
string getText() { result = this.toString().regexpCapture("(?s)<!--(.*)-->", 1) }
override string toString() { xmlComments(this, result, _, _) }

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