Merge remote-tracking branch 'codeql/main' into 'main'

Conflicts:
	config/identical-files.json
This commit is contained in:
Arthur Baars
2021-10-15 16:37:59 +02:00
1184 changed files with 51295 additions and 13569 deletions

View File

@@ -6,6 +6,8 @@ on:
- '.github/workflows/csv-coverage-pr-comment.yml'
- '*/ql/src/**/*.ql'
- '*/ql/src/**/*.qll'
- '*/ql/lib/**/*.ql'
- '*/ql/lib/**/*.qll'
- 'misc/scripts/library-coverage/*.py'
# input data files
- '*/documentation/library-coverage/cwe-sink.csv'

3
.gitignore vendored
View File

@@ -24,3 +24,6 @@
/codeql/
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
# Avoid committing cached package components
.codeql

View File

@@ -4,8 +4,8 @@ This open source repository contains the standard CodeQL libraries and queries t
## How do I learn CodeQL and run queries?
There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing CodeQL.
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode.html) extension to try out your queries on any open source project that's currently being analyzed.
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL.
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension to try out your queries on any open source project that's currently being analyzed.
## Contributing

View File

@@ -373,6 +373,7 @@
],
"Inline Test Expectations": [
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
"csharp/ql/test/TestUtilities/InlineExpectationsTest.qll",
"java/ql/test/TestUtilities/InlineExpectationsTest.qll",
"python/ql/test/TestUtilities/InlineExpectationsTest.qll",
"ruby/ql/test/TestUtilities/InlineExpectationsTest.qll"
@@ -478,5 +479,13 @@
"TypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
],
"CodeQL Tutorial": [
"cpp/ql/lib/tutorial.qll",
"csharp/ql/lib/tutorial.qll",
"java/ql/lib/tutorial.qll",
"javascript/ql/lib/tutorial.qll",
"python/ql/lib/tutorial.qll",
"ruby/ql/lib/tutorial.qll"
]
}

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* A new query (`cpp/cleartext-transmission`) has been added. This is similar to the `cpp/cleartext-storage-file`, `cpp/cleartext-storage-buffer` and `cpp/cleartext-storage-database` queries but looks for cases where sensitive information is most likely transmitted over a network.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* The "Uncontrolled data used in OS command" (`cpp/command-line-injection`) query has been enhanced to reduce false positive results and its `@precision` increased to `high`

View File

@@ -0,0 +1,3 @@
lgtm,codescanning
* Increase precision to high for the "Static buffer overflow" query
(`cpp/static-buffer-overflow`). This means the query is run and displayed by default on Code Scanning and LGTM.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Several improvements made to the `NullTermination.qll` library and the 'Potential improper null termination' (cpp/improper-null-termination). These changes reduce the number of false positive results for this query and related query 'User-controlled data may not be null terminated' (cpp/user-controlled-null-termination-tainted).

View File

@@ -0,0 +1,3 @@
codescanning
* Problems with extraction that in most cases won't break the analysis in a significant way are now reported as warnings rather than errors.
* The failed extractor invocations query now has severity `error`.

View File

@@ -275,9 +275,8 @@ class Declaration extends Locatable, @declaration {
* `getTemplateArgumentKind(0)`.
*/
final Locatable getTemplateArgumentKind(int index) {
if exists(getTemplateArgumentValue(index))
then result = getTemplateArgumentType(index)
else none()
exists(getTemplateArgumentValue(index)) and
result = getTemplateArgumentType(index)
}
/** Gets the number of template arguments for this declaration. */

View File

@@ -38,7 +38,7 @@ class Container extends Locatable, @container {
* DEPRECATED: Use `getLocation` instead.
* Gets a URL representing the location of this container.
*
* For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls).
* For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls).
*/
deprecated string getURL() { none() } // overridden by subclasses

View File

