mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge remote-tracking branch 'codeql/main' into 'main'
Conflicts: config/identical-files.json
This commit is contained in:
@@ -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
3
.gitignore
vendored
@@ -24,3 +24,6 @@
|
||||
/codeql/
|
||||
|
||||
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
|
||||
|
||||
# Avoid committing cached package components
|
||||
.codeql
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
2
cpp/change-notes/2021-06-10-cleartext-transmission.md
Normal file
2
cpp/change-notes/2021-06-10-cleartext-transmission.md
Normal 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.
|
||||
2
cpp/change-notes/2021-09-27-command-line-injection.md
Normal file
2
cpp/change-notes/2021-09-27-command-line-injection.md
Normal 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`
|
||||
3
cpp/change-notes/2021-09-27-overflow-static.md
Normal file
3
cpp/change-notes/2021-09-27-overflow-static.md
Normal 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.
|
||||
2
cpp/change-notes/2021-10-01-improper-null-termination.md
Normal file
2
cpp/change-notes/2021-10-01-improper-null-termination.md
Normal 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).
|
||||
3
cpp/change-notes/2021-10-07-extraction-errors.md
Normal file
3
cpp/change-notes/2021-10-07-extraction-errors.md
Normal 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`.
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
23
cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll
Normal file
23
cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll
Normal 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)
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,3 +36,4 @@ private import implementations.Select
|
||||
private import implementations.MySql
|
||||
private import implementations.SqLite3
|
||||
private import implementations.PostgreSql
|
||||
private import implementations.System
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
43
cpp/ql/lib/semmle/code/cpp/models/implementations/System.qll
Normal file
43
cpp/ql/lib/semmle/code/cpp/models/implementations/System.qll
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, _, _)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
*
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
1207
cpp/ql/lib/tutorial.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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."
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
18
cpp/ql/src/Diagnostics/ExtractionWarnings.ql
Normal file
18
cpp/ql/src/Diagnostics/ExtractionWarnings.ql
Normal 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()
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="CleartextStorage.inc.qhelp" /></qhelp>
|
||||
124
cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.ql
Normal file
124
cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.ql
Normal 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()
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
...
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
2
cpp/ql/src/external/CodeDuplication.qll
vendored
2
cpp/ql/src/external/CodeDuplication.qll
vendored
@@ -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
|
||||
|
||||
2
cpp/ql/src/external/DefectFilter.qll
vendored
2
cpp/ql/src/external/DefectFilter.qll
vendored
@@ -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,
|
||||
|
||||
2
cpp/ql/src/external/MetricFilter.qll
vendored
2
cpp/ql/src/external/MetricFilter.qll
vendored
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-1041/FindWrapperFunctions.ql
|
||||
@@ -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
Reference in New Issue
Block a user