@@ -61,7 +61,7 @@ class Location extends @location {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -1650,7 +1650,6 @@ class RoutineType extends Type, @routinetype {
i = 0 and result = "" and not exists(this.getAParameterType())
or
(
exists(this.getParameterType(i)) and
if i < max(int j | exists(this.getParameterType(j)))
then
// Not the last one

View File

@@ -24,7 +24,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -108,7 +108,7 @@ class XMLParent extends @xmlparent {
}
/** Gets the text value contained in this XML parent. */
string getTextValue() { result = allCharactersString() }
string getTextValue() { result = this.allCharactersString() }
/** Gets a printable representation of this XML parent. */
string toString() { result = this.getName() }
@@ -119,7 +119,7 @@ class XMLFile extends XMLParent, File {
XMLFile() { xmlEncoding(this, _) }
/** Gets a printable representation of this XML file. */
override string toString() { result = getName() }
override string toString() { result = this.getName() }
/** Gets the name of this XML file. */
override string getName() { result = File.super.getAbsolutePath() }
@@ -129,14 +129,14 @@ class XMLFile extends XMLParent, File {
*
* Gets the path of this XML file.
*/
deprecated string getPath() { result = getAbsolutePath() }
deprecated string getPath() { result = this.getAbsolutePath() }
/**
* DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead.
*
* Gets the path of the folder that contains this XML file.
*/
deprecated string getFolder() { result = getParentContainer().getAbsolutePath() }
deprecated string getFolder() { result = this.getParentContainer().getAbsolutePath() }
/** Gets the encoding of this XML file. */
string getEncoding() { xmlEncoding(this, result) }
@@ -200,7 +200,7 @@ class XMLDTD extends XMLLocatable, @xmldtd {
*/
class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
/** Holds if this XML element has the given `name`. */
predicate hasName(string name) { name = getName() }
predicate hasName(string name) { name = this.getName() }
/** Gets the name of this XML element. */
override string getName() { xmlElements(this, result, _, _, _) }
@@ -239,7 +239,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
/** Gets a printable representation of this XML element. */
override string toString() { result = getName() }
override string toString() { result = this.getName() }
}
/**

View File

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

View File

@@ -1,6 +1,7 @@
import cpp
private import semmle.code.cpp.models.interfaces.ArrayFunction
private import semmle.code.cpp.models.implementations.Strcat
import semmle.code.cpp.dataflow.DataFlow
private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) {
exists(StackVariable v0, Expr val |
@@ -45,22 +46,28 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
ae.getRValue().getAChild*() = va
)
or
// Function call: library function, varargs function, function
// containing assembler code, or function where the relevant
// parameter is potentially added a null terminator.
// Function calls...
exists(Call c, Function f, int i |
e = c and
f = c.getTarget() and
not functionArgumentMustBeNullTerminated(f, i) and
c.getAnArgumentSubExpr(i) = va
|
not f.hasEntryPoint() and not functionArgumentMustBeNullTerminated(f, i)
// library function
not f.hasEntryPoint()
or
// function where the relevant parameter is potentially added a null terminator
mayAddNullTerminator(_, f.getParameter(i).getAnAccess())
or
// varargs function
f.isVarargs() and i >= f.getNumberOfParameters()
or
// function containing assembler code
exists(AsmStmt s | s.getEnclosingFunction() = f)
or
// function where the relevant parameter is returned (leaking it to be potentially null terminated elsewhere)
DataFlow::localFlow(DataFlow::parameterNode(f.getParameter(i)),
DataFlow::exprNode(any(ReturnStmt rs).getExpr()))
)
or
// Call without target (e.g., function pointer call)
@@ -93,6 +100,15 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
fc.getArgument(i) = va
)
or
// String argument to a formatting function (such as `printf`)
exists(int n, FormatLiteral fl |
fc.(FormattingFunctionCall).getConversionArgument(n) = va and
fl = fc.(FormattingFunctionCall).getFormat() and
fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
not fl.hasPrecision(n) // exclude: `%.*s`
)
or
// Call to a wrapper function that requires null termination
// (not itself adding a null terminator)
exists(Function wrapper, int i, Parameter p, VariableAccess use |

View File

@@ -253,6 +253,21 @@ class FormattingFunctionCall extends Expr {
// format arguments must be known
exists(getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
}
/**
* Gets the argument, if any, to which the output is written. If `isStream` is
* `true`, the output argument is a stream (that is, this call behaves like
* `fprintf`). If `isStream` is `false`, the output argument is a buffer (that
* is, this call behaves like `sprintf`)
*/
Expr getOutputArgument(boolean isStream) {
result =
this.(Call)
.getArgument(this.(Call)
.getTarget()
.(FormattingFunction)
.getOutputParameterIndex(isStream))
}
}
/**

View File

@@ -194,7 +194,7 @@ class BasicBlock extends ControlFlowNodeBase {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*
* Yields no result if this basic block spans multiple source files.
*/

View File

@@ -344,14 +344,13 @@ private int convertIntToType(int val, IntegralType t) {
then if val = 0 then result = 0 else result = 1
else
if t.isUnsigned()
then if val >= 0 and val.bitShiftRight(t.getSize() * 8) = 0 then result = val else none()
then val >= 0 and val.bitShiftRight(t.getSize() * 8) = 0 and result = val
else
if val >= 0 and val.bitShiftRight(t.getSize() * 8 - 1) = 0
then result = val
else
if (-(val + 1)).bitShiftRight(t.getSize() * 8 - 1) = 0
then result = val
else none()
else (
(-(val + 1)).bitShiftRight(t.getSize() * 8 - 1) = 0 and result = val
)
}
/**

View File

@@ -2139,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
tupleLimit < (tails - 1) * nodes
tupleLimit < (tails - 1) * nodes and
not tc.forceHighPrecision()
)
}
@@ -2973,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
if apa.getHead().forceHighPrecision()
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
}
/**
@@ -3248,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -4033,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -2139,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
tupleLimit < (tails - 1) * nodes
tupleLimit < (tails - 1) * nodes and
not tc.forceHighPrecision()
)
}
@@ -2973,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
if apa.getHead().forceHighPrecision()
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
}
/**
@@ -3248,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -4033,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -2139,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
tupleLimit < (tails - 1) * nodes
tupleLimit < (tails - 1) * nodes and
not tc.forceHighPrecision()
)
}
@@ -2973,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
if apa.getHead().forceHighPrecision()
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
}
/**
@@ -3248,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -4033,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -2139,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
tupleLimit < (tails - 1) * nodes
tupleLimit < (tails - 1) * nodes and
not tc.forceHighPrecision()
)
}
@@ -2973,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
if apa.getHead().forceHighPrecision()
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
}
/**
@@ -3248,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -4033,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -937,7 +937,7 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
}
override predicate relevantFor(DataFlowCallable callable) {
recordDataFlowCallSite(getCall(), callable)
recordDataFlowCallSite(this.getCall(), callable)
}
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
@@ -1236,6 +1236,13 @@ class TypedContent extends MkTypedContent {
/** 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) }
}
/**
@@ -1250,7 +1257,7 @@ abstract class AccessPathFront extends TAccessPathFront {
TypedContent getHead() { this = TFrontHead(result) }
predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) }
predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {

View File

@@ -2139,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
tupleLimit < (tails - 1) * nodes
tupleLimit < (tails - 1) * nodes and
not tc.forceHighPrecision()
)
}
@@ -2973,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
if apa.getHead().forceHighPrecision()
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
}
/**
@@ -3248,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -4033,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -219,15 +219,13 @@ class DataFlowExpr = Expr;
class DataFlowType = Type;
/** A function call relevant for data flow. */
class DataFlowCall extends Expr {
DataFlowCall() { this instanceof Call }
class DataFlowCall extends Expr instanceof Call {
/**
* Gets the nth argument for this call.
*
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
*/
Expr getArgument(int n) { result = this.(Call).getArgument(n) }
Expr getArgument(int n) { result = super.getArgument(n) }
/** Gets the data flow node corresponding to this call. */
ExprNode getNode() { result.getExpr() = this }
@@ -240,6 +238,12 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub impl
int accessPathLimit() { result = 5 }
/**
* Holds if access paths with `c` at their head always should be tracked at high
* precision. This disables adaptive access path precision for such access paths.
*/
predicate forceHighPrecision(Content c) { none() }
/** The unit type. */
private newtype TUnit = TMkUnit()

View File

@@ -101,7 +101,7 @@ class Node extends TNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
this.isSanitizer(node) or
defaultTaintSanitizer(node)
}
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
/** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
this.isSanitizerGuard(guard)
}
/**
* Holds if the additional taint propagation step from `node1` to `node2`
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2)
}

View File

@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
this.isSanitizer(node) or
defaultTaintSanitizer(node)
}
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
/** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
this.isSanitizerGuard(guard)
}
/**
* Holds if the additional taint propagation step from `node1` to `node2`
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2)
}

View File

@@ -4,7 +4,7 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
private import semmle.code.cpp.ir.dataflow.ResolveCall
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
@@ -355,20 +355,6 @@ predicate taintedIncludingGlobalVars(Expr source, Element tainted, string global
*/
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
/**
* Resolve potential target function(s) for `call`.
*
* If `call` is a call through a function pointer (`ExprCall`) or
* targets a virtual method, simple data flow analysis is performed
* in order to identify target(s).
*/
Function resolveCall(Call call) {
exists(CallInstruction callInstruction |
callInstruction.getAST() = call and
result = Dispatch::viableCallable(callInstruction)
)
}
/**
* Provides definitions for augmenting source/sink pairs with data-flow paths
* between them. From a `@kind path-problem` query, import this module in the
@@ -479,7 +465,7 @@ module TaintedWithPath {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -0,0 +1,23 @@
/**
* Provides a predicate for non-contextual virtual dispatch and function
* pointer resolution.
*/
import cpp
private import semmle.code.cpp.ir.ValueNumbering
private import internal.DataFlowDispatch
private import semmle.code.cpp.ir.IR
/**
* Resolve potential target function(s) for `call`.
*
* If `call` is a call through a function pointer (`ExprCall`) or its target is
* a virtual member function, simple data flow analysis is performed in order
* to identify the possible target(s).
*/
Function resolveCall(Call call) {
exists(CallInstruction callInstruction |
callInstruction.getAST() = call and
result = viableCallable(callInstruction)
)
}

View File

@@ -2139,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
tupleLimit < (tails - 1) * nodes
tupleLimit < (tails - 1) * nodes and
not tc.forceHighPrecision()
)
}
@@ -2973,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
if apa.getHead().forceHighPrecision()
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
}
/**
@@ -3248,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -4033,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -2139,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
tupleLimit < (tails - 1) * nodes
tupleLimit < (tails - 1) * nodes and
not tc.forceHighPrecision()
)
}
@@ -2973,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
if apa.getHead().forceHighPrecision()
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
}
/**
@@ -3248,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -4033,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -2139,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
tupleLimit < (tails - 1) * nodes
tupleLimit < (tails - 1) * nodes and
not tc.forceHighPrecision()
)
}
@@ -2973,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
if apa.getHead().forceHighPrecision()
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
}
/**
@@ -3248,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -4033,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -2139,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
tupleLimit < (tails - 1) * nodes
tupleLimit < (tails - 1) * nodes and
not tc.forceHighPrecision()
)
}
@@ -2973,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
if apa.getHead().forceHighPrecision()
then unfold = true
else
exists(int aps, int nodes, int apLimit, int tupleLimit |
aps = countPotentialAps(apa, config) and
nodes = countNodesUsingAccessPath(apa, config) and
accessPathCostLimits(apLimit, tupleLimit) and
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
)
}
/**
@@ -3248,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -4033,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -937,7 +937,7 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
}
override predicate relevantFor(DataFlowCallable callable) {
recordDataFlowCallSite(getCall(), callable)
recordDataFlowCallSite(this.getCall(), callable)
}
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
@@ -1236,6 +1236,13 @@ class TypedContent extends MkTypedContent {
/** 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) }
}
/**
@@ -1250,7 +1257,7 @@ abstract class AccessPathFront extends TAccessPathFront {
TypedContent getHead() { this = TFrontHead(result) }
predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) }
predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {

View File

@@ -466,6 +466,12 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub impl
int accessPathLimit() { result = 5 }
/**
* Holds if access paths with `c` at their head always should be tracked at high
* precision. This disables adaptive access path precision for such access paths.
*/
predicate forceHighPrecision(Content c) { none() }
/** The unit type. */
private newtype TUnit = TMkUnit()

View File

@@ -120,7 +120,7 @@ class Node extends TIRDataFlowNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -38,5 +38,8 @@ Instruction callOutput(CallInstruction call, FunctionOutput output) {
effect.getPrimaryInstruction() = call and
output.isParameterDerefOrQualifierObject(effect.getIndex())
)
// TODO: return value dereference
or
// TODO: modify this when we get return value dereferences
result = call and
output.isReturnValueDeref()
}

View File

@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
this.isSanitizer(node) or
defaultTaintSanitizer(node)
}
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
/** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
this.isSanitizerGuard(guard)
}
/**
* Holds if the additional taint propagation step from `node1` to `node2`
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2)
}

View File

@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
this.isSanitizer(node) or
defaultTaintSanitizer(node)
}
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
/** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
this.isSanitizerGuard(guard)
}
/**
* Holds if the additional taint propagation step from `node1` to `node2`
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2)
}

View File

@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
this.isSanitizer(node) or
defaultTaintSanitizer(node)
}
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
/** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
this.isSanitizerGuard(guard)
}
/**
* Holds if the additional taint propagation step from `node1` to `node2`
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2)
}

View File

@@ -24,7 +24,7 @@ class IRBlockBase extends TIRBlock {
final string toString() { result = getFirstInstruction(this).toString() }
/** Gets the source location of the first non-`Phi` instruction in this block. */
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() }
/**
* INTERNAL: Do not use.
@@ -39,7 +39,7 @@ class IRBlockBase extends TIRBlock {
) and
this =
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
// Ensure that the block containing `EnterFunction` always comes first.
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
@@ -59,15 +59,15 @@ class IRBlockBase extends TIRBlock {
* Get the `Phi` instructions that appear at the start of this block.
*/
final PhiInstruction getAPhiInstruction() {
Construction::getPhiInstructionBlockStart(result) = getFirstInstruction()
Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction()
}
/**
* Gets an instruction in this block. This includes `Phi` instructions.
*/
final Instruction getAnInstruction() {
result = getInstruction(_) or
result = getAPhiInstruction()
result = this.getInstruction(_) or
result = this.getAPhiInstruction()
}
/**
@@ -78,7 +78,9 @@ class IRBlockBase extends TIRBlock {
/**
* Gets the last instruction in this block.
*/
final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) }
final Instruction getLastInstruction() {
result = this.getInstruction(this.getInstructionCount() - 1)
}
/**
* Gets the number of non-`Phi` instructions in this block.
@@ -149,7 +151,7 @@ class IRBlock extends IRBlockBase {
* Block `A` dominates block `B` if any control flow path from the entry block of the function to
* block `B` must pass through block `A`. A block always dominates itself.
*/
final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block }
final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block }
/**
* Gets a block on the dominance frontier of this block.
@@ -159,8 +161,8 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock dominanceFrontier() {
dominates(result.getAPredecessor()) and
not strictlyDominates(result)
this.dominates(result.getAPredecessor()) and
not this.strictlyDominates(result)
}
/**
@@ -189,7 +191,7 @@ class IRBlock extends IRBlockBase {
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
* function must pass through block `A`. A block always post-dominates itself.
*/
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block }
/**
* Gets a block on the post-dominance frontier of this block.
@@ -199,16 +201,16 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock postPominanceFrontier() {
postDominates(result.getASuccessor()) and
not strictlyPostDominates(result)
this.postDominates(result.getASuccessor()) and
not this.strictlyPostDominates(result)
}
/**
* Holds if this block is reachable from the entry block of its function.
*/
final predicate isReachableFromFunctionEntry() {
this = getEnclosingIRFunction().getEntryBlock() or
getAPredecessor().isReachableFromFunctionEntry()
this = this.getEnclosingIRFunction().getEntryBlock() or
this.getAPredecessor().isReachableFromFunctionEntry()
}
}

View File

@@ -46,12 +46,12 @@ class Operand extends TStageOperand {
/**
* Gets the location of the source code for this operand.
*/
final Language::Location getLocation() { result = getUse().getLocation() }
final Language::Location getLocation() { result = this.getUse().getLocation() }
/**
* Gets the function that contains this operand.
*/
final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() }
final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() }
/**
* Gets the `Instruction` that consumes this operand.
@@ -74,7 +74,7 @@ class Operand extends TStageOperand {
*/
final Instruction getDef() {
result = this.getAnyDef() and
getDefinitionOverlap() instanceof MustExactlyOverlap
this.getDefinitionOverlap() instanceof MustExactlyOverlap
}
/**
@@ -82,7 +82,7 @@ class Operand extends TStageOperand {
*
* Gets the `Instruction` that consumes this operand.
*/
deprecated final Instruction getUseInstruction() { result = getUse() }
deprecated final Instruction getUseInstruction() { result = this.getUse() }
/**
* DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this
@@ -91,7 +91,7 @@ class Operand extends TStageOperand {
*
* Gets the `Instruction` whose result is the value of the operand.
*/
deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() }
deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() }
/**
* Gets the overlap relationship between the operand's definition and its use.
@@ -101,7 +101,9 @@ class Operand extends TStageOperand {
/**
* Holds if the result of the definition instruction does not exactly overlap this use.
*/
final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap }
final predicate isDefinitionInexact() {
not this.getDefinitionOverlap() instanceof MustExactlyOverlap
}
/**
* Gets a prefix to use when dumping the operand in an operand list.
@@ -121,7 +123,7 @@ class Operand extends TStageOperand {
* For example: `this:r3_5`
*/
final string getDumpString() {
result = getDumpLabel() + getInexactSpecifier() + getDefinitionId()
result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId()
}
/**
@@ -129,9 +131,9 @@ class Operand extends TStageOperand {
* definition is not modeled in SSA.
*/
private string getDefinitionId() {
result = getAnyDef().getResultId()
result = this.getAnyDef().getResultId()
or
not exists(getAnyDef()) and result = "m?"
not exists(this.getAnyDef()) and result = "m?"
}
/**
@@ -140,7 +142,7 @@ class Operand extends TStageOperand {
* the empty string.
*/
private string getInexactSpecifier() {
if isDefinitionInexact() then result = "~" else result = ""
if this.isDefinitionInexact() then result = "~" else result = ""
}
/**
@@ -155,7 +157,7 @@ class Operand extends TStageOperand {
* the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() }
/**
* Gets the language-neutral type of the value consumed by this operand. This is usually the same
@@ -164,7 +166,7 @@ class Operand extends TStageOperand {
* from the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
final IRType getIRType() { result = getLanguageType().getIRType() }
final IRType getIRType() { result = this.getLanguageType().getIRType() }
/**
* Gets the type of the value consumed by this operand. This is usually the same as the
@@ -173,7 +175,7 @@ class Operand extends TStageOperand {
* the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
final Language::Type getType() { getLanguageType().hasType(result, _) }
final Language::Type getType() { this.getLanguageType().hasType(result, _) }
/**
* Holds if the value consumed by this operand is a glvalue. If this
@@ -182,13 +184,13 @@ class Operand extends TStageOperand {
* not hold, the value of the operand represents a value whose type is
* given by `getType()`.
*/
final predicate isGLValue() { getLanguageType().hasType(_, true) }
final predicate isGLValue() { this.getLanguageType().hasType(_, true) }
/**
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
* a known constant size, this predicate does not hold.
*/
final int getSize() { result = getLanguageType().getByteSize() }
final int getSize() { result = this.getLanguageType().getByteSize() }
}
/**
@@ -205,7 +207,7 @@ class MemoryOperand extends Operand {
/**
* Gets the kind of memory access performed by the operand.
*/
MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() }
MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() }
/**
* Holds if the memory access performed by this operand will not always read from every bit in the
@@ -215,7 +217,7 @@ class MemoryOperand extends Operand {
* conservative estimate of the memory that might actually be accessed at runtime (for example,
* the global side effects of a function call).
*/
predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() }
predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() }
/**
* Returns the operand that holds the memory address from which the current operand loads its
@@ -223,8 +225,8 @@ class MemoryOperand extends Operand {
* is `r1`.
*/
final AddressOperand getAddressOperand() {
getMemoryAccess().usesAddressOperand() and
result.getUse() = getUse()
this.getMemoryAccess().usesAddressOperand() and
result.getUse() = this.getUse()
}
}
@@ -294,7 +296,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
result = unique(Instruction defInstr | hasDefinition(defInstr, _))
}
final override Overlap getDefinitionOverlap() { hasDefinition(_, result) }
final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) }
pragma[noinline]
private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
@@ -449,13 +451,17 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
final override Overlap getDefinitionOverlap() { result = overlap }
final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() }
final override string getDumpLabel() {
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
final override int getDumpSortOrder() {
result = 11 + this.getPredecessorBlock().getDisplayIndex()
}
final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() }
final override string getDumpLabel() {
result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":"
}
final override string getDumpId() {
result = this.getPredecessorBlock().getDisplayIndex().toString()
}
/**
* Gets the predecessor block from which this value comes.

View File

@@ -24,7 +24,7 @@ class IRBlockBase extends TIRBlock {
final string toString() { result = getFirstInstruction(this).toString() }
/** Gets the source location of the first non-`Phi` instruction in this block. */
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() }
/**
* INTERNAL: Do not use.
@@ -39,7 +39,7 @@ class IRBlockBase extends TIRBlock {
) and
this =
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
// Ensure that the block containing `EnterFunction` always comes first.
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
@@ -59,15 +59,15 @@ class IRBlockBase extends TIRBlock {
* Get the `Phi` instructions that appear at the start of this block.
*/
final PhiInstruction getAPhiInstruction() {
Construction::getPhiInstructionBlockStart(result) = getFirstInstruction()
Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction()
}
/**
* Gets an instruction in this block. This includes `Phi` instructions.
*/
final Instruction getAnInstruction() {
result = getInstruction(_) or
result = getAPhiInstruction()
result = this.getInstruction(_) or
result = this.getAPhiInstruction()
}
/**
@@ -78,7 +78,9 @@ class IRBlockBase extends TIRBlock {
/**
* Gets the last instruction in this block.
*/
final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) }
final Instruction getLastInstruction() {
result = this.getInstruction(this.getInstructionCount() - 1)
}
/**
* Gets the number of non-`Phi` instructions in this block.
@@ -149,7 +151,7 @@ class IRBlock extends IRBlockBase {
* Block `A` dominates block `B` if any control flow path from the entry block of the function to
* block `B` must pass through block `A`. A block always dominates itself.
*/
final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block }
final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block }
/**
* Gets a block on the dominance frontier of this block.
@@ -159,8 +161,8 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock dominanceFrontier() {
dominates(result.getAPredecessor()) and
not strictlyDominates(result)
this.dominates(result.getAPredecessor()) and
not this.strictlyDominates(result)
}
/**
@@ -189,7 +191,7 @@ class IRBlock extends IRBlockBase {
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
* function must pass through block `A`. A block always post-dominates itself.
*/
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block }
/**
* Gets a block on the post-dominance frontier of this block.
@@ -199,16 +201,16 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock postPominanceFrontier() {
postDominates(result.getASuccessor()) and
not strictlyPostDominates(result)
this.postDominates(result.getASuccessor()) and
not this.strictlyPostDominates(result)
}
/**
* Holds if this block is reachable from the entry block of its function.
*/
final predicate isReachableFromFunctionEntry() {
this = getEnclosingIRFunction().getEntryBlock() or
getAPredecessor().isReachableFromFunctionEntry()
this = this.getEnclosingIRFunction().getEntryBlock() or
this.getAPredecessor().isReachableFromFunctionEntry()
}
}

View File

@@ -46,12 +46,12 @@ class Operand extends TStageOperand {
/**
* Gets the location of the source code for this operand.
*/
final Language::Location getLocation() { result = getUse().getLocation() }
final Language::Location getLocation() { result = this.getUse().getLocation() }
/**
* Gets the function that contains this operand.
*/
final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() }
final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() }
/**
* Gets the `Instruction` that consumes this operand.
@@ -74,7 +74,7 @@ class Operand extends TStageOperand {
*/
final Instruction getDef() {
result = this.getAnyDef() and
getDefinitionOverlap() instanceof MustExactlyOverlap
this.getDefinitionOverlap() instanceof MustExactlyOverlap
}
/**
@@ -82,7 +82,7 @@ class Operand extends TStageOperand {
*
* Gets the `Instruction` that consumes this operand.
*/
deprecated final Instruction getUseInstruction() { result = getUse() }
deprecated final Instruction getUseInstruction() { result = this.getUse() }
/**
* DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this
@@ -91,7 +91,7 @@ class Operand extends TStageOperand {
*
* Gets the `Instruction` whose result is the value of the operand.
*/
deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() }
deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() }
/**
* Gets the overlap relationship between the operand's definition and its use.
@@ -101,7 +101,9 @@ class Operand extends TStageOperand {
/**
* Holds if the result of the definition instruction does not exactly overlap this use.
*/
final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap }
final predicate isDefinitionInexact() {
not this.getDefinitionOverlap() instanceof MustExactlyOverlap
}
/**
* Gets a prefix to use when dumping the operand in an operand list.
@@ -121,7 +123,7 @@ class Operand extends TStageOperand {
* For example: `this:r3_5`
*/
final string getDumpString() {
result = getDumpLabel() + getInexactSpecifier() + getDefinitionId()
result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId()
}
/**
@@ -129,9 +131,9 @@ class Operand extends TStageOperand {
* definition is not modeled in SSA.
*/
private string getDefinitionId() {
result = getAnyDef().getResultId()
result = this.getAnyDef().getResultId()
or
not exists(getAnyDef()) and result = "m?"
not exists(this.getAnyDef()) and result = "m?"
}
/**
@@ -140,7 +142,7 @@ class Operand extends TStageOperand {
* the empty string.
*/
private string getInexactSpecifier() {
if isDefinitionInexact() then result = "~" else result = ""
if this.isDefinitionInexact() then result = "~" else result = ""
}
/**
@@ -155,7 +157,7 @@ class Operand extends TStageOperand {
* the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() }
/**
* Gets the language-neutral type of the value consumed by this operand. This is usually the same
@@ -164,7 +166,7 @@ class Operand extends TStageOperand {
* from the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
final IRType getIRType() { result = getLanguageType().getIRType() }
final IRType getIRType() { result = this.getLanguageType().getIRType() }
/**
* Gets the type of the value consumed by this operand. This is usually the same as the
@@ -173,7 +175,7 @@ class Operand extends TStageOperand {
* the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
final Language::Type getType() { getLanguageType().hasType(result, _) }
final Language::Type getType() { this.getLanguageType().hasType(result, _) }
/**
* Holds if the value consumed by this operand is a glvalue. If this
@@ -182,13 +184,13 @@ class Operand extends TStageOperand {
* not hold, the value of the operand represents a value whose type is
* given by `getType()`.
*/
final predicate isGLValue() { getLanguageType().hasType(_, true) }
final predicate isGLValue() { this.getLanguageType().hasType(_, true) }
/**
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
* a known constant size, this predicate does not hold.
*/
final int getSize() { result = getLanguageType().getByteSize() }
final int getSize() { result = this.getLanguageType().getByteSize() }
}
/**
@@ -205,7 +207,7 @@ class MemoryOperand extends Operand {
/**
* Gets the kind of memory access performed by the operand.
*/
MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() }
MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() }
/**
* Holds if the memory access performed by this operand will not always read from every bit in the
@@ -215,7 +217,7 @@ class MemoryOperand extends Operand {
* conservative estimate of the memory that might actually be accessed at runtime (for example,
* the global side effects of a function call).
*/
predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() }
predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() }
/**
* Returns the operand that holds the memory address from which the current operand loads its
@@ -223,8 +225,8 @@ class MemoryOperand extends Operand {
* is `r1`.
*/
final AddressOperand getAddressOperand() {
getMemoryAccess().usesAddressOperand() and
result.getUse() = getUse()
this.getMemoryAccess().usesAddressOperand() and
result.getUse() = this.getUse()
}
}
@@ -294,7 +296,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
result = unique(Instruction defInstr | hasDefinition(defInstr, _))
}
final override Overlap getDefinitionOverlap() { hasDefinition(_, result) }
final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) }
pragma[noinline]
private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
@@ -449,13 +451,17 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
final override Overlap getDefinitionOverlap() { result = overlap }
final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() }
final override string getDumpLabel() {
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
final override int getDumpSortOrder() {
result = 11 + this.getPredecessorBlock().getDisplayIndex()
}
final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() }
final override string getDumpLabel() {
result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":"
}
final override string getDumpId() {
result = this.getPredecessorBlock().getDisplayIndex().toString()
}
/**
* Gets the predecessor block from which this value comes.

View File

@@ -24,7 +24,7 @@ class IRBlockBase extends TIRBlock {
final string toString() { result = getFirstInstruction(this).toString() }
/** Gets the source location of the first non-`Phi` instruction in this block. */
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() }
/**
* INTERNAL: Do not use.
@@ -39,7 +39,7 @@ class IRBlockBase extends TIRBlock {
) and
this =
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
// Ensure that the block containing `EnterFunction` always comes first.
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
@@ -59,15 +59,15 @@ class IRBlockBase extends TIRBlock {
* Get the `Phi` instructions that appear at the start of this block.
*/
final PhiInstruction getAPhiInstruction() {
Construction::getPhiInstructionBlockStart(result) = getFirstInstruction()
Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction()
}
/**
* Gets an instruction in this block. This includes `Phi` instructions.
*/
final Instruction getAnInstruction() {
result = getInstruction(_) or
result = getAPhiInstruction()
result = this.getInstruction(_) or
result = this.getAPhiInstruction()
}
/**
@@ -78,7 +78,9 @@ class IRBlockBase extends TIRBlock {
/**
* Gets the last instruction in this block.
*/
final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) }
final Instruction getLastInstruction() {
result = this.getInstruction(this.getInstructionCount() - 1)
}
/**
* Gets the number of non-`Phi` instructions in this block.
@@ -149,7 +151,7 @@ class IRBlock extends IRBlockBase {
* Block `A` dominates block `B` if any control flow path from the entry block of the function to
* block `B` must pass through block `A`. A block always dominates itself.
*/
final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block }
final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block }
/**
* Gets a block on the dominance frontier of this block.
@@ -159,8 +161,8 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock dominanceFrontier() {
dominates(result.getAPredecessor()) and
not strictlyDominates(result)
this.dominates(result.getAPredecessor()) and
not this.strictlyDominates(result)
}
/**
@@ -189,7 +191,7 @@ class IRBlock extends IRBlockBase {
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
* function must pass through block `A`. A block always post-dominates itself.
*/
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block }
/**
* Gets a block on the post-dominance frontier of this block.
@@ -199,16 +201,16 @@ class IRBlock extends IRBlockBase {
*/
pragma[noinline]
final IRBlock postPominanceFrontier() {
postDominates(result.getASuccessor()) and
not strictlyPostDominates(result)
this.postDominates(result.getASuccessor()) and
not this.strictlyPostDominates(result)
}
/**
* Holds if this block is reachable from the entry block of its function.
*/
final predicate isReachableFromFunctionEntry() {
this = getEnclosingIRFunction().getEntryBlock() or
getAPredecessor().isReachableFromFunctionEntry()
this = this.getEnclosingIRFunction().getEntryBlock() or
this.getAPredecessor().isReachableFromFunctionEntry()
}
}

View File

@@ -46,12 +46,12 @@ class Operand extends TStageOperand {
/**
* Gets the location of the source code for this operand.
*/
final Language::Location getLocation() { result = getUse().getLocation() }
final Language::Location getLocation() { result = this.getUse().getLocation() }
/**
* Gets the function that contains this operand.
*/
final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() }
final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() }
/**
* Gets the `Instruction` that consumes this operand.
@@ -74,7 +74,7 @@ class Operand extends TStageOperand {
*/
final Instruction getDef() {
result = this.getAnyDef() and
getDefinitionOverlap() instanceof MustExactlyOverlap
this.getDefinitionOverlap() instanceof MustExactlyOverlap
}
/**
@@ -82,7 +82,7 @@ class Operand extends TStageOperand {
*
* Gets the `Instruction` that consumes this operand.
*/
deprecated final Instruction getUseInstruction() { result = getUse() }
deprecated final Instruction getUseInstruction() { result = this.getUse() }
/**
* DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this
@@ -91,7 +91,7 @@ class Operand extends TStageOperand {
*
* Gets the `Instruction` whose result is the value of the operand.
*/
deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() }
deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() }
/**
* Gets the overlap relationship between the operand's definition and its use.
@@ -101,7 +101,9 @@ class Operand extends TStageOperand {
/**
* Holds if the result of the definition instruction does not exactly overlap this use.
*/
final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap }
final predicate isDefinitionInexact() {
not this.getDefinitionOverlap() instanceof MustExactlyOverlap
}
/**
* Gets a prefix to use when dumping the operand in an operand list.
@@ -121,7 +123,7 @@ class Operand extends TStageOperand {
* For example: `this:r3_5`
*/
final string getDumpString() {
result = getDumpLabel() + getInexactSpecifier() + getDefinitionId()
result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId()
}
/**
@@ -129,9 +131,9 @@ class Operand extends TStageOperand {
* definition is not modeled in SSA.
*/
private string getDefinitionId() {
result = getAnyDef().getResultId()
result = this.getAnyDef().getResultId()
or
not exists(getAnyDef()) and result = "m?"
not exists(this.getAnyDef()) and result = "m?"
}
/**
@@ -140,7 +142,7 @@ class Operand extends TStageOperand {
* the empty string.
*/
private string getInexactSpecifier() {
if isDefinitionInexact() then result = "~" else result = ""
if this.isDefinitionInexact() then result = "~" else result = ""
}
/**
@@ -155,7 +157,7 @@ class Operand extends TStageOperand {
* the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() }
/**
* Gets the language-neutral type of the value consumed by this operand. This is usually the same
@@ -164,7 +166,7 @@ class Operand extends TStageOperand {
* from the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
final IRType getIRType() { result = getLanguageType().getIRType() }
final IRType getIRType() { result = this.getLanguageType().getIRType() }
/**
* Gets the type of the value consumed by this operand. This is usually the same as the
@@ -173,7 +175,7 @@ class Operand extends TStageOperand {
* the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
final Language::Type getType() { getLanguageType().hasType(result, _) }
final Language::Type getType() { this.getLanguageType().hasType(result, _) }
/**
* Holds if the value consumed by this operand is a glvalue. If this
@@ -182,13 +184,13 @@ class Operand extends TStageOperand {
* not hold, the value of the operand represents a value whose type is
* given by `getType()`.
*/
final predicate isGLValue() { getLanguageType().hasType(_, true) }
final predicate isGLValue() { this.getLanguageType().hasType(_, true) }
/**
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
* a known constant size, this predicate does not hold.
*/
final int getSize() { result = getLanguageType().getByteSize() }
final int getSize() { result = this.getLanguageType().getByteSize() }
}
/**
@@ -205,7 +207,7 @@ class MemoryOperand extends Operand {
/**
* Gets the kind of memory access performed by the operand.
*/
MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() }
MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() }
/**
* Holds if the memory access performed by this operand will not always read from every bit in the
@@ -215,7 +217,7 @@ class MemoryOperand extends Operand {
* conservative estimate of the memory that might actually be accessed at runtime (for example,
* the global side effects of a function call).
*/
predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() }
predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() }
/**
* Returns the operand that holds the memory address from which the current operand loads its
@@ -223,8 +225,8 @@ class MemoryOperand extends Operand {
* is `r1`.
*/
final AddressOperand getAddressOperand() {
getMemoryAccess().usesAddressOperand() and
result.getUse() = getUse()
this.getMemoryAccess().usesAddressOperand() and
result.getUse() = this.getUse()
}
}
@@ -294,7 +296,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
result = unique(Instruction defInstr | hasDefinition(defInstr, _))
}
final override Overlap getDefinitionOverlap() { hasDefinition(_, result) }
final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) }
pragma[noinline]
private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
@@ -449,13 +451,17 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
final override Overlap getDefinitionOverlap() { result = overlap }
final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() }
final override string getDumpLabel() {
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
final override int getDumpSortOrder() {
result = 11 + this.getPredecessorBlock().getDisplayIndex()
}
final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() }
final override string getDumpLabel() {
result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":"
}
final override string getDumpId() {
result = this.getPredecessorBlock().getDisplayIndex().toString()
}
/**
* Gets the predecessor block from which this value comes.

View File

@@ -18,10 +18,11 @@ Overlap getOverlap(IntValue defStart, IntValue defEnd, IntValue useStart, IntVal
else
if isLE(defStart, useStart) and isGE(defEnd, useEnd)
then result instanceof MustTotallyOverlap
else
if isLE(defEnd, useStart) or isGE(defStart, useEnd)
then none()
else result instanceof MayPartiallyOverlap
else (
not isLE(defEnd, useStart) and
not isGE(defStart, useEnd) and
result instanceof MayPartiallyOverlap
)
}
/**

View File

@@ -36,3 +36,4 @@ private import implementations.Select
private import implementations.MySql
private import implementations.SqLite3
private import implementations.PostgreSql
private import implementations.System

View File

@@ -56,7 +56,7 @@ private class MallocAllocationFunction extends AllocationFunction {
]) and
sizeArg = 1
or
hasGlobalName(["HeapAlloc"]) and // HeapAlloc(heap, flags, size)
hasGlobalName("HeapAlloc") and // HeapAlloc(heap, flags, size)
sizeArg = 2
or
hasGlobalName([

View File

@@ -85,4 +85,6 @@ private class Recv extends AliasFunction, ArrayFunction, SideEffectFunction,
) and
description = "Buffer read by " + this.getName()
}
override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }
}

View File

@@ -60,4 +60,6 @@ private class Send extends AliasFunction, ArrayFunction, SideEffectFunction, Rem
override predicate hasRemoteFlowSink(FunctionInput input, string description) {
input.isParameterDeref(1) and description = "Buffer sent by " + this.getName()
}
override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }
}

View File

@@ -0,0 +1,43 @@
import cpp
import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.CommandExecution
/**
* A function for running a command using a command interpreter.
*/
private class SystemFunction extends CommandExecutionFunction, ArrayFunction, AliasFunction,
SideEffectFunction {
SystemFunction() {
hasGlobalOrStdName("system") or // system(command)
hasGlobalName("popen") or // popen(command, mode)
// Windows variants
hasGlobalName("_popen") or // _popen(command, mode)
hasGlobalName("_wpopen") or // _wpopen(command, mode)
hasGlobalName("_wsystem") // _wsystem(command)
}
override predicate hasCommandArgument(FunctionInput input) { input.isParameterDeref(0) }
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 or bufParam = 1 }
override predicate hasArrayInput(int bufParam) { bufParam = 0 or bufParam = 1 }
override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 }
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
override predicate parameterIsAlwaysReturned(int index) { none() }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() {
hasGlobalOrStdName("system") or
hasGlobalName("_wsystem")
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
(i = 0 or i = 1) and
buffer = true
}
}

View File

@@ -0,0 +1,23 @@
/**
* Provides classes for modeling functions that execute new programs by
* interpreting string data as shell commands. To use this QL library, create
* a QL class extending `CommandExecutionFunction` with a characteristic
* predicate that selects the function or set of functions you are modeling.
* Within that class, override the `hasCommandArgument` predicate to indicate
* which parameters are interpreted as shell commands.
*/
import cpp
import FunctionInputsAndOutputs
import semmle.code.cpp.models.Models
/**
* A function, such as `exec` or `popen` that starts a new process by
* interpreting a string as a shell command.
*/
abstract class CommandExecutionFunction extends Function {
/**
* Holds if `input` is interpreted as a shell command.
*/
abstract predicate hasCommandArgument(FunctionInput input);
}

View File

@@ -18,6 +18,12 @@ abstract class RemoteFlowSourceFunction extends Function {
* Holds if remote data described by `description` flows from `output` of a call to this function.
*/
abstract predicate hasRemoteFlowSource(FunctionOutput output, string description);
/**
* Holds if remote data from this source comes from a socket described by
* `input`. There is no result if a socket is not specified.
*/
predicate hasSocketInput(FunctionInput input) { none() }
}
/**
@@ -51,4 +57,10 @@ abstract class RemoteFlowSinkFunction extends Function {
* send over a network connection.
*/
abstract predicate hasRemoteFlowSink(FunctionInput input, string description);
/**
* Holds if data put into this sink is transmitted through a socket described
* by `input`. There is no result if a socket is not specified.
*/
predicate hasSocketInput(FunctionInput input) { none() }
}

View File

@@ -29,8 +29,7 @@ predicate nanExcludingComparison(ComparisonOperation guard, boolean polarity) {
*/
private predicate excludesNan(RangeSsaDefinition def, VariableAccess v) {
exists(VariableAccess inCond, ComparisonOperation guard, boolean branch, StackVariable lsv |
def.isGuardPhi(inCond, guard, branch) and
inCond.getTarget() = lsv and
def.isGuardPhi(lsv, inCond, guard, branch) and
v = def.getAUse(lsv) and
guard.getAnOperand() = inCond and
nanExcludingComparison(guard, branch)

View File

@@ -94,10 +94,11 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
predicate isPhiNode(StackVariable v) { exists(RangeSSA x | x.phi_node(v, this.(BasicBlock))) }
/**
* DEPRECATED: Use isGuardPhi/4 instead
* If this definition is a phi node corresponding to a guard,
* then return the variable access and the guard.
*/
predicate isGuardPhi(VariableAccess va, Expr guard, boolean branch) {
deprecated predicate isGuardPhi(VariableAccess va, Expr guard, boolean branch) {
guard_defn(va, guard, this, branch)
}
@@ -142,9 +143,8 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
// below excludes definitions which can only reach guard phi
// nodes by going through the corresponding guard.
not exists(VariableAccess access |
v = access.getTarget() and
pred.contains(access) and
this.isGuardPhi(access, _, _)
this.isGuardPhi(v, access, _, _)
)
)
}

View File

@@ -433,10 +433,7 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
private predicate phiDependsOnDef(
RangeSsaDefinition phi, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar
) {
exists(VariableAccess access, Expr guard |
access = v.getAnAccess() and
phi.isGuardPhi(access, guard, _)
|
exists(VariableAccess access, Expr guard | phi.isGuardPhi(v, access, guard, _) |
exprDependsOnDef(guard.(ComparisonOperation).getAnOperand(), srcDef, srcVar) or
exprDependsOnDef(access, srcDef, srcVar)
)
@@ -1204,8 +1201,7 @@ private float boolConversionUpperBound(Expr expr) {
*/
private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) {
exists(VariableAccess access, Expr guard, boolean branch, float defLB, float guardLB |
access = v.getAnAccess() and
phi.isGuardPhi(access, guard, branch) and
phi.isGuardPhi(v, access, guard, branch) and
lowerBoundFromGuard(guard, access, guardLB, branch) and
defLB = getFullyConvertedLowerBounds(access)
|
@@ -1230,8 +1226,7 @@ private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) {
/** See comment for `getPhiLowerBounds`, above. */
private float getPhiUpperBounds(StackVariable v, RangeSsaDefinition phi) {
exists(VariableAccess access, Expr guard, boolean branch, float defUB, float guardUB |
access = v.getAnAccess() and
phi.isGuardPhi(access, guard, branch) and
phi.isGuardPhi(v, access, guard, branch) and
upperBoundFromGuard(guard, access, guardUB, branch) and
defUB = getFullyConvertedUpperBounds(access)
|
@@ -1493,8 +1488,7 @@ private predicate isNEPhi(
exists(
ComparisonOperation cmp, boolean branch, Expr linearExpr, Expr rExpr, float p, float q, float r
|
access.getTarget() = v and
phi.isGuardPhi(access, cmp, branch) and
phi.isGuardPhi(v, access, cmp, branch) and
eqOpWithSwapAndNegate(cmp, linearExpr, rExpr, false, branch) and
v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!=` is too imprecise
r = getValue(rExpr).toFloat() and
@@ -1503,8 +1497,7 @@ private predicate isNEPhi(
)
or
exists(Expr op, boolean branch, Expr linearExpr, float p, float q |
access.getTarget() = v and
phi.isGuardPhi(access, op, branch) and
phi.isGuardPhi(v, access, op, branch) and
eqZeroWithNegate(op, linearExpr, false, branch) and
v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!` is too imprecise
linearAccess(linearExpr, access, p, q) and
@@ -1524,8 +1517,7 @@ private predicate isUnsupportedGuardPhi(Variable v, RangeSsaDefinition phi, Vari
or
eqZeroWithNegate(cmp, _, false, branch)
|
access.getTarget() = v and
phi.isGuardPhi(access, cmp, branch) and
phi.isGuardPhi(v, access, cmp, branch) and
not isNEPhi(v, phi, access, _)
)
}
@@ -1594,6 +1586,15 @@ private module SimpleRangeAnalysisCached {
result = min([max(getTruncatedUpperBounds(expr)), getGuardedUpperBound(expr)])
}
/** Holds if the upper bound of `expr` may have been widened. This means the the upper bound is in practice likely to be overly wide. */
cached
predicate upperBoundMayBeWidened(Expr e) {
isRecursiveExpr(e) and
// Widening is not a problem if the post-analysis in `getGuardedUpperBound` has overridden the widening.
// Note that the RHS of `<` may be multi-valued.
not getGuardedUpperBound(e) < getTruncatedUpperBounds(e)
}
/**
* Holds if `expr` has a provably empty range. For example:
*

View File

@@ -365,7 +365,7 @@ class GetsBW extends BufferWriteCall {
/**
* Gets the index of the parameter that is the maximum number of characters to be read.
*/
int getParamSize() { if exists(getArgument(1)) then result = 1 else none() }
int getParamSize() { exists(getArgument(1)) and result = 1 }
override Type getBufferType() { result = this.getTarget().getParameter(0).getUnspecifiedType() }

View File

@@ -4,42 +4,20 @@ import cpp
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.CommandExecution
/**
* A function for running a command using a command interpreter.
*/
class SystemFunction extends FunctionWithWrappers, ArrayFunction, AliasFunction, SideEffectFunction {
SystemFunction() {
hasGlobalOrStdName("system") or // system(command)
hasGlobalName("popen") or // popen(command, mode)
// Windows variants
hasGlobalName("_popen") or // _popen(command, mode)
hasGlobalName("_wpopen") or // _wpopen(command, mode)
hasGlobalName("_wsystem") // _wsystem(command)
}
override predicate interestingArg(int arg) { arg = 0 }
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 or bufParam = 1 }
override predicate hasArrayInput(int bufParam) { bufParam = 0 or bufParam = 1 }
override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 }
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
override predicate parameterIsAlwaysReturned(int index) { none() }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() {
hasGlobalOrStdName("system") or
hasGlobalName("_wsystem")
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
(i = 0 or i = 1) and
buffer = true
class SystemFunction extends FunctionWithWrappers instanceof CommandExecutionFunction {
override predicate interestingArg(int arg) {
exists(FunctionInput input |
this.(CommandExecutionFunction).hasCommandArgument(input) and
(
input.isParameterDerefOrQualifierObject(arg) or
input.isParameterOrQualifierAddress(arg)
)
)
}
}

View File

@@ -17,7 +17,7 @@
import cpp
import PrintfLike
private import TaintTracking
private import semmle.code.cpp.ir.dataflow.ResolveCall
bindingset[index]
private string toCause(Function func, int index) {

View File

@@ -245,7 +245,7 @@ svnchurn(
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `file`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
locations_default(
/** The location of an element that is not an expression or a statement. */
@@ -262,7 +262,7 @@ locations_default(
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `file`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
locations_stmt(
/** The location of a statement. */
@@ -279,7 +279,7 @@ locations_stmt(
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `file`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
locations_expr(
/** The location of an expression. */

1207
cpp/ql/lib/tutorial.qll Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -60,20 +60,18 @@ class SuppressionComment extends Comment {
/**
* The scope of an alert suppression comment.
*/
class SuppressionScope extends ElementBase {
SuppressionScope() { this instanceof SuppressionComment }
class SuppressionScope extends ElementBase instanceof SuppressionComment {
/**
* 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://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
super.covers(filepath, startline, startcolumn, endline, endcolumn)
}
}

View File

@@ -5,7 +5,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 9.3
* @precision medium
* @precision high
* @id cpp/static-buffer-overflow
* @tags reliability
* security
@@ -55,6 +55,8 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
loop.counter().getAnAccess() = bufaccess.getArrayOffset() and
// Ensure that we don't have an upper bound on the array index that's less than the buffer size.
not upperBound(bufaccess.getArrayOffset().getFullyConverted()) < bufaccess.bufferSize() and
// The upper bounds analysis must not have been widended
not upperBoundMayBeWidened(bufaccess.getArrayOffset().getFullyConverted()) and
msg =
"Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " +
@@ -130,11 +132,13 @@ predicate outOfBounds(BufferAccess bufaccess, string msg) {
(
access > size
or
access = size and not exists(AddressOfExpr addof | bufaccess = addof.getOperand())
access = size and
not exists(AddressOfExpr addof | bufaccess = addof.getOperand()) and
not exists(BuiltInOperationBuiltInOffsetOf offsetof | offsetof.getAChild() = bufaccess)
) and
msg =
"Potential buffer-overflow: '" + buf + "' has size " + size.toString() + " but '" + buf + "[" +
access.toString() + "]' is accessed here."
access.toString() + "]' may be accessed here."
)
}

View File

@@ -1,16 +0,0 @@
/**
* @name Extraction errors
* @description List all extraction errors for files in the source code directory.
* @kind diagnostic
* @id cpp/diagnostics/extraction-errors
*/
import cpp
import ExtractionErrors
from ExtractionError error
where
error instanceof ExtractionUnknownError or
exists(error.getFile().getRelativePath())
select error, "Extraction failed in " + error.getFile() + " with error " + error.getErrorMessage(),
error.getSeverity()

View File

@@ -1,12 +1,12 @@
/**
* Provides a common hierarchy of all types of errors that can occur during extraction.
* Provides a common hierarchy of all types of problems that can occur during extraction.
*/
import cpp
/*
* A note about how the C/C++ extractor emits diagnostics:
* When the extractor frontend encounters an error, it emits a diagnostic message,
* When the extractor frontend encounters a problem, it emits a diagnostic message,
* that includes a message, location and severity.
* However, that process is best-effort and may fail (e.g. due to lack of memory).
* Thus, if the extractor emitted at least one diagnostic of severity discretionary
@@ -15,17 +15,17 @@ import cpp
* In the common case, this means that a compilation during which one or more errors happened also gets
* the catch-all diagnostic.
* This diagnostic has the empty string as file path.
* We filter out these useless diagnostics if there is at least one error-level diagnostic
* We filter out these useless diagnostics if there is at least one warning-level diagnostic
* for the affected compilation in the database.
* Otherwise, we show it to indicate that something went wrong and that we
* don't know what exactly happened.
*/
/**
* An error that, if present, leads to a file being marked as non-successfully extracted.
* A problem with a file that, if present, leads to a file being marked as non-successfully extracted.
*/
class ReportableError extends Diagnostic {
ReportableError() {
class ReportableWarning extends Diagnostic {
ReportableWarning() {
(
this instanceof CompilerDiscretionaryError or
this instanceof CompilerError or
@@ -36,39 +36,35 @@ class ReportableError extends Diagnostic {
}
}
private newtype TExtractionError =
TReportableError(ReportableError err) or
private newtype TExtractionProblem =
TReportableWarning(ReportableWarning err) or
TCompilationFailed(Compilation c, File f) {
f = c.getAFileCompiled() and not c.normalTermination()
} or
// Show the catch-all diagnostic (see note above) only if we haven't seen any other error-level diagnostic
// for that compilation
TUnknownError(CompilerError err) {
not exists(ReportableError e | e.getCompilation() = err.getCompilation())
TUnknownProblem(CompilerError err) {
not exists(ReportableWarning e | e.getCompilation() = err.getCompilation())
}
/**
* Superclass for the extraction error hierarchy.
* Superclass for the extraction problem hierarchy.
*/
class ExtractionError extends TExtractionError {
/** Gets the string representation of the error. */
class ExtractionProblem extends TExtractionProblem {
/** Gets the string representation of the problem. */
string toString() { none() }
/** Gets the error message for this error. */
string getErrorMessage() { none() }
/** Gets the problem message for this problem. */
string getProblemMessage() { none() }
/** Gets the file this error occured in. */
/** Gets the file this problem occured in. */
File getFile() { none() }
/** Gets the location this error occured in. */
/** Gets the location this problem occured in. */
Location getLocation() { none() }
/** Gets the SARIF severity of this error. */
int getSeverity() {
// Unfortunately, we can't distinguish between errors and fatal errors in SARIF,
// so all errors have severity 2.
result = 2
}
/** Gets the SARIF severity of this problem. */
int getSeverity() { none() }
}
/**
@@ -79,7 +75,7 @@ class ExtractionError extends TExtractionError {
* - stack overflow
* - out of memory
*/
class ExtractionUnrecoverableError extends ExtractionError, TCompilationFailed {
class ExtractionUnrecoverableError extends ExtractionProblem, TCompilationFailed {
Compilation c;
File f;
@@ -89,49 +85,67 @@ class ExtractionUnrecoverableError extends ExtractionError, TCompilationFailed {
result = "Unrecoverable extraction error while compiling " + f.toString()
}
override string getErrorMessage() { result = "unrecoverable compilation failure." }
override string getProblemMessage() { result = "unrecoverable compilation failure." }
override File getFile() { result = f }
override Location getLocation() { result = f.getLocation() }
override int getSeverity() {
// These extractor errors break the analysis, so we mark them in SARIF as
// [errors](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc10541338).
result = 2
}
}
/**
* A recoverable extraction error.
* A recoverable extraction warning.
* These are compiler errors from the frontend.
* Upon encountering one of these, we still continue extraction, but the
* database will be incomplete for that file.
*/
class ExtractionRecoverableError extends ExtractionError, TReportableError {
ReportableError err;
class ExtractionRecoverableWarning extends ExtractionProblem, TReportableWarning {
ReportableWarning err;
ExtractionRecoverableError() { this = TReportableError(err) }
ExtractionRecoverableWarning() { this = TReportableWarning(err) }
override string toString() { result = "Recoverable extraction error: " + err }
override string getErrorMessage() { result = err.getFullMessage() }
override string getProblemMessage() { result = err.getFullMessage() }
override File getFile() { result = err.getFile() }
override Location getLocation() { result = err.getLocation() }
override int getSeverity() {
// Recoverable extraction problems don't tend to break the analysis, so we mark them in SARIF as
// [warnings](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc10541338).
result = 1
}
}
/**
* An unknown error happened during extraction.
* These are only displayed if we know that we encountered an error during extraction,
* An unknown problem happened during extraction.
* These are only displayed if we know that we encountered an problem during extraction,
* but, for some reason, failed to emit a proper diagnostic with location information
* and error message.
* and problem message.
*/
class ExtractionUnknownError extends ExtractionError, TUnknownError {
class ExtractionUnknownProblem extends ExtractionProblem, TUnknownProblem {
CompilerError err;
ExtractionUnknownError() { this = TUnknownError(err) }
ExtractionUnknownProblem() { this = TUnknownProblem(err) }
override string toString() { result = "Unknown extraction error: " + err }
override string toString() { result = "Unknown extraction problem: " + err }
override string getErrorMessage() { result = err.getFullMessage() }
override string getProblemMessage() { result = err.getFullMessage() }
override File getFile() { result = err.getFile() }
override Location getLocation() { result = err.getLocation() }
override int getSeverity() {
// Unknown extraction problems don't tend to break the analysis, so we mark them in SARIF as
// [warnings](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc10541338).
result = 1
}
}

View File

@@ -0,0 +1,18 @@
/**
* @name Extraction warnings
* @description List all extraction warnings for files in the source code directory.
* @kind diagnostic
* @id cpp/diagnostics/extraction-warnings
*/
import cpp
import ExtractionProblems
from ExtractionProblem warning
where
warning instanceof ExtractionRecoverableWarning and exists(warning.getFile().getRelativePath())
or
warning instanceof ExtractionUnknownProblem
select warning,
"Extraction failed in " + warning.getFile() + " with warning " + warning.getProblemMessage(),
warning.getSeverity()

View File

@@ -13,6 +13,9 @@ string describe(Compilation c) {
else result = "extractor invocation " + concat(int i | | c.getArgument(i), " " order by i)
}
/** Gets the SARIF severity level that indicates an error. */
private int getErrorSeverity() { result = 2 }
from Compilation c
where not c.normalTermination()
select "Extraction aborted for " + describe(c)
select "Extraction aborted for " + describe(c), getErrorSeverity()

View File

@@ -1,15 +1,15 @@
/**
* @name Successfully extracted files
* @description Lists all files in the source code directory that were extracted without encountering an error in the file.
* @description Lists all files in the source code directory that were extracted without encountering a problem in the file.
* @kind diagnostic
* @id cpp/diagnostics/successfully-extracted-files
*/
import cpp
import ExtractionErrors
import ExtractionProblems
from File f
where
not exists(ExtractionError e | e.getFile() = f) and
not exists(ExtractionProblem e | e.getFile() = f) and
exists(f.getRelativePath())
select f, ""
select f, "File successfully extracted"

View File

@@ -198,7 +198,7 @@ class CommentBlock extends Comment {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -166,6 +166,7 @@ class VarAnalyzableExpr extends AnalyzableExpr, VariableAccess {
* Holds if `t` is not an instance of `IntegralType`,
* or if `me` cannot be proven to not overflow
*/
pragma[inline]
predicate overflows(MulExpr me, Type t) {
t instanceof IntegralType
implies

View File

@@ -50,10 +50,7 @@ where
// If either of the operands is constant, then don't include it.
(
if cmp.getLeftOperand().isConstant()
then
if cmp.getRightOperand().isConstant()
then none() // Both operands are constant so don't create a message.
else reason = rightReason
then not cmp.getRightOperand().isConstant() and reason = rightReason
else
if cmp.getRightOperand().isConstant()
then reason = leftReason

View File

@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/improper-null-termination
* @problem.severity warning
* @precision medium
* @security-severity 7.8
* @tags security
* external/cwe/cwe-170
@@ -53,6 +54,7 @@ class ImproperNullTerminationReachability extends StackVariableReachabilityWithR
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
exprDefinition(v, node, _) or
mayAddNullTerminator(node, v.getAnAccess()) or
node.(AddressOfExpr).getOperand() = v.getAnAccess() or // address taken
isSinkActual(node, v) // only report first use
}
}

View File

@@ -18,7 +18,7 @@ class RangeFunction extends Function {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
super.getLocation().hasLocationInfo(path, sl, sc, _, _) and

View File

@@ -9,7 +9,7 @@ int main(int argc, char** argv) {
system(command1);
}
{
{
// GOOD: the user string is encoded by a library routine.
char userNameQuoted[1000] = {0};
encodeShellString(userNameQuoted, 1000, userName);

View File

@@ -3,10 +3,10 @@
* @description Using user-supplied data in an OS command, without
* neutralizing special elements, can make code vulnerable
* to command injection.
* @kind problem
* @kind path-problem
* @problem.severity error
* @security-severity 9.8
* @precision low
* @precision high
* @id cpp/command-line-injection
* @tags security
* external/cwe/cwe-078
@@ -16,13 +16,204 @@
import cpp
import semmle.code.cpp.security.CommandExecution
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.ir.dataflow.TaintTracking2
import semmle.code.cpp.security.FlowSources
import semmle.code.cpp.models.implementations.Strcat
from Expr taintedArg, Expr taintSource, string taintCause, string callChain
Expr sinkAsArgumentIndirection(DataFlow::Node sink) {
result =
sink.asOperand()
.(SideEffectOperand)
.getAddressOperand()
.getAnyDef()
.getUnconvertedResultExpression()
}
/**
* Holds if `fst` is a string that is used in a format or concatenation function resulting in `snd`,
* and is *not* placed at the start of the resulting string. This indicates that the author did not
* expect `fst` to control what program is run if the resulting string is eventually interpreted as
* a command line, for example as an argument to `system`.
*/
predicate interestingConcatenation(DataFlow::Node fst, DataFlow::Node snd) {
exists(FormattingFunctionCall call, int index, FormatLiteral literal |
sinkAsArgumentIndirection(fst) = call.getConversionArgument(index) and
snd.asDefiningArgument() = call.getOutputArgument(false) and
literal = call.getFormat() and
not literal.getConvSpecOffset(index) = 0 and
literal.getConversionChar(index) = ["s", "S"]
)
or
// strcat and friends
exists(StrcatFunction strcatFunc, CallInstruction call, ReadSideEffectInstruction rse |
call.getStaticCallTarget() = strcatFunc and
rse.getArgumentDef() = call.getArgument(strcatFunc.getParamSrc()) and
fst.asOperand() = rse.getSideEffectOperand() and
snd.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
call.getArgument(strcatFunc.getParamDest())
)
or
exists(CallInstruction call, Operator op, ReadSideEffectInstruction rse |
call.getStaticCallTarget() = op and
op.hasQualifiedName("std", "operator+") and
op.getType().(UserType).hasQualifiedName("std", "basic_string") and
call.getArgument(1) = rse.getArgumentOperand().getAnyDef() and // left operand
fst.asOperand() = rse.getSideEffectOperand() and
call = snd.asInstruction()
)
}
class TaintToConcatenationConfiguration extends TaintTracking::Configuration {
TaintToConcatenationConfiguration() { this = "TaintToConcatenationConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof FlowSource }
override predicate isSink(DataFlow::Node sink) { interestingConcatenation(sink, _) }
override predicate isSanitizer(DataFlow::Node node) {
node.asInstruction().getResultType() instanceof IntegralType
or
node.asInstruction().getResultType() instanceof FloatingPointType
}
}
class ExecTaintConfiguration extends TaintTracking2::Configuration {
ExecTaintConfiguration() { this = "ExecTaintConfiguration" }
override predicate isSource(DataFlow::Node source) {
exists(DataFlow::Node prevSink, TaintToConcatenationConfiguration conf |
conf.hasFlow(_, prevSink) and
interestingConcatenation(prevSink, source)
)
}
override predicate isSink(DataFlow::Node sink) {
shellCommand(sinkAsArgumentIndirection(sink), _)
}
override predicate isSanitizerOut(DataFlow::Node node) {
isSink(node) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
}
}
module StitchedPathGraph {
// There's a different PathNode class for each DataFlowImplN.qll, so we can't simply combine the
// PathGraph predicates directly. Instead, we use a newtype so there's a single type that
// contains both sets of PathNodes.
newtype TMergedPathNode =
TPathNode1(DataFlow::PathNode node) or
TPathNode2(DataFlow2::PathNode node)
// this wraps the toString and location predicates so we can use the merged node type in a
// selection
class MergedPathNode extends TMergedPathNode {
string toString() {
exists(DataFlow::PathNode n |
this = TPathNode1(n) and
result = n.toString()
)
or
exists(DataFlow2::PathNode n |
this = TPathNode2(n) and
result = n.toString()
)
}
DataFlow::Node getNode() {
exists(DataFlow::PathNode n |
this = TPathNode1(n) and
result = n.getNode()
)
or
exists(DataFlow2::PathNode n |
this = TPathNode2(n) and
result = n.getNode()
)
}
DataFlow::PathNode getPathNode1() { this = TPathNode1(result) }
DataFlow2::PathNode getPathNode2() { this = TPathNode2(result) }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(DataFlow::PathNode n |
this = TPathNode1(n) and
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
or
exists(DataFlow2::PathNode n |
this = TPathNode2(n) and
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
}
}
query predicate edges(MergedPathNode a, MergedPathNode b) {
exists(DataFlow::PathNode an, DataFlow::PathNode bn |
a = TPathNode1(an) and
b = TPathNode1(bn) and
DataFlow::PathGraph::edges(an, bn)
)
or
exists(DataFlow2::PathNode an, DataFlow2::PathNode bn |
a = TPathNode2(an) and
b = TPathNode2(bn) and
DataFlow2::PathGraph::edges(an, bn)
)
or
// This is where paths from the two configurations are connected. `interestingConcatenation`
// is the only thing in this module that's actually specific to the query - everything else is
// just using types and predicates from the DataFlow library.
interestingConcatenation(a.getNode(), b.getNode()) and
a instanceof TPathNode1 and
b instanceof TPathNode2
}
query predicate nodes(MergedPathNode mpn, string key, string val) {
// here we just need the union of the underlying `nodes` predicates
exists(DataFlow::PathNode n |
mpn = TPathNode1(n) and
DataFlow::PathGraph::nodes(n, key, val)
)
or
exists(DataFlow2::PathNode n |
mpn = TPathNode2(n) and
DataFlow2::PathGraph::nodes(n, key, val)
)
}
query predicate subpaths(
MergedPathNode arg, MergedPathNode par, MergedPathNode ret, MergedPathNode out
) {
// just forward subpaths from the underlying libraries. This might be slightly awkward when
// the concatenation is deep in a call chain.
DataFlow::PathGraph::subpaths(arg.getPathNode1(), par.getPathNode1(), ret.getPathNode1(),
out.getPathNode1())
or
DataFlow2::PathGraph::subpaths(arg.getPathNode2(), par.getPathNode2(), ret.getPathNode2(),
out.getPathNode2())
}
}
import StitchedPathGraph
from
DataFlow::PathNode sourceNode, DataFlow::PathNode concatSink, DataFlow2::PathNode concatSource,
DataFlow2::PathNode sinkNode, string taintCause, string callChain,
TaintToConcatenationConfiguration conf1, ExecTaintConfiguration conf2
where
shellCommand(taintedArg, callChain) and
tainted(taintSource, taintedArg) and
isUserInput(taintSource, taintCause)
select taintedArg,
"This argument to an OS command is derived from $@ and then passed to " + callChain, taintSource,
"user input (" + taintCause + ")"
taintCause = sourceNode.getNode().(FlowSource).getSourceType() and
conf1.hasFlowPath(sourceNode, concatSink) and
interestingConcatenation(concatSink.getNode(), concatSource.getNode()) and // this loses call context
conf2.hasFlowPath(concatSource, sinkNode) and
shellCommand(sinkAsArgumentIndirection(sinkNode.getNode()), callChain)
select sinkAsArgumentIndirection(sinkNode.getNode()), TPathNode1(sourceNode).(MergedPathNode),
TPathNode2(sinkNode).(MergedPathNode),
"This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to "
+ callChain, sourceNode, "user input (" + taintCause + ")", concatSource,
concatSource.toString()

View File

@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/user-controlled-null-termination-tainted
* @problem.severity warning
* @precision medium
* @security-severity 10.0
* @tags security
* external/cwe/cwe-170

View File

@@ -9,7 +9,7 @@ storage.</p>
</overview>
<recommendation>
<p>Ensure that sensitive information is always encrypted before being stored, especially before writing to a file.
<p>Ensure that sensitive information is always encrypted before being stored to a file or transmitted over the network.
It may be wise to encrypt information before it is put into a buffer that may be readable in memory.</p>
<p>In general, decrypt sensitive information only at the point where it is necessary for it to be used in

View File

@@ -0,0 +1,5 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="CleartextStorage.inc.qhelp" /></qhelp>

View File

@@ -0,0 +1,124 @@
/**
* @name Cleartext transmission of sensitive information
* @description Transmitting sensitive information across a network in
* cleartext can expose it to an attacker.
* @kind path-problem
* @problem.severity warning
* @security-severity 7.5
* @precision medium
* @id cpp/cleartext-transmission
* @tags security
* external/cwe/cwe-319
*/
import cpp
import semmle.code.cpp.security.SensitiveExprs
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.models.interfaces.FlowSource
import DataFlow::PathGraph
/**
* A function call that sends or receives data over a network.
*/
abstract class NetworkSendRecv extends FunctionCall {
/**
* Gets the expression for the socket or similar object used for sending or
* receiving data (if any).
*/
abstract Expr getSocketExpr();
/**
* Gets the expression for the buffer to be sent from / received into.
*/
abstract Expr getDataExpr();
}
/**
* A function call that sends data over a network.
*
* note: functions such as `write` may be writing to a network source or a file. We could attempt to determine which, and sort results into `cpp/cleartext-transmission` and perhaps `cpp/cleartext-storage-file`. In practice it usually isn't very important which query reports a result as long as its reported exactly once.
*/
class NetworkSend extends NetworkSendRecv {
RemoteFlowSinkFunction target;
NetworkSend() { target = this.getTarget() }
override Expr getSocketExpr() {
exists(FunctionInput input, int arg |
target.hasSocketInput(input) and
input.isParameter(arg) and
result = this.getArgument(arg)
)
}
override Expr getDataExpr() {
exists(FunctionInput input, int arg |
target.hasRemoteFlowSink(input, _) and
input.isParameterDeref(arg) and
result = this.getArgument(arg)
)
}
}
/**
* A function call that receives data over a network.
*/
class NetworkRecv extends NetworkSendRecv {
RemoteFlowSourceFunction target;
NetworkRecv() { target = this.getTarget() }
override Expr getSocketExpr() {
exists(FunctionInput input, int arg |
target.hasSocketInput(input) and
input.isParameter(arg) and
result = this.getArgument(arg)
)
}
override Expr getDataExpr() {
exists(FunctionOutput output, int arg |
target.hasRemoteFlowSource(output, _) and
output.isParameterDeref(arg) and
result = this.getArgument(arg)
)
}
}
/**
* Taint flow from a sensitive expression to a network operation with data
* tainted by that expression.
*/
class SensitiveSendRecvConfiguration extends TaintTracking::Configuration {
SensitiveSendRecvConfiguration() { this = "SensitiveSendRecvConfiguration" }
override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof SensitiveExpr }
override predicate isSink(DataFlow::Node sink) {
exists(NetworkSendRecv transmission |
sink.asExpr() = transmission.getDataExpr() and
// a zero socket descriptor is standard input, which is not interesting for this query.
not exists(Zero zero |
DataFlow::localFlow(DataFlow::exprNode(zero),
DataFlow::exprNode(transmission.getSocketExpr()))
)
)
}
}
from
SensitiveSendRecvConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
NetworkSendRecv transmission, string msg
where
config.hasFlowPath(source, sink) and
sink.getNode().asExpr() = transmission.getDataExpr() and
if transmission instanceof NetworkSend
then
msg =
"This operation transmits '" + sink.toString() +
"', which may contain unencrypted sensitive data from $@"
else
msg =
"This operation receives into '" + sink.toString() +
"', which may put unencrypted sensitive data into $@"
select transmission, source, sink, msg, source, source.getNode().asExpr().toString()

View File

@@ -121,16 +121,14 @@ predicate exprSourceType(Expr use, Type sourceType, Location sourceLoc) {
else
if use instanceof CrementOperation
then exprSourceType(use.(CrementOperation).getOperand(), sourceType, sourceLoc)
else
else (
// Conversions are not in the AST, so ignore them.
if use instanceof Conversion
then none()
else (
// Source expressions
sourceType = use.getUnspecifiedType() and
isPointerType(sourceType) and
sourceLoc = use.getLocation()
)
not use instanceof Conversion and
// Source expressions
sourceType = use.getUnspecifiedType() and
isPointerType(sourceType) and
sourceLoc = use.getLocation()
)
}
/**

View File

@@ -4,10 +4,13 @@
* @kind problem
* @id cpp/incorrect-allocation-error-handling
* @problem.severity warning
* @security-severity 7.5
* @precision medium
* @tags correctness
* security
* external/cwe/cwe-570
* external/cwe/cwe-252
* external/cwe/cwe-755
*/
import cpp

View File

@@ -24,7 +24,7 @@ class Top extends Element {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
pragma[noopt]
final predicate hasLocationInfo(

View File

@@ -0,0 +1,18 @@
...
int myFclose(FILE * fmy)
{
if(!fclose(fmy)) {
fmy = NULL;
return 0;
}
return -1;
}
...
fe = fopen("myFile.txt", "wt");
...
fclose(fe); // BAD
...
fe = fopen("myFile.txt", "wt");
...
myFclose(fe); // GOOD
...

View File

@@ -0,0 +1,23 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The presence of a shell function with additional check indicates the possible risks of the call. Use this check everywhere.</p>
</overview>
<example>
<p>The following example demonstrates fallacious and fixed methods of using wrapper functions.</p>
<sample src="FindWrapperFunctions.cpp" />
</example>
<references>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/java/JNI00-J.+Define+wrappers+around+native+methods">JNI00-J. Define wrappers around native methods</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,141 @@
/**
* @name Missed opportunity to call wrapper function
* @description If a wrapper function is defined for a given function, any call to the given function should be via the wrapper function.
* @kind problem
* @id cpp/call-to-function-without-wrapper
* @problem.severity warning
* @precision medium
* @tags correctness
* maintainability
* security
* external/cwe/cwe-1041
*/
import cpp
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.commons.Assertions
/**
* A function call that is used in error situations (logging, throwing an exception, abnormal termination).
*/
class CallUsedToHandleErrors extends FunctionCall {
CallUsedToHandleErrors() {
// call that is known to not return
not exists(this.(ControlFlowNode).getASuccessor())
or
// call throwing an exception
exists(ThrowExpr tex | tex = this.(ControlFlowNode).getASuccessor())
or
// call logging a message, possibly an error
exists(FormattingFunction ff | ff = this.(ControlFlowNode).getASuccessor())
or
// enabling recursive search
exists(CallUsedToHandleErrors fr | getTarget() = fr.getEnclosingFunction())
}
}
/** Holds if the conditions for a call outside the wrapper function are met. */
predicate conditionsOutsideWrapper(FunctionCall fcp) {
fcp.getNumberOfArguments() > 0 and
not exists(ConditionalStmt cdtmp | fcp.getEnclosingStmt().getParentStmt*() = cdtmp) and
not exists(Loop lptmp | fcp.getEnclosingStmt().getParentStmt*() = lptmp) and
not exists(ReturnStmt rttmp | fcp.getEnclosingStmt().getParentStmt*() = rttmp) and
not exists(FunctionCall fctmp2 | fcp = fctmp2.getAnArgument().getAChild*()) and
not exists(Assignment astmp | fcp = astmp.getRValue().getAChild*()) and
not exists(Initializer intmp | fcp = intmp.getExpr().getAChild*()) and
not exists(Assertion astmp | fcp = astmp.getAsserted().getAChild*()) and
not exists(Operation optmp | fcp = optmp.getAChild*()) and
not exists(ArrayExpr aetmp | fcp = aetmp.getAChild*()) and
not exists(ExprCall ectmp | fcp = ectmp.getAnArgument().getAChild*())
}
/** Holds if the conditions for calling `fcp` inside the `fnp` wrapper function are met. */
pragma[inline]
predicate conditionsInsideWrapper(FunctionCall fcp, Function fnp) {
not exists(FunctionCall fctmp2 |
fctmp2.getEnclosingFunction() = fnp and fcp = fctmp2.getAnArgument().getAChild*()
) and
not fcp instanceof CallUsedToHandleErrors and
not fcp.getAnArgument().isConstant() and
fcp.getEnclosingFunction() = fnp and
fnp.getNumberOfParameters() > 0 and
// the call arguments must be passed through the arguments of the wrapper function
forall(int i | i in [0 .. fcp.getNumberOfArguments() - 1] |
globalValueNumber(fcp.getArgument(i)) = globalValueNumber(fnp.getAParameter().getAnAccess())
) and
// there should be no more than one required call inside the wrapper function
not exists(FunctionCall fctmp |
fctmp.getTarget() = fcp.getTarget() and
fctmp.getFile() = fcp.getFile() and
fctmp != fcp and
fctmp.getEnclosingFunction() = fnp
) and
// inside the wrapper function there should be no calls without paths to the desired function
not exists(FunctionCall fctmp |
fctmp.getEnclosingFunction() = fnp and
fctmp.getFile() = fcp.getFile() and
fctmp != fcp and
(
fctmp = fcp.getAPredecessor+()
or
not exists(FunctionCall fctmp1 |
fctmp1 = fcp and
(
fctmp.getASuccessor+() = fctmp1 or
fctmp.getAPredecessor+() = fctmp1
)
)
)
)
}
/** Holds if the conditions for the wrapper function are met. */
pragma[inline]
predicate conditionsForWrapper(FunctionCall fcp, Function fnp) {
not exists(ExprCall ectmp | fnp = ectmp.getEnclosingFunction()) and
not exists(Loop lp | lp.getEnclosingFunction() = fnp) and
not exists(SwitchStmt sw | sw.getEnclosingFunction() = fnp) and
not fnp instanceof Operator and
// inside the wrapper function there should be checks of arguments or the result,
// perhaps by means of passing the latter as an argument to some function
(
exists(IfStmt ifs |
ifs.getEnclosingFunction() = fnp and
(
globalValueNumber(ifs.getCondition().getAChild*()) = globalValueNumber(fcp.getAnArgument()) and
ifs.getASuccessor*() = fcp
or
ifs.getCondition().getAChild() = fcp
)
)
or
exists(FunctionCall fctmp |
fctmp.getEnclosingFunction() = fnp and
globalValueNumber(fctmp.getAnArgument().getAChild*()) = globalValueNumber(fcp)
)
) and
// inside the wrapper function there must be a function call to handle the error
exists(CallUsedToHandleErrors fctmp |
fctmp.getEnclosingFunction() = fnp and
forall(int i | i in [0 .. fnp.getNumberOfParameters() - 1] |
fnp.getParameter(i).getAnAccess().getTarget() =
fcp.getAnArgument().(VariableAccess).getTarget() or
fnp.getParameter(i).getUnspecifiedType() instanceof Class or
fnp.getParameter(i).getUnspecifiedType().(ReferenceType).getBaseType() instanceof Class or
fnp.getParameter(i).getAnAccess().getTarget() =
fctmp.getAnArgument().(VariableAccess).getTarget()
)
)
}
from FunctionCall fc, Function fn
where
exists(FunctionCall fctmp |
conditionsInsideWrapper(fctmp, fn) and
conditionsForWrapper(fctmp, fn) and
conditionsOutsideWrapper(fc) and
fctmp.getTarget() = fc.getTarget() and
fc.getEnclosingFunction() != fn and
fc.getEnclosingFunction().getMetrics().getNumberOfCalls() > fn.getMetrics().getNumberOfCalls()
)
select fc, "Consider changing the call to $@", fn, fn.getName()

View File

@@ -61,7 +61,7 @@ class Copy extends @duplication_or_similarity {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn

View File

@@ -8,7 +8,7 @@ import cpp
* column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
* in file `filepath`.
*
* For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
external predicate defectResults(
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,

View File

@@ -8,7 +8,7 @@ import cpp
* column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
* in file `filepath`.
*
* For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
* For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
external predicate metricResults(
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,

View File

@@ -230,13 +230,13 @@ predicate leakedInSameMethod(Resource r, Expr acquire) {
)
)
)
or
exists(FunctionAccess fa, string kind |
// the address of a function that releases `r` is taken (and likely
// used to release `r` at some point).
r.acquisitionWithRequiredKind(acquire, kind) and
fa.getTarget() = r.getAReleaseExpr(kind).getEnclosingFunction()
)
)
or
exists(FunctionAccess fa, string kind |
// the address of a function that releases `r` is taken (and likely
// used to release `r` at some point).
r.acquisitionWithRequiredKind(acquire, kind) and
fa.getTarget() = r.getAReleaseExpr(kind).getEnclosingFunction()
)
}

View File

@@ -4,7 +4,7 @@
* (in that the `.expected` file should always be empty).
*
* To add this framework to a new language:
* - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class
* - Add a file `InlineExpectationsTestPrivate.qll` that defines a `ExpectationComment` class. This class
* must support a `getContents` method that returns the contents of the given comment, _excluding_
* the comment indicator itself. It should also define `toString` and `getLocation` as usual.
*
@@ -60,8 +60,8 @@
*
* Example:
* ```cpp
* int i = x + 5; // $const=5
* int j = y + (7 - 3) // $const=7 const=3 const=4 // The result of the subtraction is a constant.
* int i = x + 5; // $ const=5
* int j = y + (7 - 3) // $ const=7 const=3 const=4 // The result of the subtraction is a constant.
* ```
*
* For tests that contain known missing and spurious results, it is possible to further
@@ -194,7 +194,7 @@ private int getEndOfColumnPosition(int start, string content) {
}
private predicate getAnExpectation(
LineComment comment, TColumn column, string expectation, string tags, string value
ExpectationComment comment, TColumn column, string expectation, string tags, string value
) {
exists(string content |
content = comment.getContents().regexpCapture(expectationCommentPattern(), 1) and
@@ -247,14 +247,14 @@ private newtype TFailureLocatable =
) {
test.hasActualResult(location, element, tag, value)
} or
TValidExpectation(LineComment comment, string tag, string value, string knownFailure) {
TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) {
exists(TColumn column, string tags |
getAnExpectation(comment, column, _, tags, value) and
tag = tags.splitAt(",") and
knownFailure = getColumnString(column)
)
} or
TInvalidExpectation(LineComment comment, string expectation) {
TInvalidExpectation(ExpectationComment comment, string expectation) {
getAnExpectation(comment, _, expectation, _, _) and
not expectation.regexpMatch(expectationPattern())
}
@@ -292,7 +292,7 @@ class ActualResult extends FailureLocatable, TActualResult {
}
abstract private class Expectation extends FailureLocatable {
LineComment comment;
ExpectationComment comment;
override string toString() { result = comment.toString() }

View File

@@ -1,16 +1,16 @@
import cpp
private newtype TLineComment = MkLineComment(CppStyleComment c)
private newtype TExpectationComment = MkExpectationComment(CppStyleComment c)
/**
* Represents a line comment in the CPP style.
* Unlike the `CppStyleComment` class, however, the string returned by `getContents` does _not_
* include the preceding comment marker (`//`).
*/
class LineComment extends TLineComment {
class ExpectationComment extends TExpectationComment {
CppStyleComment comment;
LineComment() { this = MkLineComment(comment) }
ExpectationComment() { this = MkExpectationComment(comment) }
/** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
string getContents() { result = comment.getContents().suffix(2) }

View File

@@ -0,0 +1 @@
| test.cpp:23:3:23:8 | call to fclose | Consider changing the call to $@ | test.cpp:9:6:9:13 | myFclose | myFclose |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-1041/FindWrapperFunctions.ql

View File

@@ -0,0 +1,27 @@
#define NULL (0)
typedef int FILE;
FILE *fopen(const char *filename, const char *mode);
int fclose(FILE *stream);
extern FILE * fe;
extern int printf(const char *fmt, ...);
void exit(int status);
void myFclose(FILE * fmy)
{
int i;
if(fmy) {
i = fclose(fmy);
fmy = NULL;
printf("close end is code %d",i);
if(i!=0) exit(1);
}
}
int main(int argc, char *argv[])
{
fe = fopen("myFile.txt", "wt");
fclose(fe); // BAD
fe = fopen("myFile.txt", "wt");
myFclose(fe); // GOOD
return 0;
}

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