Merge branch 'main' into alternative-instruction-operand-flow

This commit is contained in:
Mathias Vorreiter Pedersen
2020-08-19 11:18:51 +02:00
316 changed files with 15554 additions and 4147 deletions

View File

@@ -1,5 +1,6 @@
{ "provide": [ "*/ql/src/qlpack.yml",
"*/ql/test/qlpack.yml",
"*/ql/examples/qlpack.yml",
"*/upgrades/qlpack.yml",
"misc/legacy-support/*/qlpack.yml",
"misc/suite-helpers/qlpack.yml" ] }

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"omnisharp.autoStart": false
}

View File

@@ -28,27 +28,51 @@ The following changes in version 1.25 affect C# analysis in all applications.
such as `A<int>.B`, no longer are considered unbound generics. (Such nested types do,
however, still have relevant `.getSourceDeclaration()`s, for example `A<>.B`.)
* The data-flow library has been improved, which affects most security queries by potentially
adding more results. Flow through methods now takes nested field reads/writes into account.
For example, the library is able to track flow from `"taint"` to `Sink()` via the method
`GetF2F1()` in
```csharp
class C1
{
string F1;
}
adding more results:
- Flow through methods now takes nested field reads/writes into account.
For example, the library is able to track flow from `"taint"` to `Sink()` via the method
`GetF2F1()` in
```csharp
class C1
{
string F1;
}
class C2
{
C1 F2;
string GetF2F1() => F2.F1; // Nested field read
class C2
{
C1 F2;
void M()
{
F2 = new C1() { F1 = "taint" };
Sink(GetF2F1()); // NEW: "taint" reaches here
}
}
```
string GetF2F1() => F2.F1; // Nested field read
void M()
{
F2 = new C1() { F1 = "taint" };
Sink(GetF2F1()); // NEW: "taint" reaches here
}
}
```
- Flow through collections is now modeled precisely. For example, instead of modeling an array
store `a[i] = x` as a taint-step from `x` to `a`, we now model it as a data-flow step that
stores `x` into `a`. To get the value back out, a matching read step must be taken.
For source-code based data-flow analysis, the following constructs are modeled as stores into
collections:
- Direct array assignments, `a[i] = x`.
- Array initializers, `new [] { x }`.
- C# 6-style array initializers, `new C() { Array = { [i] = x } }`.
- Call arguments that match a `params` parameter, where the C# compiler creates an array under-the-hood.
- `yield return` statements.
The following source-code constructs read from a collection:
- Direct array reads, `a[i]`.
- `foreach` statements.
For calls out to library code, existing flow summaries have been refined to precisely
capture how they interact with collection contents. For example, a call to
`System.Collections.Generic.List<T>.Add(T)` stores the value of the argument into the
qualifier, and a call to `System.Collections.Generic.List<T>.get_Item(int)` (that is, an
indexer call) reads contents out of the qualifier. Moreover, the effect of
collection-clearing methods such as `System.Collections.Generic.List<T>.Clear()` is now
also modeled.
## Changes to autobuilder

View File

@@ -6,8 +6,10 @@
- [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
- [bluebird](http://bluebirdjs.com/)
- [express](https://www.npmjs.com/package/express)
- [execa](https://www.npmjs.com/package/execa)
- [fancy-log](https://www.npmjs.com/package/fancy-log)
- [fastify](https://www.npmjs.com/package/fastify)
- [foreground-child](https://www.npmjs.com/package/foreground-child)
- [fstream](https://www.npmjs.com/package/fstream)
- [jGrowl](https://github.com/stanlemon/jGrowl)
- [jQuery](https://jquery.com/)
@@ -17,6 +19,7 @@
- [mssql](https://www.npmjs.com/package/mssql)
- [mysql](https://www.npmjs.com/package/mysql)
- [npmlog](https://www.npmjs.com/package/npmlog)
- [opener](https://www.npmjs.com/package/opener)
- [pg](https://www.npmjs.com/package/pg)
- [sequelize](https://www.npmjs.com/package/sequelize)
- [spanner](https://www.npmjs.com/package/spanner)

View File

@@ -0,0 +1,19 @@
# Improvements to C/C++ analysis
The following changes in version 1.26 affect C/C++ analysis in all applications.
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
| Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | Fewer false positive results | The query now accounts for intentional wrapping of an unsigned loop counter. |
## Changes to libraries

View File

@@ -0,0 +1,30 @@
# Improvements to JavaScript analysis
## General improvements
* Support for the following frameworks and libraries has been improved:
- [fast-json-stable-stringify](https://www.npmjs.com/package/fast-json-stable-stringify)
- [fast-safe-stringify](https://www.npmjs.com/package/fast-safe-stringify)
- [javascript-stringify](https://www.npmjs.com/package/javascript-stringify)
- [js-stringify](https://www.npmjs.com/package/js-stringify)
- [json-stable-stringify](https://www.npmjs.com/package/json-stable-stringify)
- [json-stringify-safe](https://www.npmjs.com/package/json-stringify-safe)
- [json3](https://www.npmjs.com/package/json3)
- [object-inspect](https://www.npmjs.com/package/object-inspect)
- [pretty-format](https://www.npmjs.com/package/pretty-format)
- [stringify-object](https://www.npmjs.com/package/stringify-object)
## New queries
| **Query** | **Tags** | **Purpose** |
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
## Changes to libraries

View File

@@ -0,0 +1,3 @@
name: codeql-cpp-examples
version: 0.0.0
libraryPathDependencies: codeql-cpp

View File

@@ -50,7 +50,12 @@ predicate illDefinedDecrForStmt(
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(lesserOperand)) and
// `initialCondition` < `terminalCondition`
(
upperBound(initialCondition) < lowerBound(terminalCondition)
upperBound(initialCondition) < lowerBound(terminalCondition) and
(
// exclude cases where the loop counter is `unsigned` (where wrapping behaviour can be used deliberately)
v.getUnspecifiedType().(IntegralType).isSigned() or
initialCondition.getValue().toInt() = 0
)
or
(forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue())
)

View File

@@ -9,3 +9,10 @@
tags contain:
- ide-contextual-queries/local-definitions
- ide-contextual-queries/local-references
- query: Metrics/Dependencies/ExternalDependencies.ql
- query: Metrics/Dependencies/ExternalDependenciesSourceLinks.ql
- query: Metrics/Files/FLinesOfCode.ql
- query: Metrics/Files/FLinesOfCommentedOutCode.ql
- query: Metrics/Files/FLinesOfComments.ql
- query: Metrics/Files/FLinesOfDuplicatedCode.ql
- query: Metrics/Files/FNumberOfTests.ql

View File

@@ -197,7 +197,8 @@ class Element extends ElementBase {
initialisers(underlyingElement(this), unresolveElement(result), _, _) or
exprconv(unresolveElement(result), underlyingElement(this)) or
param_decl_bind(underlyingElement(this), _, unresolveElement(result)) or
using_container(unresolveElement(result), underlyingElement(this))
using_container(unresolveElement(result), underlyingElement(this)) or
static_asserts(unresolveElement(this), _, _, _, underlyingElement(result))
}
/** Gets the closest `Element` enclosing this one. */
@@ -278,12 +279,12 @@ class StaticAssert extends Locatable, @static_assert {
/**
* Gets the expression which this static assertion ensures is true.
*/
Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _) }
Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _, _) }
/**
* Gets the message which will be reported by the compiler if this static assertion fails.
*/
string getMessage() { static_asserts(underlyingElement(this), _, result, _) }
string getMessage() { static_asserts(underlyingElement(this), _, result, _, _) }
override Location getLocation() { static_asserts(underlyingElement(this), _, _, result) }
override Location getLocation() { static_asserts(underlyingElement(this), _, _, result, _) }
}

View File

@@ -214,6 +214,9 @@ abstract class ImplicitConversionFunction extends MemberFunction {
}
/**
* DEPRECATED: as of C++11 this class does not correspond perfectly with the
* language definition of a converting constructor.
*
* A C++ constructor that also defines an implicit conversion. For example the
* function `MyClass` in the following code is a `ConversionConstructor`:
* ```
@@ -225,15 +228,16 @@ abstract class ImplicitConversionFunction extends MemberFunction {
* };
* ```
*/
class ConversionConstructor extends Constructor, ImplicitConversionFunction {
deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction {
ConversionConstructor() {
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
not hasSpecifier("explicit") and
not this instanceof CopyConstructor
not hasSpecifier("explicit")
}
override string getAPrimaryQlClass() {
not this instanceof MoveConstructor and result = "ConversionConstructor"
not this instanceof CopyConstructor and
not this instanceof MoveConstructor and
result = "ConversionConstructor"
}
/** Gets the type this `ConversionConstructor` takes as input. */

View File

@@ -582,7 +582,7 @@ class TemplateVariable extends Variable {
* float a;
* }
*
* template<type T>
* template<typename T>
* void myTemplateFunction() {
* T b;
* }

View File

@@ -49,6 +49,18 @@ predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex) {
)
}
/**
* A standard function such as `vsprintf` that has an output parameter
* and a variable argument list of type `va_arg`.
*/
private predicate primitiveVariadicFormatterOutput(TopLevelFunction f, int outputParamIndex) {
// note: this might look like the regular expression in `primitiveVariadicFormatter`, but
// there is one important difference: the [fs] part is not optional, as these classify
// the `printf` variants that write to a buffer.
// Conveniently, these buffer parameters are all at index 0.
f.getName().regexpMatch("_?_?va?[fs]n?w?printf(_s)?(_p)?(_l)?") and outputParamIndex = 0
}
private predicate callsVariadicFormatter(Function f, int formatParamIndex) {
exists(FunctionCall fc, int i |
variadicFormatter(fc.getTarget(), i) and
@@ -57,6 +69,26 @@ private predicate callsVariadicFormatter(Function f, int formatParamIndex) {
)
}
private predicate callsVariadicFormatterOutput(Function f, int outputParamIndex) {
exists(FunctionCall fc, int i |
fc.getEnclosingFunction() = f and
variadicFormatterOutput(fc.getTarget(), i) and
fc.getArgument(i) = f.getParameter(outputParamIndex).getAnAccess()
)
}
/**
* Holds if `f` is a function such as `vprintf` that takes variable argument list
* of type `va_arg` and writes formatted output to a buffer given as a parameter at
* index `outputParamIndex`, if any.
*/
private predicate variadicFormatterOutput(Function f, int outputParamIndex) {
primitiveVariadicFormatterOutput(f, outputParamIndex)
or
not f.isVarargs() and
callsVariadicFormatterOutput(f, outputParamIndex)
}
/**
* Holds if `f` is a function such as `vprintf` that has a format parameter
* (at `formatParamIndex`) and a variable argument list of type `va_arg`.
@@ -78,6 +110,8 @@ class UserDefinedFormattingFunction extends FormattingFunction {
UserDefinedFormattingFunction() { isVarargs() and callsVariadicFormatter(this, _) }
override int getFormatParameterIndex() { callsVariadicFormatter(this, result) }
override int getOutputParameterIndex() { callsVariadicFormatterOutput(this, result) }
}
/**

View File

@@ -65,6 +65,15 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
// tracking. The flow from expression `x` into `x++` etc. is handled in the
// case above.
exprTo = DataFlow::getAnAccessToAssignedVariable(exprFrom.(PostfixCrementOperation))
or
// In `for (char c : s) { ... c ... }`, this rule propagates taint from `s`
// to `c`.
exists(RangeBasedForStmt rbf |
exprFrom = rbf.getRange() and
// It's guaranteed up to at least C++20 that the range-based for loop
// desugars to a variable with an initializer.
exprTo = rbf.getVariable().getInitializer().getExpr()
)
)
or
// Taint can flow through modeled functions

View File

@@ -697,13 +697,20 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)
*/
class BarrierGuard extends IRGuardCondition {
/** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
abstract predicate checks(Instruction instr, boolean b);
predicate checksInstr(Instruction instr, boolean b) { none() }
/** Override this predicate to hold if this guard validates `expr` upon evaluating to `b`. */
predicate checks(Expr e, boolean b) { none() }
/** Gets a node guarded by this guard. */
final Node getAGuardedNode() {
exists(ValueNumber value, boolean edge |
(
this.checksInstr(value.getAnInstruction(), edge)
or
this.checks(value.getAnInstruction().getConvertedResultExpression(), edge)
) and
result.asInstruction() = value.getAnInstruction() and
this.checks(value.getAnInstruction(), edge) and
this.controls(result.asInstruction().getBlock(), edge)
)
}

View File

@@ -7,9 +7,17 @@ import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
/**
* Model for C++ conversion constructors.
* Model for C++ conversion constructors. As of C++11 this does not correspond
* perfectly with the language definition of a converting constructor, however,
* it does correspond with the constructors we are confident taint should flow
* through.
*/
class ConversionConstructorModel extends ConversionConstructor, TaintFunction {
class ConversionConstructorModel extends Constructor, TaintFunction {
ConversionConstructorModel() {
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
not hasSpecifier("explicit")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// taint flow from the first constructor argument to the returned object
input.isParameter(0) and

View File

@@ -508,7 +508,8 @@ static_asserts(
unique int id: @static_assert,
int condition : @expr ref,
string message : string ref,
int location: @location_default ref
int location: @location_default ref,
int enclosing : @element ref
);
// each function has an ordered list of parameters

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ import semmle.code.cpp.ir.IR
class TestBarrierGuard extends DataFlow::BarrierGuard {
TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" }
override predicate checks(Instruction checked, boolean isTrue) {
override predicate checksInstr(Instruction checked, boolean isTrue) {
checked = this.(CallInstruction).getPositionalArgument(0) and
isTrue = true
}

View File

@@ -112,7 +112,7 @@ void test1()
{
char buffer[256] = {0};
sink(mysprintf(buffer, 256, "%s", string::source()));
sink(buffer); // tainted [NOT DETECTED - implement UserDefinedFormattingFunction.getOutputParameterIndex()]
sink(buffer); // tainted
}
{

View File

@@ -206,6 +206,8 @@
| format.cpp:113:21:113:24 | {...} | format.cpp:115:8:115:13 | buffer | |
| format.cpp:113:23:113:23 | 0 | format.cpp:113:21:113:24 | {...} | TAINT |
| format.cpp:114:18:114:23 | ref arg buffer | format.cpp:115:8:115:13 | buffer | |
| format.cpp:114:31:114:34 | %s | format.cpp:114:18:114:23 | ref arg buffer | TAINT |
| format.cpp:114:37:114:50 | call to source | format.cpp:114:18:114:23 | ref arg buffer | TAINT |
| format.cpp:119:10:119:11 | 0 | format.cpp:120:29:120:29 | i | |
| format.cpp:119:10:119:11 | 0 | format.cpp:121:8:121:8 | i | |
| format.cpp:120:28:120:29 | ref arg & ... | format.cpp:120:29:120:29 | i [inner post update] | |
@@ -306,99 +308,205 @@
| movableclass.cpp:65:13:65:18 | call to source | movableclass.cpp:65:13:65:20 | call to MyMovableClass | TAINT |
| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | ref arg s3 | TAINT |
| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:11:65:11 | call to operator= | TAINT |
| stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | |
| stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT |
| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | |
| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:74:7:74:7 | b | |
| stl.cpp:69:16:69:21 | call to source | stl.cpp:69:16:69:24 | call to basic_string | TAINT |
| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:73:7:73:7 | c | |
| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:75:7:75:7 | c | |
| stl.cpp:74:7:74:7 | b | stl.cpp:74:9:74:13 | call to c_str | TAINT |
| stl.cpp:75:7:75:7 | c | stl.cpp:75:9:75:13 | call to c_str | TAINT |
| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:83:2:83:4 | ss1 | |
| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:89:7:89:9 | ss1 | |
| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:94:7:94:9 | ss1 | |
| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:84:2:84:4 | ss2 | |
| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:90:7:90:9 | ss2 | |
| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:95:7:95:9 | ss2 | |
| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:85:2:85:4 | ss3 | |
| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:91:7:91:9 | ss3 | |
| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:96:7:96:9 | ss3 | |
| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:86:2:86:4 | ss4 | |
| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:92:7:92:9 | ss4 | |
| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:97:7:97:9 | ss4 | |
| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:87:2:87:4 | ss5 | |
| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:93:7:93:9 | ss5 | |
| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:98:7:98:9 | ss5 | |
| stl.cpp:81:16:81:21 | call to source | stl.cpp:81:16:81:24 | call to basic_string | TAINT |
| stl.cpp:81:16:81:24 | call to basic_string | stl.cpp:87:9:87:9 | t | |
| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:89:7:89:9 | ss1 | |
| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:94:7:94:9 | ss1 | |
| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:90:7:90:9 | ss2 | |
| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:95:7:95:9 | ss2 | |
| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:91:7:91:9 | ss3 | |
| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:96:7:96:9 | ss3 | |
| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:92:7:92:9 | ss4 | |
| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:97:7:97:9 | ss4 | |
| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:93:7:93:9 | ss5 | |
| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:98:7:98:9 | ss5 | |
| stl.cpp:101:32:101:37 | source | stl.cpp:106:9:106:14 | source | |
| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:105:2:105:4 | ss1 | |
| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:108:7:108:9 | ss1 | |
| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:110:7:110:9 | ss1 | |
| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:106:2:106:4 | ss2 | |
| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:109:7:109:9 | ss2 | |
| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:111:7:111:9 | ss2 | |
| stl.cpp:105:2:105:4 | ref arg ss1 | stl.cpp:108:7:108:9 | ss1 | |
| stl.cpp:105:2:105:4 | ref arg ss1 | stl.cpp:110:7:110:9 | ss1 | |
| stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:109:7:109:9 | ss2 | |
| stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:111:7:111:9 | ss2 | |
| stl.cpp:124:16:124:28 | call to basic_string | stl.cpp:125:7:125:11 | path1 | |
| stl.cpp:124:17:124:26 | call to user_input | stl.cpp:124:16:124:28 | call to basic_string | TAINT |
| stl.cpp:125:7:125:11 | path1 | stl.cpp:125:13:125:17 | call to c_str | TAINT |
| stl.cpp:128:10:128:19 | call to user_input | stl.cpp:128:10:128:21 | call to basic_string | TAINT |
| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:128:2:128:21 | ... = ... | |
| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:129:7:129:11 | path2 | |
| stl.cpp:129:7:129:11 | path2 | stl.cpp:129:13:129:17 | call to c_str | TAINT |
| stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT |
| stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | |
| stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT |
| stl.cpp:137:19:137:24 | call to source | stl.cpp:140:17:140:18 | cs | |
| stl.cpp:137:19:137:24 | call to source | stl.cpp:142:7:142:8 | cs | |
| stl.cpp:140:17:140:18 | cs | stl.cpp:140:17:140:19 | call to basic_string | TAINT |
| stl.cpp:140:17:140:19 | call to basic_string | stl.cpp:143:7:143:8 | ss | |
| stl.cpp:148:19:148:24 | call to source | stl.cpp:151:17:151:18 | cs | |
| stl.cpp:151:17:151:18 | cs | stl.cpp:151:17:151:19 | call to basic_string | TAINT |
| stl.cpp:151:17:151:19 | call to basic_string | stl.cpp:154:7:154:8 | ss | |
| stl.cpp:151:17:151:19 | call to basic_string | stl.cpp:157:7:157:8 | ss | |
| stl.cpp:154:7:154:8 | ss | stl.cpp:154:10:154:14 | call to c_str | TAINT |
| stl.cpp:154:10:154:14 | call to c_str | stl.cpp:154:2:154:16 | ... = ... | |
| stl.cpp:154:10:154:14 | call to c_str | stl.cpp:156:7:156:8 | cs | |
| stl.cpp:163:18:163:24 | hello | stl.cpp:163:18:163:25 | call to basic_string | TAINT |
| stl.cpp:163:18:163:25 | call to basic_string | stl.cpp:168:8:168:9 | s1 | |
| stl.cpp:164:19:164:26 | call to basic_string | stl.cpp:169:8:169:9 | s2 | |
| stl.cpp:164:20:164:26 | hello | stl.cpp:164:19:164:26 | call to basic_string | TAINT |
| stl.cpp:166:8:166:14 | call to basic_string | stl.cpp:166:3:166:14 | ... = ... | |
| stl.cpp:166:8:166:14 | call to basic_string | stl.cpp:170:8:170:9 | s3 | |
| stl.cpp:166:8:166:14 | hello | stl.cpp:166:8:166:14 | call to basic_string | TAINT |
| stl.cpp:174:18:174:23 | call to source | stl.cpp:174:18:174:26 | call to basic_string | TAINT |
| stl.cpp:174:18:174:26 | call to basic_string | stl.cpp:179:8:179:9 | s1 | |
| stl.cpp:175:19:175:27 | call to basic_string | stl.cpp:180:8:180:9 | s2 | |
| stl.cpp:175:20:175:25 | call to source | stl.cpp:175:19:175:27 | call to basic_string | TAINT |
| stl.cpp:177:8:177:13 | call to source | stl.cpp:177:8:177:15 | call to basic_string | TAINT |
| stl.cpp:177:8:177:15 | call to basic_string | stl.cpp:177:3:177:15 | ... = ... | |
| stl.cpp:177:8:177:15 | call to basic_string | stl.cpp:181:8:181:9 | s3 | |
| stl.cpp:185:15:185:16 | call to basic_string | stl.cpp:186:20:186:21 | s1 | |
| stl.cpp:185:15:185:16 | call to basic_string | stl.cpp:188:8:188:9 | s1 | |
| stl.cpp:185:15:185:16 | call to basic_string | stl.cpp:190:8:190:9 | s1 | |
| stl.cpp:186:20:186:21 | s1 | stl.cpp:191:8:191:9 | s2 | |
| stl.cpp:188:8:188:9 | s1 | stl.cpp:188:3:188:9 | ... = ... | |
| stl.cpp:188:8:188:9 | s1 | stl.cpp:192:8:192:9 | s3 | |
| stl.cpp:196:19:196:40 | call to basic_string | stl.cpp:200:8:200:9 | s1 | |
| stl.cpp:196:32:196:37 | call to source | stl.cpp:196:19:196:40 | call to basic_string | TAINT |
| stl.cpp:198:8:198:28 | call to basic_string | stl.cpp:198:3:198:28 | ... = ... | |
| stl.cpp:198:8:198:28 | call to basic_string | stl.cpp:201:8:201:9 | s2 | |
| stl.cpp:198:20:198:25 | call to source | stl.cpp:198:8:198:28 | call to basic_string | TAINT |
| stl.cpp:97:12:97:17 | call to source | stl.cpp:101:7:101:7 | a | |
| stl.cpp:98:16:98:20 | 123 | stl.cpp:98:16:98:21 | call to basic_string | TAINT |
| stl.cpp:98:16:98:21 | call to basic_string | stl.cpp:102:7:102:7 | b | |
| stl.cpp:98:16:98:21 | call to basic_string | stl.cpp:104:7:104:7 | b | |
| stl.cpp:99:16:99:21 | call to source | stl.cpp:99:16:99:24 | call to basic_string | TAINT |
| stl.cpp:99:16:99:24 | call to basic_string | stl.cpp:103:7:103:7 | c | |
| stl.cpp:99:16:99:24 | call to basic_string | stl.cpp:105:7:105:7 | c | |
| stl.cpp:104:7:104:7 | b | stl.cpp:104:9:104:13 | call to c_str | TAINT |
| stl.cpp:105:7:105:7 | c | stl.cpp:105:9:105:13 | call to c_str | TAINT |
| stl.cpp:110:20:110:22 | call to basic_stringstream | stl.cpp:113:2:113:4 | ss1 | |
| stl.cpp:110:20:110:22 | call to basic_stringstream | stl.cpp:119:7:119:9 | ss1 | |
| stl.cpp:110:20:110:22 | call to basic_stringstream | stl.cpp:124:7:124:9 | ss1 | |
| stl.cpp:110:25:110:27 | call to basic_stringstream | stl.cpp:114:2:114:4 | ss2 | |
| stl.cpp:110:25:110:27 | call to basic_stringstream | stl.cpp:120:7:120:9 | ss2 | |
| stl.cpp:110:25:110:27 | call to basic_stringstream | stl.cpp:125:7:125:9 | ss2 | |
| stl.cpp:110:30:110:32 | call to basic_stringstream | stl.cpp:115:2:115:4 | ss3 | |
| stl.cpp:110:30:110:32 | call to basic_stringstream | stl.cpp:121:7:121:9 | ss3 | |
| stl.cpp:110:30:110:32 | call to basic_stringstream | stl.cpp:126:7:126:9 | ss3 | |
| stl.cpp:110:35:110:37 | call to basic_stringstream | stl.cpp:116:2:116:4 | ss4 | |
| stl.cpp:110:35:110:37 | call to basic_stringstream | stl.cpp:122:7:122:9 | ss4 | |
| stl.cpp:110:35:110:37 | call to basic_stringstream | stl.cpp:127:7:127:9 | ss4 | |
| stl.cpp:110:40:110:42 | call to basic_stringstream | stl.cpp:117:2:117:4 | ss5 | |
| stl.cpp:110:40:110:42 | call to basic_stringstream | stl.cpp:123:7:123:9 | ss5 | |
| stl.cpp:110:40:110:42 | call to basic_stringstream | stl.cpp:128:7:128:9 | ss5 | |
| stl.cpp:111:16:111:21 | call to source | stl.cpp:111:16:111:24 | call to basic_string | TAINT |
| stl.cpp:111:16:111:24 | call to basic_string | stl.cpp:117:9:117:9 | t | |
| stl.cpp:113:2:113:4 | ref arg ss1 | stl.cpp:119:7:119:9 | ss1 | |
| stl.cpp:113:2:113:4 | ref arg ss1 | stl.cpp:124:7:124:9 | ss1 | |
| stl.cpp:114:2:114:4 | ref arg ss2 | stl.cpp:120:7:120:9 | ss2 | |
| stl.cpp:114:2:114:4 | ref arg ss2 | stl.cpp:125:7:125:9 | ss2 | |
| stl.cpp:115:2:115:4 | ref arg ss3 | stl.cpp:121:7:121:9 | ss3 | |
| stl.cpp:115:2:115:4 | ref arg ss3 | stl.cpp:126:7:126:9 | ss3 | |
| stl.cpp:116:2:116:4 | ref arg ss4 | stl.cpp:122:7:122:9 | ss4 | |
| stl.cpp:116:2:116:4 | ref arg ss4 | stl.cpp:127:7:127:9 | ss4 | |
| stl.cpp:117:2:117:4 | ref arg ss5 | stl.cpp:123:7:123:9 | ss5 | |
| stl.cpp:117:2:117:4 | ref arg ss5 | stl.cpp:128:7:128:9 | ss5 | |
| stl.cpp:131:32:131:37 | source | stl.cpp:136:9:136:14 | source | |
| stl.cpp:133:20:133:22 | call to basic_stringstream | stl.cpp:135:2:135:4 | ss1 | |
| stl.cpp:133:20:133:22 | call to basic_stringstream | stl.cpp:138:7:138:9 | ss1 | |
| stl.cpp:133:20:133:22 | call to basic_stringstream | stl.cpp:140:7:140:9 | ss1 | |
| stl.cpp:133:25:133:27 | call to basic_stringstream | stl.cpp:136:2:136:4 | ss2 | |
| stl.cpp:133:25:133:27 | call to basic_stringstream | stl.cpp:139:7:139:9 | ss2 | |
| stl.cpp:133:25:133:27 | call to basic_stringstream | stl.cpp:141:7:141:9 | ss2 | |
| stl.cpp:135:2:135:4 | ref arg ss1 | stl.cpp:138:7:138:9 | ss1 | |
| stl.cpp:135:2:135:4 | ref arg ss1 | stl.cpp:140:7:140:9 | ss1 | |
| stl.cpp:136:2:136:4 | ref arg ss2 | stl.cpp:139:7:139:9 | ss2 | |
| stl.cpp:136:2:136:4 | ref arg ss2 | stl.cpp:141:7:141:9 | ss2 | |
| stl.cpp:154:16:154:28 | call to basic_string | stl.cpp:155:7:155:11 | path1 | |
| stl.cpp:154:17:154:26 | call to user_input | stl.cpp:154:16:154:28 | call to basic_string | TAINT |
| stl.cpp:155:7:155:11 | path1 | stl.cpp:155:13:155:17 | call to c_str | TAINT |
| stl.cpp:158:10:158:19 | call to user_input | stl.cpp:158:10:158:21 | call to basic_string | TAINT |
| stl.cpp:158:10:158:21 | call to basic_string | stl.cpp:158:2:158:21 | ... = ... | |
| stl.cpp:158:10:158:21 | call to basic_string | stl.cpp:159:7:159:11 | path2 | |
| stl.cpp:159:7:159:11 | path2 | stl.cpp:159:13:159:17 | call to c_str | TAINT |
| stl.cpp:161:15:161:24 | call to user_input | stl.cpp:161:15:161:27 | call to basic_string | TAINT |
| stl.cpp:161:15:161:27 | call to basic_string | stl.cpp:162:7:162:11 | path3 | |
| stl.cpp:162:7:162:11 | path3 | stl.cpp:162:13:162:17 | call to c_str | TAINT |
| stl.cpp:167:19:167:24 | call to source | stl.cpp:170:17:170:18 | cs | |
| stl.cpp:167:19:167:24 | call to source | stl.cpp:172:7:172:8 | cs | |
| stl.cpp:170:17:170:18 | cs | stl.cpp:170:17:170:19 | call to basic_string | TAINT |
| stl.cpp:170:17:170:19 | call to basic_string | stl.cpp:173:7:173:8 | ss | |
| stl.cpp:178:19:178:24 | call to source | stl.cpp:181:17:181:18 | cs | |
| stl.cpp:181:17:181:18 | cs | stl.cpp:181:17:181:19 | call to basic_string | TAINT |
| stl.cpp:181:17:181:19 | call to basic_string | stl.cpp:184:7:184:8 | ss | |
| stl.cpp:181:17:181:19 | call to basic_string | stl.cpp:187:7:187:8 | ss | |
| stl.cpp:184:7:184:8 | ss | stl.cpp:184:10:184:14 | call to c_str | TAINT |
| stl.cpp:184:10:184:14 | call to c_str | stl.cpp:184:2:184:16 | ... = ... | |
| stl.cpp:184:10:184:14 | call to c_str | stl.cpp:186:7:186:8 | cs | |
| stl.cpp:193:18:193:24 | hello | stl.cpp:193:18:193:25 | call to basic_string | TAINT |
| stl.cpp:193:18:193:25 | call to basic_string | stl.cpp:198:8:198:9 | s1 | |
| stl.cpp:194:19:194:26 | call to basic_string | stl.cpp:199:8:199:9 | s2 | |
| stl.cpp:194:20:194:26 | hello | stl.cpp:194:19:194:26 | call to basic_string | TAINT |
| stl.cpp:196:8:196:14 | call to basic_string | stl.cpp:196:3:196:14 | ... = ... | |
| stl.cpp:196:8:196:14 | call to basic_string | stl.cpp:200:8:200:9 | s3 | |
| stl.cpp:196:8:196:14 | hello | stl.cpp:196:8:196:14 | call to basic_string | TAINT |
| stl.cpp:204:18:204:23 | call to source | stl.cpp:204:18:204:26 | call to basic_string | TAINT |
| stl.cpp:204:18:204:26 | call to basic_string | stl.cpp:209:8:209:9 | s1 | |
| stl.cpp:205:19:205:27 | call to basic_string | stl.cpp:210:8:210:9 | s2 | |
| stl.cpp:205:20:205:25 | call to source | stl.cpp:205:19:205:27 | call to basic_string | TAINT |
| stl.cpp:207:8:207:13 | call to source | stl.cpp:207:8:207:15 | call to basic_string | TAINT |
| stl.cpp:207:8:207:15 | call to basic_string | stl.cpp:207:3:207:15 | ... = ... | |
| stl.cpp:207:8:207:15 | call to basic_string | stl.cpp:211:8:211:9 | s3 | |
| stl.cpp:215:15:215:16 | call to basic_string | stl.cpp:216:20:216:21 | s1 | |
| stl.cpp:215:15:215:16 | call to basic_string | stl.cpp:218:8:218:9 | s1 | |
| stl.cpp:215:15:215:16 | call to basic_string | stl.cpp:220:8:220:9 | s1 | |
| stl.cpp:216:20:216:21 | s1 | stl.cpp:221:8:221:9 | s2 | |
| stl.cpp:218:8:218:9 | s1 | stl.cpp:218:3:218:9 | ... = ... | |
| stl.cpp:218:8:218:9 | s1 | stl.cpp:222:8:222:9 | s3 | |
| stl.cpp:226:19:226:40 | call to basic_string | stl.cpp:230:8:230:9 | s1 | |
| stl.cpp:226:32:226:37 | call to source | stl.cpp:226:19:226:40 | call to basic_string | TAINT |
| stl.cpp:228:8:228:28 | call to basic_string | stl.cpp:228:3:228:28 | ... = ... | |
| stl.cpp:228:8:228:28 | call to basic_string | stl.cpp:231:8:231:9 | s2 | |
| stl.cpp:228:20:228:25 | call to source | stl.cpp:228:8:228:28 | call to basic_string | TAINT |
| stl.cpp:238:16:238:21 | call to source | stl.cpp:238:16:238:24 | call to basic_string | TAINT |
| stl.cpp:238:16:238:24 | call to basic_string | stl.cpp:239:15:239:15 | s | |
| stl.cpp:238:16:238:24 | call to basic_string | stl.cpp:243:33:243:33 | s | |
| stl.cpp:238:16:238:24 | call to basic_string | stl.cpp:243:50:243:50 | s | |
| stl.cpp:238:16:238:24 | call to basic_string | stl.cpp:247:16:247:16 | s | |
| stl.cpp:239:15:239:15 | call to begin | stl.cpp:239:15:239:15 | (__begin) | |
| stl.cpp:239:15:239:15 | call to begin | stl.cpp:239:15:239:15 | (__begin) | |
| stl.cpp:239:15:239:15 | call to begin | stl.cpp:239:15:239:15 | (__begin) | |
| stl.cpp:239:15:239:15 | call to end | stl.cpp:239:15:239:15 | (__end) | |
| stl.cpp:239:15:239:15 | call to operator* | stl.cpp:240:8:240:8 | c | |
| stl.cpp:239:15:239:15 | ref arg (__begin) | stl.cpp:239:15:239:15 | (__begin) | |
| stl.cpp:239:15:239:15 | ref arg (__begin) | stl.cpp:239:15:239:15 | (__begin) | |
| stl.cpp:239:15:239:15 | ref arg (__begin) | stl.cpp:239:15:239:15 | (__begin) | |
| stl.cpp:239:15:239:15 | ref arg (__range) | stl.cpp:239:15:239:15 | (__range) | |
| stl.cpp:239:15:239:15 | s | stl.cpp:239:15:239:15 | (__range) | |
| stl.cpp:239:15:239:15 | s | stl.cpp:239:15:239:15 | (__range) | |
| stl.cpp:239:15:239:15 | s | stl.cpp:239:15:239:15 | call to operator* | TAINT |
| stl.cpp:243:33:243:33 | ref arg s | stl.cpp:243:50:243:50 | s | |
| stl.cpp:243:33:243:33 | ref arg s | stl.cpp:247:16:247:16 | s | |
| stl.cpp:243:35:243:39 | call to begin | stl.cpp:243:44:243:45 | it | |
| stl.cpp:243:35:243:39 | call to begin | stl.cpp:243:61:243:62 | it | |
| stl.cpp:243:35:243:39 | call to begin | stl.cpp:244:9:244:10 | it | |
| stl.cpp:243:50:243:50 | ref arg s | stl.cpp:243:50:243:50 | s | |
| stl.cpp:243:50:243:50 | ref arg s | stl.cpp:247:16:247:16 | s | |
| stl.cpp:243:61:243:62 | ref arg it | stl.cpp:243:44:243:45 | it | |
| stl.cpp:243:61:243:62 | ref arg it | stl.cpp:243:61:243:62 | it | |
| stl.cpp:243:61:243:62 | ref arg it | stl.cpp:244:9:244:10 | it | |
| stl.cpp:247:16:247:16 | call to begin | stl.cpp:247:16:247:16 | (__begin) | |
| stl.cpp:247:16:247:16 | call to begin | stl.cpp:247:16:247:16 | (__begin) | |
| stl.cpp:247:16:247:16 | call to begin | stl.cpp:247:16:247:16 | (__begin) | |
| stl.cpp:247:16:247:16 | call to end | stl.cpp:247:16:247:16 | (__end) | |
| stl.cpp:247:16:247:16 | call to operator* | stl.cpp:248:8:248:8 | c | |
| stl.cpp:247:16:247:16 | ref arg (__begin) | stl.cpp:247:16:247:16 | (__begin) | |
| stl.cpp:247:16:247:16 | ref arg (__begin) | stl.cpp:247:16:247:16 | (__begin) | |
| stl.cpp:247:16:247:16 | ref arg (__begin) | stl.cpp:247:16:247:16 | (__begin) | |
| stl.cpp:247:16:247:16 | ref arg (__range) | stl.cpp:247:16:247:16 | (__range) | |
| stl.cpp:247:16:247:16 | s | stl.cpp:247:16:247:16 | (__range) | |
| stl.cpp:247:16:247:16 | s | stl.cpp:247:16:247:16 | (__range) | |
| stl.cpp:247:16:247:16 | s | stl.cpp:247:16:247:16 | call to operator* | TAINT |
| stl.cpp:251:28:251:33 | call to source | stl.cpp:251:28:251:36 | call to basic_string | TAINT |
| stl.cpp:251:28:251:36 | call to basic_string | stl.cpp:252:22:252:28 | const_s | |
| stl.cpp:252:22:252:22 | call to begin | stl.cpp:252:22:252:22 | (__begin) | |
| stl.cpp:252:22:252:22 | call to begin | stl.cpp:252:22:252:22 | (__begin) | |
| stl.cpp:252:22:252:22 | call to begin | stl.cpp:252:22:252:22 | (__begin) | |
| stl.cpp:252:22:252:22 | call to end | stl.cpp:252:22:252:22 | (__end) | |
| stl.cpp:252:22:252:22 | call to operator* | stl.cpp:253:8:253:8 | c | |
| stl.cpp:252:22:252:22 | ref arg (__begin) | stl.cpp:252:22:252:22 | (__begin) | |
| stl.cpp:252:22:252:22 | ref arg (__begin) | stl.cpp:252:22:252:22 | (__begin) | |
| stl.cpp:252:22:252:22 | ref arg (__begin) | stl.cpp:252:22:252:22 | (__begin) | |
| stl.cpp:252:22:252:28 | const_s | stl.cpp:252:22:252:22 | (__range) | |
| stl.cpp:252:22:252:28 | const_s | stl.cpp:252:22:252:22 | (__range) | |
| stl.cpp:252:22:252:28 | const_s | stl.cpp:252:22:252:22 | call to operator* | TAINT |
| stl.cpp:288:43:288:49 | source1 | stl.cpp:292:21:292:27 | source1 | |
| stl.cpp:288:43:288:49 | source1 | stl.cpp:306:33:306:39 | source1 | |
| stl.cpp:292:21:292:27 | source1 | stl.cpp:292:21:292:28 | call to vector | TAINT |
| stl.cpp:292:21:292:28 | call to vector | stl.cpp:294:14:294:14 | v | |
| stl.cpp:292:21:292:28 | call to vector | stl.cpp:298:38:298:38 | v | |
| stl.cpp:292:21:292:28 | call to vector | stl.cpp:298:55:298:55 | v | |
| stl.cpp:292:21:292:28 | call to vector | stl.cpp:302:15:302:15 | v | |
| stl.cpp:294:14:294:14 | call to begin | stl.cpp:294:14:294:14 | (__begin) | |
| stl.cpp:294:14:294:14 | call to begin | stl.cpp:294:14:294:14 | (__begin) | |
| stl.cpp:294:14:294:14 | call to begin | stl.cpp:294:14:294:14 | (__begin) | |
| stl.cpp:294:14:294:14 | call to end | stl.cpp:294:14:294:14 | (__end) | |
| stl.cpp:294:14:294:14 | call to operator* | stl.cpp:295:8:295:8 | x | |
| stl.cpp:294:14:294:14 | ref arg (__begin) | stl.cpp:294:14:294:14 | (__begin) | |
| stl.cpp:294:14:294:14 | ref arg (__begin) | stl.cpp:294:14:294:14 | (__begin) | |
| stl.cpp:294:14:294:14 | ref arg (__begin) | stl.cpp:294:14:294:14 | (__begin) | |
| stl.cpp:294:14:294:14 | ref arg (__range) | stl.cpp:294:14:294:14 | (__range) | |
| stl.cpp:294:14:294:14 | v | stl.cpp:294:14:294:14 | (__range) | |
| stl.cpp:294:14:294:14 | v | stl.cpp:294:14:294:14 | (__range) | |
| stl.cpp:294:14:294:14 | v | stl.cpp:294:14:294:14 | call to operator* | TAINT |
| stl.cpp:298:38:298:38 | ref arg v | stl.cpp:298:55:298:55 | v | |
| stl.cpp:298:38:298:38 | ref arg v | stl.cpp:302:15:302:15 | v | |
| stl.cpp:298:40:298:44 | call to begin | stl.cpp:298:49:298:50 | it | |
| stl.cpp:298:40:298:44 | call to begin | stl.cpp:298:66:298:67 | it | |
| stl.cpp:298:40:298:44 | call to begin | stl.cpp:299:9:299:10 | it | |
| stl.cpp:298:55:298:55 | ref arg v | stl.cpp:298:55:298:55 | v | |
| stl.cpp:298:55:298:55 | ref arg v | stl.cpp:302:15:302:15 | v | |
| stl.cpp:298:66:298:67 | ref arg it | stl.cpp:298:49:298:50 | it | |
| stl.cpp:298:66:298:67 | ref arg it | stl.cpp:298:66:298:67 | it | |
| stl.cpp:298:66:298:67 | ref arg it | stl.cpp:299:9:299:10 | it | |
| stl.cpp:302:15:302:15 | call to begin | stl.cpp:302:15:302:15 | (__begin) | |
| stl.cpp:302:15:302:15 | call to begin | stl.cpp:302:15:302:15 | (__begin) | |
| stl.cpp:302:15:302:15 | call to begin | stl.cpp:302:15:302:15 | (__begin) | |
| stl.cpp:302:15:302:15 | call to end | stl.cpp:302:15:302:15 | (__end) | |
| stl.cpp:302:15:302:15 | call to operator* | stl.cpp:303:8:303:8 | x | |
| stl.cpp:302:15:302:15 | ref arg (__begin) | stl.cpp:302:15:302:15 | (__begin) | |
| stl.cpp:302:15:302:15 | ref arg (__begin) | stl.cpp:302:15:302:15 | (__begin) | |
| stl.cpp:302:15:302:15 | ref arg (__begin) | stl.cpp:302:15:302:15 | (__begin) | |
| stl.cpp:302:15:302:15 | ref arg (__range) | stl.cpp:302:15:302:15 | (__range) | |
| stl.cpp:302:15:302:15 | v | stl.cpp:302:15:302:15 | (__range) | |
| stl.cpp:302:15:302:15 | v | stl.cpp:302:15:302:15 | (__range) | |
| stl.cpp:302:15:302:15 | v | stl.cpp:302:15:302:15 | call to operator* | TAINT |
| stl.cpp:306:33:306:39 | source1 | stl.cpp:306:33:306:40 | call to vector | TAINT |
| stl.cpp:306:33:306:40 | call to vector | stl.cpp:307:21:307:27 | const_v | |
| stl.cpp:307:21:307:21 | call to begin | stl.cpp:307:21:307:21 | (__begin) | |
| stl.cpp:307:21:307:21 | call to begin | stl.cpp:307:21:307:21 | (__begin) | |
| stl.cpp:307:21:307:21 | call to begin | stl.cpp:307:21:307:21 | (__begin) | |
| stl.cpp:307:21:307:21 | call to end | stl.cpp:307:21:307:21 | (__end) | |
| stl.cpp:307:21:307:21 | call to operator* | stl.cpp:308:8:308:8 | x | |
| stl.cpp:307:21:307:21 | ref arg (__begin) | stl.cpp:307:21:307:21 | (__begin) | |
| stl.cpp:307:21:307:21 | ref arg (__begin) | stl.cpp:307:21:307:21 | (__begin) | |
| stl.cpp:307:21:307:21 | ref arg (__begin) | stl.cpp:307:21:307:21 | (__begin) | |
| stl.cpp:307:21:307:27 | const_v | stl.cpp:307:21:307:21 | (__range) | |
| stl.cpp:307:21:307:27 | const_v | stl.cpp:307:21:307:21 | (__range) | |
| stl.cpp:307:21:307:27 | const_v | stl.cpp:307:21:307:21 | call to operator* | TAINT |
| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | |

View File

@@ -7,6 +7,26 @@ namespace std
typedef size_t streamsize;
struct ptrdiff_t;
template <class iterator_category,
class value_type,
class difference_type = ptrdiff_t,
class pointer_type = value_type*,
class reference_type = value_type&>
struct iterator {
iterator &operator++();
iterator operator++(int);
bool operator==(iterator other) const;
bool operator!=(iterator other) const;
reference_type operator*() const;
};
struct input_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
template <class T> class allocator {
public:
allocator() throw();
@@ -19,6 +39,16 @@ namespace std
basic_string(const charT* s, const Allocator& a = Allocator());
const charT* c_str() const;
typedef std::iterator<random_access_iterator_tag, charT> iterator;
typedef std::iterator<random_access_iterator_tag, const charT> const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
};
typedef basic_string<char> string;
@@ -202,3 +232,79 @@ void test_string_constructors_assignments()
}
}
void sink(char) {}
void test_range_based_for_loop_string() {
std::string s(source());
for(char c : s) {
sink(c); // tainted [NOT DETECTED by IR]
}
for(std::string::iterator it = s.begin(); it != s.end(); ++it) {
sink(*it); // tainted [NOT DETECTED]
}
for(char& c : s) {
sink(c); // tainted [NOT DETECTED by IR]
}
const std::string const_s(source());
for(const char& c : const_s) {
sink(c); // tainted [NOT DETECTED by IR]
}
}
namespace std {
template <class T>
class vector {
private:
void *data_;
public:
vector(int size);
T& operator[](int idx);
const T& operator[](int idx) const;
typedef std::iterator<random_access_iterator_tag, T> iterator;
typedef std::iterator<random_access_iterator_tag, const T> const_iterator;
iterator begin() noexcept;
iterator end() noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
};
}
void sink(int);
void test_range_based_for_loop_vector(int source1) {
// Tainting the vector by allocating a tainted length. This doesn't represent
// how a vector would typically get tainted, but it allows this test to avoid
// being concerned with std::vector modeling.
std::vector<int> v(source1);
for(int x : v) {
sink(x); // tainted [NOT DETECTED by IR]
}
for(std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
sink(*it); // tainted [NOT DETECTED]
}
for(int& x : v) {
sink(x); // tainted [NOT DETECTED by IR]
}
const std::vector<int> const_v(source1);
for(const int& x : const_v) {
sink(x); // tainted [NOT DETECTED by IR]
}
}

View File

@@ -22,6 +22,7 @@
| format.cpp:100:8:100:13 | buffer | format.cpp:99:30:99:43 | call to source |
| format.cpp:105:8:105:13 | buffer | format.cpp:104:31:104:45 | call to source |
| format.cpp:110:8:110:14 | wbuffer | format.cpp:109:38:109:52 | call to source |
| format.cpp:115:8:115:13 | buffer | format.cpp:114:37:114:50 | call to source |
| format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source |
| format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source |
| movableclass.cpp:44:8:44:9 | s1 | movableclass.cpp:39:21:39:26 | call to source |
@@ -31,21 +32,27 @@
| movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source |
| movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source |
| movableclass.cpp:65:11:65:11 | call to operator= | movableclass.cpp:65:13:65:18 | call to source |
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
| stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source |
| stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source |
| stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
| stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
| stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
| stl.cpp:143:7:143:8 | ss | stl.cpp:137:19:137:24 | call to source |
| stl.cpp:156:7:156:8 | cs | stl.cpp:148:19:148:24 | call to source |
| stl.cpp:157:7:157:8 | ss | stl.cpp:148:19:148:24 | call to source |
| stl.cpp:179:8:179:9 | s1 | stl.cpp:174:18:174:23 | call to source |
| stl.cpp:180:8:180:9 | s2 | stl.cpp:175:20:175:25 | call to source |
| stl.cpp:181:8:181:9 | s3 | stl.cpp:177:8:177:13 | call to source |
| stl.cpp:200:8:200:9 | s1 | stl.cpp:196:32:196:37 | call to source |
| stl.cpp:201:8:201:9 | s2 | stl.cpp:198:20:198:25 | call to source |
| stl.cpp:101:7:101:7 | a | stl.cpp:97:12:97:17 | call to source |
| stl.cpp:103:7:103:7 | c | stl.cpp:99:16:99:21 | call to source |
| stl.cpp:105:9:105:13 | call to c_str | stl.cpp:99:16:99:21 | call to source |
| stl.cpp:155:13:155:17 | call to c_str | stl.cpp:147:10:147:15 | call to source |
| stl.cpp:159:13:159:17 | call to c_str | stl.cpp:147:10:147:15 | call to source |
| stl.cpp:162:13:162:17 | call to c_str | stl.cpp:147:10:147:15 | call to source |
| stl.cpp:172:7:172:8 | cs | stl.cpp:167:19:167:24 | call to source |
| stl.cpp:173:7:173:8 | ss | stl.cpp:167:19:167:24 | call to source |
| stl.cpp:186:7:186:8 | cs | stl.cpp:178:19:178:24 | call to source |
| stl.cpp:187:7:187:8 | ss | stl.cpp:178:19:178:24 | call to source |
| stl.cpp:209:8:209:9 | s1 | stl.cpp:204:18:204:23 | call to source |
| stl.cpp:210:8:210:9 | s2 | stl.cpp:205:20:205:25 | call to source |
| stl.cpp:211:8:211:9 | s3 | stl.cpp:207:8:207:13 | call to source |
| stl.cpp:230:8:230:9 | s1 | stl.cpp:226:32:226:37 | call to source |
| stl.cpp:231:8:231:9 | s2 | stl.cpp:228:20:228:25 | call to source |
| stl.cpp:240:8:240:8 | c | stl.cpp:238:16:238:21 | call to source |
| stl.cpp:248:8:248:8 | c | stl.cpp:238:16:238:21 | call to source |
| stl.cpp:253:8:253:8 | c | stl.cpp:251:28:251:33 | call to source |
| stl.cpp:295:8:295:8 | x | stl.cpp:288:43:288:49 | source1 |
| stl.cpp:303:8:303:8 | x | stl.cpp:288:43:288:49 | source1 |
| stl.cpp:308:8:308:8 | x | stl.cpp:288:43:288:49 | source1 |
| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source |
| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source |
| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source |

View File

@@ -22,6 +22,7 @@
| format.cpp:100:8:100:13 | format.cpp:99:30:99:43 | AST only |
| format.cpp:105:8:105:13 | format.cpp:104:31:104:45 | AST only |
| format.cpp:110:8:110:14 | format.cpp:109:38:109:52 | AST only |
| format.cpp:115:8:115:13 | format.cpp:114:37:114:50 | AST only |
| movableclass.cpp:44:8:44:9 | movableclass.cpp:39:21:39:26 | AST only |
| movableclass.cpp:45:8:45:9 | movableclass.cpp:40:23:40:28 | AST only |
| movableclass.cpp:46:8:46:9 | movableclass.cpp:42:8:42:13 | AST only |
@@ -29,20 +30,26 @@
| movableclass.cpp:55:8:55:9 | movableclass.cpp:52:23:52:28 | AST only |
| movableclass.cpp:64:8:64:9 | movableclass.cpp:23:55:23:60 | AST only |
| movableclass.cpp:65:11:65:11 | movableclass.cpp:65:13:65:18 | AST only |
| stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only |
| stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only |
| stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only |
| stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only |
| stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only |
| stl.cpp:142:7:142:8 | stl.cpp:137:19:137:26 | IR only |
| stl.cpp:143:7:143:8 | stl.cpp:137:19:137:24 | AST only |
| stl.cpp:156:7:156:8 | stl.cpp:148:19:148:24 | AST only |
| stl.cpp:157:7:157:8 | stl.cpp:148:19:148:24 | AST only |
| stl.cpp:179:8:179:9 | stl.cpp:174:18:174:23 | AST only |
| stl.cpp:180:8:180:9 | stl.cpp:175:20:175:25 | AST only |
| stl.cpp:181:8:181:9 | stl.cpp:177:8:177:13 | AST only |
| stl.cpp:200:8:200:9 | stl.cpp:196:32:196:37 | AST only |
| stl.cpp:201:8:201:9 | stl.cpp:198:20:198:25 | AST only |
| stl.cpp:103:7:103:7 | stl.cpp:99:16:99:21 | AST only |
| stl.cpp:105:9:105:13 | stl.cpp:99:16:99:21 | AST only |
| stl.cpp:155:13:155:17 | stl.cpp:147:10:147:15 | AST only |
| stl.cpp:159:13:159:17 | stl.cpp:147:10:147:15 | AST only |
| stl.cpp:162:13:162:17 | stl.cpp:147:10:147:15 | AST only |
| stl.cpp:172:7:172:8 | stl.cpp:167:19:167:26 | IR only |
| stl.cpp:173:7:173:8 | stl.cpp:167:19:167:24 | AST only |
| stl.cpp:186:7:186:8 | stl.cpp:178:19:178:24 | AST only |
| stl.cpp:187:7:187:8 | stl.cpp:178:19:178:24 | AST only |
| stl.cpp:209:8:209:9 | stl.cpp:204:18:204:23 | AST only |
| stl.cpp:210:8:210:9 | stl.cpp:205:20:205:25 | AST only |
| stl.cpp:211:8:211:9 | stl.cpp:207:8:207:13 | AST only |
| stl.cpp:230:8:230:9 | stl.cpp:226:32:226:37 | AST only |
| stl.cpp:231:8:231:9 | stl.cpp:228:20:228:25 | AST only |
| stl.cpp:240:8:240:8 | stl.cpp:238:16:238:21 | AST only |
| stl.cpp:248:8:248:8 | stl.cpp:238:16:238:21 | AST only |
| stl.cpp:253:8:253:8 | stl.cpp:251:28:251:33 | AST only |
| stl.cpp:295:8:295:8 | stl.cpp:288:43:288:49 | AST only |
| stl.cpp:303:8:303:8 | stl.cpp:288:43:288:49 | AST only |
| stl.cpp:308:8:308:8 | stl.cpp:288:43:288:49 | AST only |
| structlikeclass.cpp:35:8:35:9 | structlikeclass.cpp:29:22:29:27 | AST only |
| structlikeclass.cpp:36:8:36:9 | structlikeclass.cpp:30:24:30:29 | AST only |
| structlikeclass.cpp:37:8:37:9 | structlikeclass.cpp:29:22:29:27 | AST only |

View File

@@ -1,10 +1,10 @@
| format.cpp:157:7:157:22 | (int)... | format.cpp:147:12:147:25 | call to source |
| format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source |
| format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source |
| stl.cpp:71:7:71:7 | (const char *)... | stl.cpp:67:12:67:17 | call to source |
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:26 | (const char *)... |
| stl.cpp:101:7:101:7 | (const char *)... | stl.cpp:97:12:97:17 | call to source |
| stl.cpp:101:7:101:7 | a | stl.cpp:97:12:97:17 | call to source |
| stl.cpp:172:7:172:8 | cs | stl.cpp:167:19:167:24 | call to source |
| stl.cpp:172:7:172:8 | cs | stl.cpp:167:19:167:26 | (const char *)... |
| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source |
| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source |
| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source |

View File

@@ -20,7 +20,7 @@
| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:30:8:30:13 | insert | |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:33:7:33:7 | operator= | |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:36:2:36:8 | MyClass | Constructor, NoArgConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:37:2:37:8 | MyClass | Constructor, ConversionConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:37:2:37:8 | MyClass | Constructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:38:2:38:8 | MyClass | Constructor, CopyConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:39:2:39:8 | MyClass | Constructor, ConversionConstructor, MoveConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:39:2:39:8 | MyClass | Constructor, MoveConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:40:2:40:13 | operator int | ConversionOperator |

View File

@@ -19,9 +19,6 @@ string describe(Class c, MemberFunction f) {
f instanceof Destructor and
result = "Destructor"
or
f instanceof ConversionConstructor and
result = "ConversionConstructor"
or
f instanceof CopyConstructor and
result = "CopyConstructor"
or

View File

@@ -177,4 +177,43 @@ void FalseNegativeTestCases()
for (int i = 100; i > 0; i += 2) {}
// For comparison
for (int i = 100; i > 0; i ++ ) {} // BUG
}
}
void IntendedOverflow(unsigned char p)
{
const unsigned char m = 10;
unsigned char i;
signed char s;
for (i = 63; i < 64; i--) {} // GOOD (legitimate way to count down with an unsigned)
for (i = 63; i < 128; i--) {} // DUBIOUS (could still be a typo?)
for (i = 63; i < 255; i--) {} // GOOD
for (i = m - 1; i < m; i--) {} // GOOD
for (i = m - 2; i < m; i--) {} // DUBIOUS
for (i = m; i < m + 1; i--) {} // GOOD
for (s = 63; s < 64; s--) {} // BAD (signed numbers don't wrap at 0 / at all)
for (s = m + 1; s < m; s--) {} // BAD (never runs)
for (i = p - 1; i < p; i--) {} // GOOD
for (s = p - 1; s < p; s--) {} // BAD [NOT DETECTED]
{
int n;
n = 64;
for (i = n - 1; i < n; i--) {} // GOOD
n = 64;
for (i = n - 1; i < 64; i--) {} // GOOD
n = 64;
for (i = 63; i < n; i--) {} // GOOD
n = 64;
for (s = n - 1; s < n; s--) {} // BAD [NOT DETECTED]
n = 64;
for (s = n - 1; s < 64; s--) {} // BAD
n = 64;
for (s = 63; s < n; s--) {} // BAD [NOT DETECTED]
}
}

View File

@@ -20,3 +20,6 @@
| inconsistentLoopDirection.cpp:140:5:142:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (200), but the terminal condition is lower (0). |
| inconsistentLoopDirection.cpp:175:5:175:36 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (10). |
| inconsistentLoopDirection.cpp:179:5:179:38 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
| inconsistentLoopDirection.cpp:196:5:196:32 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (63), but the terminal condition is higher (64). |
| inconsistentLoopDirection.cpp:197:5:197:34 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (... + ...), but the terminal condition is always false. |
| inconsistentLoopDirection.cpp:215:3:215:33 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (... - ...), but the terminal condition is higher (64). |

View File

@@ -2,3 +2,4 @@
| tests.cpp:259:2:259:8 | call to sprintf | This 'call to sprintf' operation requires 17 bytes but the destination is only 10 bytes. |
| tests.cpp:272:2:272:8 | call to sprintf | This 'call to sprintf' operation requires 9 bytes but the destination is only 8 bytes. |
| tests.cpp:273:2:273:8 | call to sprintf | This 'call to sprintf' operation requires 9 bytes but the destination is only 8 bytes. |
| tests.cpp:308:3:308:9 | call to sprintf | This 'call to sprintf' operation requires 9 bytes but the destination is only 8 bytes. |

View File

@@ -289,3 +289,22 @@ void test5(va_list args, float f)
vsprintf(buffer4, "123", args); // GOOD
vsprintf(buffer4, "1234", args); // BAD: buffer overflow [NOT DETECTED]
}
namespace custom_sprintf_impl {
int sprintf(char *buf, const char *format, ...)
{
__builtin_va_list args;
int i;
__builtin_va_start(args, format);
i = vsprintf(buf, format, args);
__builtin_va_end(args);
return i;
}
void regression_test1()
{
char buffer8[8];
sprintf(buffer8, "12345678"); // BAD: potential buffer overflow
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
class StaticAssert extends @static_assert {
string toString() { none() }
}
class Expr extends @expr {
string toString() { none() }
}
class Location extends @location_default {
string toString() { none() }
}
class NameSpace extends @namespace {
string toString() { none() }
}
from StaticAssert sa, Expr condition, string message, Location loc, NameSpace ns
where
static_asserts(sa, condition, message, loc) and
namespaces(ns, "")
select sa, condition, message, loc, ns

View File

@@ -0,0 +1,4 @@
description: Give static_assert's an enclosing element
compatibility: partial
static_asserts.rel: run static_asserts.qlo

9
csharp/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"recommendations": [
"github.vscode-codeql",
"ms-dotnettools.csharp",
"formulahendry.dotnet-test-explorer",
"hbenl.vscode-test-explorer"
],
"unwantedRecommendations": []
}

7
csharp/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"dotnet-test-explorer.enableTelemetry": false,
"dotnet-test-explorer.testProjectPath": "**/*Tests.@(csproj|vbproj|fsproj)",
"dotnet-test-explorer.testArguments": "/property:GenerateTargetFrameworkAttribute=false",
"csharp.supressBuildAssetsNotification": true,
"csharp.suppressDotnetRestoreNotification": true
}

53
csharp/.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,53 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "dotnet build",
"command": "dotnet",
"type": "shell",
"args": [
"build",
// Ask dotnet build to generate full paths for file names.
"/property:GenerateFullPaths=true",
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
"/consoleloggerparameters:NoSummary"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
},
{
"label": "dotnet rebuild",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"--no-incremental",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
},
{
"label": "dotnet test",
"command": "dotnet",
"type": "shell",
"args": [
"test",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"group": "test",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -19,15 +19,15 @@ namespace Semmle.Autobuild.CSharp.Tests
/// <summary>
/// List of strings passed to FileDelete.
/// </summary>
public IList<string> FileDeleteIn = new List<string>();
public readonly IList<string> FileDeleteIn = new List<string>();
void IBuildActions.FileDelete(string file)
{
FileDeleteIn.Add(file);
}
public IList<string> FileExistsIn = new List<string>();
public IDictionary<string, bool> FileExists = new Dictionary<string, bool>();
public readonly IList<string> FileExistsIn = new List<string>();
public readonly IDictionary<string, bool> FileExists = new Dictionary<string, bool>();
bool IBuildActions.FileExists(string file)
{
@@ -39,10 +39,10 @@ namespace Semmle.Autobuild.CSharp.Tests
throw new ArgumentException("Missing FileExists " + file);
}
public IList<string> RunProcessIn = new List<string>();
public IDictionary<string, int> RunProcess = new Dictionary<string, int>();
public IDictionary<string, string> RunProcessOut = new Dictionary<string, string>();
public IDictionary<string, string> RunProcessWorkingDirectory = new Dictionary<string, string>();
public readonly IList<string> RunProcessIn = new List<string>();
public readonly IDictionary<string, int> RunProcess = new Dictionary<string, int>();
public readonly IDictionary<string, string> RunProcessOut = new Dictionary<string, string>();
public readonly IDictionary<string, string> RunProcessWorkingDirectory = new Dictionary<string, string>();
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? env, out IList<string> stdOut)
{
@@ -72,14 +72,14 @@ namespace Semmle.Autobuild.CSharp.Tests
throw new ArgumentException("Missing RunProcess " + pattern);
}
public IList<string> DirectoryDeleteIn = new List<string>();
public readonly IList<string> DirectoryDeleteIn = new List<string>();
void IBuildActions.DirectoryDelete(string dir, bool recursive)
{
DirectoryDeleteIn.Add(dir);
}
public IDictionary<string, bool> DirectoryExists = new Dictionary<string, bool>();
public readonly IDictionary<string, bool> DirectoryExists = new Dictionary<string, bool>();
bool IBuildActions.DirectoryExists(string dir)
{
@@ -88,7 +88,7 @@ namespace Semmle.Autobuild.CSharp.Tests
throw new ArgumentException("Missing DirectoryExists " + dir);
}
public IDictionary<string, string?> GetEnvironmentVariable = new Dictionary<string, string?>();
public readonly IDictionary<string, string?> GetEnvironmentVariable = new Dictionary<string, string?>();
string? IBuildActions.GetEnvironmentVariable(string name)
{
@@ -104,7 +104,7 @@ namespace Semmle.Autobuild.CSharp.Tests
return GetCurrentDirectory;
}
public IDictionary<string, string> EnumerateFiles = new Dictionary<string, string>();
public readonly IDictionary<string, string> EnumerateFiles = new Dictionary<string, string>();
IEnumerable<string> IBuildActions.EnumerateFiles(string dir)
{
@@ -113,7 +113,7 @@ namespace Semmle.Autobuild.CSharp.Tests
throw new ArgumentException("Missing EnumerateFiles " + dir);
}
public IDictionary<string, string> EnumerateDirectories = new Dictionary<string, string>();
public readonly IDictionary<string, string> EnumerateDirectories = new Dictionary<string, string>();
IEnumerable<string> IBuildActions.EnumerateDirectories(string dir)
{
@@ -137,7 +137,8 @@ namespace Semmle.Autobuild.CSharp.Tests
{
}
public IDictionary<string, XmlDocument> LoadXml = new Dictionary<string, XmlDocument>();
public readonly IDictionary<string, XmlDocument> LoadXml = new Dictionary<string, XmlDocument>();
XmlDocument IBuildActions.LoadXml(string filename)
{
if (LoadXml.TryGetValue(filename, out var xml))
@@ -178,10 +179,10 @@ namespace Semmle.Autobuild.CSharp.Tests
public class BuildScriptTests
{
TestActions Actions = new TestActions();
readonly TestActions Actions = new TestActions();
// Records the arguments passed to StartCallback.
IList<string> StartCallbackIn = new List<string>();
readonly IList<string> StartCallbackIn = new List<string>();
void StartCallback(string s, bool silent)
{
@@ -189,8 +190,8 @@ namespace Semmle.Autobuild.CSharp.Tests
}
// Records the arguments passed to EndCallback
IList<string> EndCallbackIn = new List<string>();
IList<int> EndCallbackReturn = new List<int>();
readonly IList<string> EndCallbackIn = new List<string>();
readonly IList<int> EndCallbackReturn = new List<int>();
void EndCallback(int ret, string s, bool silent)
{

View File

@@ -19,6 +19,12 @@ namespace Semmle.Autobuild.Shared
BuildScript Analyse(Autobuilder builder, bool auto);
}
/// <summary>
/// A delegate used to wrap a build script in an environment where an appropriate
/// version of .NET Core is automatically installed.
/// </summary>
public delegate BuildScript WithDotNet(Autobuilder builder, Func<IDictionary<string, string>?, BuildScript> f);
/// <summary>
/// Exception indicating that environment variables are missing or invalid.
/// </summary>

View File

@@ -11,9 +11,9 @@ namespace Semmle.Autobuild.Shared
/// </summary>
public class BuildCommandAutoRule : IBuildRule
{
private readonly Func<Autobuilder, Func<IDictionary<string, string>?, BuildScript>, BuildScript> withDotNet;
private readonly WithDotNet withDotNet;
public BuildCommandAutoRule(Func<Autobuilder, Func<IDictionary<string, string>?, BuildScript>, BuildScript> withDotNet)
public BuildCommandAutoRule(WithDotNet withDotNet)
{
this.withDotNet = withDotNet;
}

View File

@@ -8,9 +8,9 @@ namespace Semmle.Autobuild.Shared
/// </summary>
public class BuildCommandRule : IBuildRule
{
private readonly Func<Autobuilder, Func<IDictionary<string, string>?, BuildScript>, BuildScript> withDotNet;
private readonly WithDotNet withDotNet;
public BuildCommandRule(Func<Autobuilder, Func<IDictionary<string, string>?, BuildScript>, BuildScript> withDotNet)
public BuildCommandRule(WithDotNet withDotNet)
{
this.withDotNet = withDotNet;
}

View File

@@ -0,0 +1,18 @@
name: "csharp"
display_name: "C#"
version: 1.22.1
column_kind: "utf16"
extra_env_vars:
DOTNET_GENERATE_ASPNET_CERTIFICATE: "false"
COR_ENABLE_PROFILING: "1"
COR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}"
COR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}"
CORECLR_ENABLE_PROFILING: "1"
CORECLR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}"
CORECLR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}"
file_types:
- name: cs
display_name: C# sources
extensions:
- .cs
legacy_qltest_extraction: true

View File

@@ -185,7 +185,7 @@ namespace Semmle.Extraction.Tests
class StringTrapEmitter : ITrapEmitter
{
string Content;
readonly string Content;
public StringTrapEmitter(string content)
{
Content = content;

View File

@@ -191,7 +191,7 @@ namespace Semmle.Extraction.Tests
[Fact]
public void ArchiveArguments()
{
var sw = new StringWriter();
using var sw = new StringWriter();
var file = Path.GetTempFileName();
try

View File

@@ -32,7 +32,7 @@ namespace Semmle.Extraction.Entities
class GeneratedLocationFactory : ICachedEntityFactory<string?, GeneratedLocation>
{
public static GeneratedLocationFactory Instance = new GeneratedLocationFactory();
public static readonly GeneratedLocationFactory Instance = new GeneratedLocationFactory();
public GeneratedLocation Create(Context cx, string? init) => new GeneratedLocation(cx);
}

View File

@@ -21,15 +21,7 @@ namespace SemmleTests.Semmle.Util
// Change directories to a directory that is in canonical form.
Directory.SetCurrentDirectory(cache.GetCanonicalPath(Path.GetTempPath()));
if (Win32.IsWindows())
{
root = @"X:\";
}
else
{
root = "/";
}
root = Win32.IsWindows() ? @"X:\" : "/";
}
void IDisposable.Dispose()
@@ -143,10 +135,10 @@ namespace SemmleTests.Semmle.Util
Assert.Equal(0, cache.CacheSize);
// The file "ABC" will fill the cache with parent directory info.
string cp = cache.GetCanonicalPath("ABC");
cache.GetCanonicalPath("ABC");
Assert.True(cache.CacheSize == 2);
cp = cache.GetCanonicalPath("def");
string cp = cache.GetCanonicalPath("def");
Assert.Equal(2, cache.CacheSize);
Assert.Equal(Path.GetFullPath("def"), cp);
}

View File

@@ -93,7 +93,7 @@ namespace SemmleTests.Semmle.Util
Assert.Equal("def", File.ReadAllText(shortPath));
}
byte[] buffer1 = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
readonly byte[] buffer1 = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
[Fact]
public void CreateShortStream()

View File

@@ -0,0 +1,3 @@
name: codeql-csharp-examples
version: 0.0.0
libraryPathDependencies: codeql-csharp

View File

@@ -6,7 +6,7 @@
* @problem.severity recommendation
* @precision high
* @id cs/inefficient-containskey
* @tag maintainability efficiency
* @tags maintainability efficiency
*/
import csharp

View File

@@ -7,3 +7,10 @@
tags contain:
- ide-contextual-queries/local-definitions
- ide-contextual-queries/local-references
- query: Metrics/Dependencies/ExternalDependencies.ql
- query: Metrics/Dependencies/ExternalDependenciesSourceLinks.ql
- query: Metrics/Files/FLinesOfCode.ql
- query: Metrics/Files/FLinesOfCommentedCode.ql
- query: Metrics/Files/FLinesOfComment.ql
- query: Metrics/Files/FLinesOfDuplicatedCode.ql
- query: Metrics/Files/FNumberOfTests.ql

View File

@@ -0,0 +1,23 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The <code>DataSet</code> and <code>DataTable</code> types are legacy .NET components that you can use to represent data sets as managed objects.</p>
<p>While <code>DataSet</code> and <code>DataTable</code> do impose default limitations on the types that are allowed to be present while deserializing XML payloads, <code>DataSet</code> and <code>DataTable</code> are in general not safe when populated with untrusted input.</p>
<p>Please visit <a href="https://go.microsoft.com/fwlink/?linkid=2132227">DataSet and DataTable security guidance</a> for more details.</p>
</overview>
<recommendation>
<p>Please review the <a href="https://go.microsoft.com/fwlink/?linkid=2132227">DataSet and DataTable security guidance</a> before making use of these types for serialization.</p>
</recommendation>
<references>
<li>Microsoft Docs<a href="https://go.microsoft.com/fwlink/?linkid=2132227">DataSet and DataTable security guidance</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,100 @@
/**
* Provides classes for `DataSet` or `DataTable` deserialization queries.
*
* Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details.
*/
import csharp
/**
* Abstract class that depends or inherits from `DataSet` or `DataTable` types.
*/
abstract class DataSetOrTableRelatedClass extends Class { }
/**
* `DataSet`, `DataTable` types, or any types derived from them.
*/
class DataSetOrTable extends DataSetOrTableRelatedClass {
DataSetOrTable() {
this.getABaseType*().getQualifiedName() = "System.Data.DataTable" or
this.getABaseType*().getQualifiedName() = "System.Data.DataSet"
}
}
/**
* A Class that include a property or generic collection of type `DataSet` and `DataTable`
*/
class ClassWithDataSetOrTableMember extends DataSetOrTableRelatedClass {
ClassWithDataSetOrTableMember() {
this.getAMember().(AssignableMember).getType() instanceof DataSetOrTable
or
exists(Property p | p = this.getAProperty() |
p.getType() instanceof DataSetOrTable or
p.getType().(ConstructedGeneric).getATypeArgument() instanceof DataSetOrTable
)
}
}
/**
* Serializable types
*/
class SerializableClass extends Class {
SerializableClass() {
(
this.getABaseType*().getQualifiedName() = "System.Xml.Serialization.XmlSerializer" or
this.getABaseType*().getQualifiedName() = "System.Runtime.Serialization.ISerializable" or
this.getABaseType*().getQualifiedName() = "System.Runtime.Serialization.XmlObjectSerializer" or
this.getABaseType*().getQualifiedName() =
"System.Runtime.Serialization.ISerializationSurrogateProvider" or
this.getABaseType*().getQualifiedName() =
"System.Runtime.Serialization.XmlSerializableServices" or
this.getABaseType*().getQualifiedName() = "System.Xml.Serialization.IXmlSerializable"
)
or
exists(Attribute a | a = this.getAnAttribute() |
a.getType().getQualifiedName() = "System.SerializableAttribute"
)
}
}
/**
* Holds if the serializable class `c` has a property or field `m` that is of `DataSet` or `DataTable` related type
*/
predicate isClassUnsafeXmlSerializerImplementation(SerializableClass c, AssignableMember am) {
am = c.getAMember() and
am.getType() instanceof DataSetOrTableRelatedClass
}
/**
* Serializable class that has a property or field that is of `DataSet` or `DataTable` related type
*/
class UnsafeXmlSerializerImplementation extends SerializableClass {
UnsafeXmlSerializerImplementation() { isClassUnsafeXmlSerializerImplementation(this, _) }
}
/**
* Method that may be unsafe when used to deserialize DataSet and DataTable related types
*/
class UnsafeXmlReadMethod extends Method {
UnsafeXmlReadMethod() {
this.getQualifiedName() = "System.Data.DataTable.ReadXml"
or
this.getQualifiedName() = "System.Data.DataTable.ReadXmlSchema"
or
this.getQualifiedName() = "System.Data.DataSet.ReadXml"
or
this.getQualifiedName() = "System.Data.DataSet.ReadXmlSchema"
or
this.getName().matches("ReadXml%") and
exists(Class c | c.getAMethod() = this |
c.getABaseType*() instanceof DataSetOrTableRelatedClass
)
}
}
/**
* MethodCall that may be unsafe when used to deserialize DataSet and DataTable related types
*/
class UnsafeXmlReadMethodCall extends MethodCall {
UnsafeXmlReadMethodCall() { exists(UnsafeXmlReadMethod uxrm | uxrm.getACall() = this) }
}

View File

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

View File

@@ -0,0 +1,16 @@
/**
* @name Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types
* @description Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types may lead to the usage of dangerous functionality. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details.
* @kind problem
* @problem.severity warning
* @id cs/dataset-serialization/defining-dataset-related-type
* @tags security
*/
import csharp
import DataSetSerialization
from DataSetOrTableRelatedClass dstc
where dstc.fromSource()
select dstc,
"Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details."

View File

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

View File

@@ -0,0 +1,20 @@
/**
* @name Defining a potentially unsafe XML serializer
* @description Defining an XML serializable class that includes members that derive from DataSet or DataTable type may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details.
* @kind problem
* @problem.severity error
* @precision medium
* @id cs/dataset-serialization/defining-potentially-unsafe-xml-serializer
* @tags security
*/
import csharp
import DataSetSerialization
from UnsafeXmlSerializerImplementation c, Member m
where
c.fromSource() and
isClassUnsafeXmlSerializerImplementation(c, m)
select m,
"Defining an serializable class $@ that has member $@ of a type that is derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details.",
c, c.toString(), m, m.toString()

View File

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

View File

@@ -0,0 +1,43 @@
/**
* @name Unsafe type is used in data contract serializer
* @description Unsafe type is used in data contract serializer. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details."
* @kind problem
* @problem.severity error
* @precision medium
* @id cs/dataset-serialization/unsafe-type-used-data-contract-serializer
* @tags security
*/
import csharp
import DataSetSerialization
predicate xmlSerializerConstructorArgument(Expr e) {
exists(ObjectCreation oc, Constructor c | e = oc.getArgument(0) |
c = oc.getTarget() and
c.getDeclaringType().getABaseType*().hasQualifiedName("System.Xml.Serialization.XmlSerializer")
)
}
predicate unsafeDataContractTypeCreation(Expr e) {
exists(MethodCall gt |
gt.getTarget().getName() = "GetType" and
e = gt and
gt.getQualifier().getType() instanceof DataSetOrTableRelatedClass
)
or
e.(TypeofExpr).getTypeAccess().getTarget() instanceof DataSetOrTableRelatedClass
}
class Conf extends DataFlow::Configuration {
Conf() { this = "FlowToDataSerializerConstructor" }
override predicate isSource(DataFlow::Node node) { unsafeDataContractTypeCreation(node.asExpr()) }
override predicate isSink(DataFlow::Node node) { xmlSerializerConstructorArgument(node.asExpr()) }
}
from Conf conf, DataFlow::Node source, DataFlow::Node sink
where conf.hasFlow(source, sink)
select sink,
"Unsafe type is used in data contract serializer. Make sure $@ comes from the trusted source.",
source, source.toString()

View File

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

View File

@@ -0,0 +1,16 @@
/**
* @name XML deserialization with a type type derived from DataSet or DataTable
* @description Making an XML deserialization call with a type derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details."
* @kind problem
* @problem.severity error
* @precision medium
* @id cs/dataset-serialization/xml-deserialization-with-dataset
* @tags security
*/
import csharp
import DataSetSerialization
from UnsafeXmlReadMethodCall mc
select mc,
"Making an XML deserialization call with a type derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details."

View File

@@ -1,7 +1,7 @@
/**
* Provides classes representing individual opcodes.
*
* See ECMA-335 (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf)
* See ECMA-335 (https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf)
* pages 32-101 for a detailed explanation of these instructions.
*/

View File

@@ -876,6 +876,8 @@ module Internal {
not e.(QualifiableExpr).isConditional()
or
e instanceof SuppressNullableWarningExpr
or
e.stripCasts().getType() = any(ValueType t | not t instanceof NullableType)
}
/** Holds if expression `e2` is a non-`null` value whenever `e1` is. */

View File

@@ -197,42 +197,44 @@ private predicate isNullDefaultArgument(Ssa::ExplicitDefinition def, AlwaysNullE
/** Holds if `def` is an SSA definition that may be `null`. */
private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason) {
// A variable compared to `null` might be `null`
exists(G::DereferenceableExpr de | de = def.getARead() |
reason = de.getANullCheck(_, true) and
msg = "as suggested by $@ null check" and
not de = any(Ssa::PseudoDefinition pdef).getARead() and
strictcount(Element e | e = any(Ssa::Definition def0 | de = def0.getARead()).getElement()) = 1 and
not nonNullDef(def) and
// Don't use a check as reason if there is a `null` assignment
// or argument
not def.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof MaybeNullExpr and
not isMaybeNullArgument(def, _)
)
or
// A parameter might be `null` if there is a `null` argument somewhere
isMaybeNullArgument(def, reason) and
not nonNullDef(def) and
(
if reason instanceof AlwaysNullExpr
then msg = "because of $@ null argument"
else msg = "because of $@ potential null argument"
)
or
isNullDefaultArgument(def, reason) and msg = "because the parameter has a null default value"
or
// If the source of a variable is `null` then the variable may be `null`
exists(AssignableDefinition adef | adef = def.(Ssa::ExplicitDefinition).getADefinition() |
adef.getSource() instanceof MaybeNullExpr and
reason = adef.getExpr() and
msg = "because of $@ assignment"
)
or
// A variable of nullable type may be null
exists(Dereference d | dereferenceAt(_, _, def, d) |
d.hasNullableType() and
not def instanceof Ssa::PseudoDefinition and
reason = def.getSourceVariable().getAssignable() and
msg = "because it has a nullable type"
// A variable compared to `null` might be `null`
exists(G::DereferenceableExpr de | de = def.getARead() |
reason = de.getANullCheck(_, true) and
msg = "as suggested by $@ null check" and
not de = any(Ssa::PseudoDefinition pdef).getARead() and
strictcount(Element e | e = any(Ssa::Definition def0 | de = def0.getARead()).getElement()) = 1 and
// Don't use a check as reason if there is a `null` assignment
// or argument
not def.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof MaybeNullExpr and
not isMaybeNullArgument(def, _)
)
or
// A parameter might be `null` if there is a `null` argument somewhere
isMaybeNullArgument(def, reason) and
(
if reason instanceof AlwaysNullExpr
then msg = "because of $@ null argument"
else msg = "because of $@ potential null argument"
)
or
isNullDefaultArgument(def, reason) and msg = "because the parameter has a null default value"
or
// If the source of a variable is `null` then the variable may be `null`
exists(AssignableDefinition adef | adef = def.(Ssa::ExplicitDefinition).getADefinition() |
adef.getSource() instanceof MaybeNullExpr and
reason = adef.getExpr() and
msg = "because of $@ assignment"
)
or
// A variable of nullable type may be null
exists(Dereference d | dereferenceAt(_, _, def, d) |
d.hasNullableType() and
not def instanceof Ssa::PseudoDefinition and
reason = def.getSourceVariable().getAssignable() and
msg = "because it has a nullable type"
)
)
}

View File

@@ -395,7 +395,7 @@ private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, bool
f.isFieldLike() and
f instanceof InstanceFieldOrProperty
or
exists(AccessPath ap |
exists(LibraryFlow::AdjustedAccessPath ap |
LibraryFlow::libraryFlowSummary(_, _, ap, _, _, _) and
ap.contains(f.getContent())
)
@@ -440,7 +440,7 @@ private predicate fieldOrPropertyRead(Expr e1, Content c, FieldOrPropertyRead e2
ret.isFieldLike() and
ret = e2.getTarget()
or
exists(AccessPath ap, Property target |
exists(LibraryFlow::AdjustedAccessPath ap, Property target |
LibraryFlow::libraryFlowSummary(_, _, _, _, ap, _) and
ap.contains(ret.getContent()) and
target.getGetter() = e2.(PropertyCall).getARuntimeTarget() and
@@ -569,8 +569,9 @@ private module Cached {
)
} or
TLibraryCodeNode(
ControlFlow::Node callCfn, CallableFlowSource source, AccessPath sourceAp,
CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue, LibraryCodeNodeState state
ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp,
CallableFlowSink sink, AdjustedAccessPath sinkAp, boolean preservesValue,
LibraryCodeNodeState state
) {
libraryFlowSummary(callCfn.getElement(), source, sourceAp, sink, sinkAp, preservesValue) and
(
@@ -1451,11 +1452,12 @@ import OutNodes
module LibraryFlow {
pragma[nomagic]
private ValueOrRefType getPreciseSourceProperty0(
Call call, CallableFlowSource source, AccessPath sourceAp, Property p
Call call, CallableFlowSource source, AccessPath sourceAp, Property p, AccessPath sourceApTail
) {
exists(LibraryTypeDataFlow ltdf, Property p0 |
ltdf.callableFlow(source, sourceAp, _, _, call.getTarget().getSourceDeclaration(), _) and
sourceAp = AccessPath::property(p0) and
sourceAp.getHead().(PropertyContent).getProperty() = p0 and
sourceAp.getTail() = sourceApTail and
overridesOrImplementsSourceDecl(p, p0) and
result = source.getSourceType(call)
)
@@ -1476,18 +1478,19 @@ module LibraryFlow {
*/
pragma[nomagic]
private Property getPreciseSourceProperty(
Call call, CallableFlowSource source, AccessPath sourceAp
Call call, CallableFlowSource source, AccessPath sourceAp, AccessPath sourceApTail
) {
getPreciseSourceProperty0(call, source, sourceAp, result).hasMember(result)
getPreciseSourceProperty0(call, source, sourceAp, result, sourceApTail).hasMember(result)
}
pragma[nomagic]
private ValueOrRefType getPreciseSinkProperty0(
Call call, CallableFlowSink sink, AccessPath sinkAp, Property p
Call call, CallableFlowSink sink, AccessPath sinkAp, Property p, AccessPath sinkApTail
) {
exists(LibraryTypeDataFlow ltdf, Property p0 |
ltdf.callableFlow(_, _, sink, sinkAp, call.getTarget().getSourceDeclaration(), _) and
sinkAp = AccessPath::property(p0) and
sinkAp.getHead().(PropertyContent).getProperty() = p0 and
sinkAp.getTail() = sinkApTail and
overridesOrImplementsSourceDecl(p, p0) and
result = sink.getSinkType(call)
)
@@ -1510,8 +1513,132 @@ module LibraryFlow {
* from `IEnumerator<T>`.
*/
pragma[nomagic]
private Property getPreciseSinkProperty(Call call, CallableFlowSink sink, AccessPath sinkAp) {
getPreciseSinkProperty0(call, sink, sinkAp, result).hasMember(result)
private Property getPreciseSinkProperty(
Call call, CallableFlowSink sink, AccessPath sinkAp, AccessPath sinkApTail
) {
getPreciseSinkProperty0(call, sink, sinkAp, result, sinkApTail).hasMember(result)
}
predicate adjustSourceHead(
Call call, CallableFlowSource source, AccessPath sourceAp0, AccessPath sourceApTail,
PropertyContent p
) {
overridesOrImplementsSourceDecl(p.getProperty(),
getPreciseSourceProperty(call, source, sourceAp0, sourceApTail).getSourceDeclaration())
}
predicate adjustSinkHead(
Call call, CallableFlowSink sink, AccessPath sinkAp0, AccessPath sinkApTail, PropertyContent p
) {
p.getProperty() = getPreciseSinkProperty(call, sink, sinkAp0, sinkApTail).getSourceDeclaration()
}
private newtype TAdjustedAccessPath =
TOriginalAccessPath(AccessPath ap) or
THeadAdjustedAccessPath(PropertyContent head, AccessPath tail) {
adjustSourceHead(_, _, _, tail, head)
or
adjustSinkHead(_, _, _, tail, head)
}
/**
* An access path used in a library-code flow-summary, where the head of the path
* may have been adjusted. For example, in
*
* ```csharp
* var list = new List<string>();
* list.Add("taint");
* var enumerator = list.getEnumerator();
* ```
*
* the step from `list` to `list.getEnumerator()`, which may be modeled as a
* read of a collection element followed by a store into the `Current`
* property, can be strengthened to be a store into the `Current` property
* from `List<T>.Enumerator`, rather than the generic `Current` property
* from `IEnumerator<T>`.
*/
abstract class AdjustedAccessPath extends TAdjustedAccessPath {
/** Gets the head of this access path, if any. */
abstract Content getHead();
/** Gets the tail of this access path, if any. */
abstract AdjustedAccessPath getTail();
/** Gets the length of this access path. */
abstract int length();
/** Gets the access path obtained by dropping the first `i` elements, if any. */
abstract AdjustedAccessPath drop(int i);
/** Holds if this access path contains content `c`. */
predicate contains(Content c) { c = this.drop(_).getHead() }
/** Gets a textual representation of this access path. */
string toString() {
exists(Content head, AdjustedAccessPath tail |
head = this.getHead() and
tail = this.getTail() and
if tail.length() = 0 then result = head.toString() else result = head + ", " + tail
)
or
this.length() = 0 and
result = "<empty>"
}
}
private class OriginalAccessPath extends AdjustedAccessPath, TOriginalAccessPath {
private AccessPath ap;
OriginalAccessPath() { this = TOriginalAccessPath(ap) }
override Content getHead() { result = ap.getHead() }
override AdjustedAccessPath getTail() { result = TOriginalAccessPath(ap.getTail()) }
override int length() { result = ap.length() }
override AdjustedAccessPath drop(int i) { result = TOriginalAccessPath(ap.drop(i)) }
}
private class HeadAdjustedAccessPath extends AdjustedAccessPath, THeadAdjustedAccessPath {
private PropertyContent head;
private AccessPath tail;
HeadAdjustedAccessPath() { this = THeadAdjustedAccessPath(head, tail) }
override Content getHead() { result = head }
override AdjustedAccessPath getTail() { result = TOriginalAccessPath(tail) }
override int length() { result = 1 + tail.length() }
override AdjustedAccessPath drop(int i) {
i = 0 and result = this
or
result = TOriginalAccessPath(tail.drop(i - 1))
}
}
module AdjustedAccessPath {
AdjustedAccessPath empty() { result.length() = 0 }
AdjustedAccessPath singleton(Content c) { result.getHead() = c and result.length() = 1 }
}
pragma[nomagic]
private predicate callableFlow(
CallableFlowSource source, AccessPath sourceAp, boolean adjustSourceAp, CallableFlowSink sink,
AccessPath sinkAp, boolean adjustSinkAp, SourceDeclarationCallable c, boolean preservesValue
) {
any(LibraryTypeDataFlow ltdf).callableFlow(source, sourceAp, sink, sinkAp, c, preservesValue) and
(
if sourceAp.getHead() instanceof PropertyContent
then adjustSourceAp = true
else adjustSourceAp = false
) and
if sinkAp.getHead() instanceof PropertyContent
then adjustSinkAp = true
else adjustSinkAp = false
}
/**
@@ -1524,33 +1651,38 @@ module LibraryFlow {
*/
pragma[nomagic]
predicate libraryFlowSummary(
Call call, CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink,
AccessPath sinkAp, boolean preservesValue
Call call, CallableFlowSource source, AdjustedAccessPath sourceAp, CallableFlowSink sink,
AdjustedAccessPath sinkAp, boolean preservesValue
) {
exists(LibraryTypeDataFlow ltdf, SourceDeclarationCallable c |
c = call.getTarget().getSourceDeclaration()
|
ltdf.callableFlow(source, sink, c, preservesValue) and
sourceAp = AccessPath::empty() and
sinkAp = AccessPath::empty()
exists(SourceDeclarationCallable c | c = call.getTarget().getSourceDeclaration() |
any(LibraryTypeDataFlow ltdf).callableFlow(source, sink, c, preservesValue) and
sourceAp = TOriginalAccessPath(AccessPath::empty()) and
sinkAp = TOriginalAccessPath(AccessPath::empty())
or
exists(AccessPath sourceAp0, AccessPath sinkAp0 |
ltdf.callableFlow(source, sourceAp0, sink, sinkAp0, c, preservesValue) and
exists(
AccessPath sourceAp0, boolean adjustSourceAp, AccessPath sinkAp0, boolean adjustSinkAp
|
callableFlow(source, sourceAp0, adjustSourceAp, sink, sinkAp0, adjustSinkAp, c,
preservesValue) and
(
not sourceAp0 = AccessPath::property(_) and
sourceAp = sourceAp0
adjustSourceAp = false and
sourceAp = TOriginalAccessPath(sourceAp0)
or
exists(Property p |
overridesOrImplementsSourceDecl(p,
getPreciseSourceProperty(call, source, sourceAp0).getSourceDeclaration()) and
sourceAp = AccessPath::property(p)
adjustSourceAp = true and
exists(PropertyContent p, AccessPath sourceApTail |
adjustSourceHead(call, source, sourceAp0, sourceApTail, p) and
sourceAp = THeadAdjustedAccessPath(p, sourceApTail)
)
) and
(
not sinkAp0 = AccessPath::property(_) and
sinkAp = sinkAp0
adjustSinkAp = false and
sinkAp = TOriginalAccessPath(sinkAp0)
or
sinkAp = AccessPath::property(getPreciseSinkProperty(call, sink, sinkAp0))
adjustSinkAp = true and
exists(PropertyContent p, AccessPath sinkApTail |
adjustSinkHead(call, sink, sinkAp0, sinkApTail, p) and
sinkAp = THeadAdjustedAccessPath(p, sinkApTail)
)
)
)
)
@@ -1606,8 +1738,8 @@ module LibraryFlow {
}
newtype TLibraryCodeNodeState =
TLibraryCodeNodeAfterReadState(AccessPath ap) { ap.length() > 0 } or
TLibraryCodeNodeBeforeStoreState(AccessPath ap) { ap.length() > 0 }
TLibraryCodeNodeAfterReadState(AdjustedAccessPath ap) { ap.length() > 0 } or
TLibraryCodeNodeBeforeStoreState(AdjustedAccessPath ap) { ap.length() > 0 }
/**
* A state used to break up (complex) flow summaries for library code into atomic
@@ -1626,12 +1758,12 @@ module LibraryFlow {
*/
class LibraryCodeNodeState extends TLibraryCodeNodeState {
string toString() {
exists(AccessPath ap |
exists(AdjustedAccessPath ap |
this = TLibraryCodeNodeAfterReadState(ap) and
result = "after read: " + ap
)
or
exists(AccessPath ap |
exists(AdjustedAccessPath ap |
this = TLibraryCodeNodeBeforeStoreState(ap) and
result = "before store: " + ap
)
@@ -1639,12 +1771,12 @@ module LibraryFlow {
/** Holds if this state represents the state after the last read. */
predicate isLastReadState() {
this = TLibraryCodeNodeAfterReadState(any(AccessPath ap | ap.length() = 1))
this = TLibraryCodeNodeAfterReadState(AdjustedAccessPath::singleton(_))
}
/** Holds if this state represents the state before the first store. */
predicate isFirstStoreState() {
this = TLibraryCodeNodeBeforeStoreState(any(AccessPath ap | ap.length() = 1))
this = TLibraryCodeNodeBeforeStoreState(AdjustedAccessPath::singleton(_))
}
}
@@ -1726,21 +1858,21 @@ module LibraryFlow {
*/
predicate localStepLibrary(Node pred, Node succ, boolean preservesValue) {
exists(
ControlFlow::Node callCfn, CallableFlowSource source, AccessPath sourceAp,
CallableFlowSink sink, AccessPath sinkAp
ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp,
CallableFlowSink sink, AdjustedAccessPath sinkAp
|
libraryFlowSummary(callCfn.getElement(), source, sourceAp, sink, sinkAp, preservesValue)
|
// Simple flow summary without reads or stores
sourceAp = AccessPath::empty() and
sinkAp = AccessPath::empty() and
sourceAp = AdjustedAccessPath::empty() and
sinkAp = AdjustedAccessPath::empty() and
entry(pred, callCfn, source) and
exit(succ, callCfn, sink)
or
// Entry step for a complex summary with no reads and (1) multiple stores, or
// (2) at least one store and non-value-preservation
exists(LibraryCodeNodeState succState |
sourceAp.length() = 0 and
sourceAp = AdjustedAccessPath::empty() and
entry(pred, callCfn, source) and
succState.isFirstStoreState() and
succ = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, succState)
@@ -1749,7 +1881,7 @@ module LibraryFlow {
// Exit step for a complex summary with no stores and (1) multiple reads, or
// (2) at least one read and non-value-preservation
exists(LibraryCodeNodeState predState |
sinkAp.length() = 0 and
sinkAp = AdjustedAccessPath::empty() and
predState.isLastReadState() and
pred = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, predState) and
exit(succ, callCfn, sink)
@@ -1758,8 +1890,8 @@ module LibraryFlow {
or
// Internal step for complex flow summaries with both reads and writes
exists(
ControlFlow::Node callCfn, CallableFlowSource source, AccessPath sourceAp,
CallableFlowSink sink, AccessPath sinkAp, LibraryCodeNodeState predState,
ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp,
CallableFlowSink sink, AdjustedAccessPath sinkAp, LibraryCodeNodeState predState,
LibraryCodeNodeState succState
|
predState.isLastReadState() and
@@ -1775,8 +1907,8 @@ module LibraryFlow {
*/
predicate setterLibrary(Node pred, Content c, Node succ, boolean preservesValue) {
exists(ControlFlow::Node callCfn, CallableFlowSource source, CallableFlowSink sink |
libraryFlowSummary(callCfn.getElement(), source, AccessPath::empty(), sink,
AccessPath::singleton(c), preservesValue)
libraryFlowSummary(callCfn.getElement(), source, AdjustedAccessPath::empty(), sink,
AdjustedAccessPath::singleton(c), preservesValue)
|
entry(pred, callCfn, source) and
exit(succ, callCfn, sink)
@@ -1790,9 +1922,9 @@ module LibraryFlow {
predicate storeStepLibrary(Node pred, Content c, Node succ) {
// Complex flow summary
exists(
ControlFlow::Node callCfn, CallableFlowSource source, AccessPath sourceAp,
CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue,
LibraryCodeNodeState predState, AccessPath ap
ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp,
CallableFlowSink sink, AdjustedAccessPath sinkAp, boolean preservesValue,
LibraryCodeNodeState predState, AdjustedAccessPath ap
|
predState = TLibraryCodeNodeBeforeStoreState(ap) and
pred = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, predState) and
@@ -1800,7 +1932,8 @@ module LibraryFlow {
|
// More stores needed
exists(LibraryCodeNodeState succState |
succState = TLibraryCodeNodeBeforeStoreState(any(AccessPath succAp | succAp.getTail() = ap)) and
succState =
TLibraryCodeNodeBeforeStoreState(any(AdjustedAccessPath succAp | succAp.getTail() = ap)) and
succ = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, succState)
)
or
@@ -1819,8 +1952,8 @@ module LibraryFlow {
*/
predicate getterLibrary(Node pred, Content c, Node succ, boolean preservesValue) {
exists(ControlFlow::Node callCfn, CallableFlowSource source, CallableFlowSink sink |
libraryFlowSummary(callCfn.getElement(), source, AccessPath::singleton(c), sink,
AccessPath::empty(), preservesValue) and
libraryFlowSummary(callCfn.getElement(), source, AdjustedAccessPath::singleton(c), sink,
AdjustedAccessPath::empty(), preservesValue) and
entry(pred, callCfn, source) and
exit(succ, callCfn, sink)
)
@@ -1833,9 +1966,9 @@ module LibraryFlow {
predicate readStepLibrary(Node pred, Content c, Node succ) {
// Complex flow summary
exists(
ControlFlow::Node callCfn, CallableFlowSource source, AccessPath sourceAp,
CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue,
LibraryCodeNodeState succState, AccessPath ap
ControlFlow::Node callCfn, CallableFlowSource source, AdjustedAccessPath sourceAp,
CallableFlowSink sink, AdjustedAccessPath sinkAp, boolean preservesValue,
LibraryCodeNodeState succState, AdjustedAccessPath ap
|
succState = TLibraryCodeNodeAfterReadState(ap) and
succ = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, succState) and
@@ -1846,7 +1979,7 @@ module LibraryFlow {
entry(pred, callCfn, source)
or
// Subsequent reads
exists(LibraryCodeNodeState predState, AccessPath predAp |
exists(LibraryCodeNodeState predState, AdjustedAccessPath predAp |
predState = TLibraryCodeNodeAfterReadState(predAp) and
predAp.getTail() = ap and
pred = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue, predState)
@@ -1886,9 +2019,9 @@ private DataFlowType getContentType(Content c) {
class LibraryCodeNode extends NodeImpl, TLibraryCodeNode {
private ControlFlow::Node callCfn;
private CallableFlowSource source;
private AccessPath sourceAp;
private LibraryFlow::AdjustedAccessPath sourceAp;
private CallableFlowSink sink;
private AccessPath sinkAp;
private LibraryFlow::AdjustedAccessPath sinkAp;
private boolean preservesValue;
private LibraryFlow::LibraryCodeNodeState state;
@@ -1899,7 +2032,7 @@ class LibraryCodeNode extends NodeImpl, TLibraryCodeNode {
override Callable getEnclosingCallableImpl() { result = callCfn.getEnclosingCallable() }
override DataFlowType getDataFlowType() {
exists(AccessPath ap |
exists(LibraryFlow::AdjustedAccessPath ap |
state = LibraryFlow::TLibraryCodeNodeAfterReadState(ap) and
if sinkAp.length() = 0 and state.isLastReadState() and preservesValue = true
then result = Gvn::getGlobalValueNumber(sink.getSinkType(callCfn.getElement()))

View File

@@ -0,0 +1,2 @@
| test0.cs:13:18:13:43 | DerivesFromDeprecatedType1 | Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. |
| test0.cs:59:18:59:38 | AttributeSerializer01 | Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. |

View File

@@ -0,0 +1 @@
experimental/Security Features/Serialization/DefiningDatasetRelatedType.ql

View File

@@ -0,0 +1,2 @@
| test0.cs:15:24:15:32 | MyDataSet | Defining an serializable class $@ that has member $@ of a type that is derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. | test0.cs:13:18:13:43 | DerivesFromDeprecatedType1 | DerivesFromDeprecatedType1 | test0.cs:15:24:15:32 | MyDataSet | MyDataSet |
| test0.cs:61:25:61:33 | MyDataSet | Defining an serializable class $@ that has member $@ of a type that is derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. | test0.cs:59:18:59:38 | AttributeSerializer01 | AttributeSerializer01 | test0.cs:61:25:61:33 | MyDataSet | MyDataSet |

View File

@@ -0,0 +1 @@
experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.ql

View File

@@ -0,0 +1,2 @@
| test0.cs:95:49:95:63 | typeof(...) | Unsafe type is used in data contract serializer. Make sure $@ comes from the trusted source. | test0.cs:95:49:95:63 | typeof(...) | typeof(...) |
| test0.cs:96:49:96:77 | typeof(...) | Unsafe type is used in data contract serializer. Make sure $@ comes from the trusted source. | test0.cs:96:49:96:77 | typeof(...) | typeof(...) |

View File

@@ -0,0 +1 @@
experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.ql

View File

@@ -0,0 +1 @@
| test0.cs:88:17:88:46 | call to method ReadXmlSchema | Making an XML deserialization call with a type derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. |

View File

@@ -0,0 +1 @@
experimental/Security Features/Serialization/XmlDeserializationWithDataSet.ql

View File

@@ -0,0 +1,101 @@
// semmle-extractor-options: /r:System.Data.Common.dll /r:System.Runtime.WindowsRuntime.dll /r:System.Xml.XmlSerializer.dll /r:System.Runtime.Serialization.Xml.dll /r:System.Runtime.Serialization.Xml.dll /r:System.Collections.dll /r:System.Private.Xml.dll /r:System.Private.DataContractSerialization.dll /r:System.Runtime.Extensions.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Xml.ReaderWriter.dll /r:System.IO.FileSystem.dll
using System;
using System.Data;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Xml;
using System.Collections.Generic;
namespace DataSetSerializationTest
{
public class DerivesFromDeprecatedType1 : XmlSerializer // warning:DefiningDatasetRelatedType.ql
{
public DataSet MyDataSet { get; set; } // bug:DefiningPotentiallyUnsafeXmlSerializer.ql
public DerivesFromDeprecatedType1()
{
}
}
/*
* TODO: I cannot use DataContract on a QL unit test
*
[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
public class PatternDataContractSerializer : XmlObjectSerializer
{
[DataMember()]
public DataSet MyDataSet { get; set; }
[DataMember()]
public DataTable MyDataTable { get; set; }
PatternDataContractSerializer() { }
private ExtensionDataObject extensionData_Value;
public ExtensionDataObject ExtensionData
{
get
{
return extensionData_Value;
}
set
{
extensionData_Value = value;
}
}
public override void WriteObject(System.IO.Stream stream, object graph) { }
public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object graph) { }
public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { return false; }
public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object graph) { }
public override void WriteEndObject(System.Xml.XmlWriter writer) { }
public override void WriteEndObject(XmlDictionaryWriter writer) { }
public override object ReadObject(System.IO.Stream stream) { return null; }
public override object ReadObject(XmlDictionaryReader reader, bool b) { return null; }
}
*/
[Serializable()]
public class AttributeSerializer01 // warning:DefiningDatasetRelatedType.ql
{
private DataSet MyDataSet; // bug:DefiningPotentiallyUnsafeXmlSerializer.ql
AttributeSerializer01()
{
}
}
class Program
{
static string GetSerializedDataSet(DataSet dataSet)
{
DataTable dataTable = new DataTable("MyTable");
dataTable.Columns.Add("FirstName", typeof(string));
dataTable.Columns.Add("LastName", typeof(string));
dataTable.Columns.Add("Age", typeof(int));
StringWriter writer = new StringWriter();
dataSet.WriteXml(writer, XmlWriteMode.DiffGram);
return writer.ToString();
}
static void datatable_readxmlschema_01(string fileName)
{
using (FileStream fs = File.OpenRead(fileName))
{
DataTable newTable = new DataTable();
System.Xml.XmlTextReader reader = new System.Xml.XmlTextReader(fs);
newTable.ReadXmlSchema(reader); //bug:XmlDeserializationWithDataSet.ql
}
}
static void Main(string[] args)
{
XmlSerializer x = new XmlSerializer(typeof(DataSet)); // bug:UnsafeTypeUsedDataContractSerializer.ql
XmlSerializer y = new XmlSerializer(typeof(AttributeSerializer01)); //bug:UnsafeTypeUsedDataContractSerializer.ql
Console.WriteLine("Hello World!");
}
}
}

View File

@@ -8,7 +8,10 @@ alwaysNull
| dataflow.cs:80:21:80:44 | access to property NullProperty |
| dataflow.cs:89:31:89:44 | call to method NullFunction |
alwaysNotNull
| dataflow.cs:71:13:71:20 | access to local variable nonNull1 |
| dataflow.cs:71:13:71:35 | Int32 nonNull1 = ... |
| dataflow.cs:71:24:71:35 | default(...) |
| dataflow.cs:71:32:71:34 | access to type Int32 |
| dataflow.cs:72:27:72:30 | this access |
| dataflow.cs:72:27:72:40 | call to method GetType |
| dataflow.cs:73:30:73:33 | true |
@@ -25,6 +28,7 @@ alwaysNotNull
| dataflow.cs:85:24:85:30 | access to local variable nonNull |
| dataflow.cs:85:24:85:55 | call to method ReturnsNonNullIndirect |
| dataflow.cs:86:24:86:30 | access to local variable nonNull |
| dataflow.cs:89:24:89:27 | access to field cond |
| dataflow.cs:89:24:89:27 | this access |
| dataflow.cs:89:31:89:44 | this access |
| dataflow.cs:89:48:89:51 | this access |

View File

@@ -385,6 +385,32 @@ public class E
return true;
return e1.Long == e2.Long; // GOOD (false positive)
}
int Ex38(int? i)
{
i ??= 0;
return i.Value; // GOOD
}
System.Drawing.Color Ex39(System.Drawing.Color? color)
{
color ??= System.Drawing.Color.White;
return color.Value; // GOOD
}
int Ex40()
{
int? i = null;
i ??= null;
return i.Value; // BAD (always)
}
int Ex41()
{
int? i = 1;
i ??= null;
return i.Value; // GOOD
}
}
public static class Extensions
@@ -393,4 +419,4 @@ public static class Extensions
public static int M2(this string s) => s.Length;
}
// semmle-extractor-options: /r:System.Linq.dll
// semmle-extractor-options: /r:System.Linq.dll /r:System.Drawing.Primitives.dll

View File

@@ -1115,12 +1115,14 @@
| E.cs:176:13:176:14 | access to local variable b2 | true | E.cs:175:19:175:42 | ... ? ... : ... | true |
| E.cs:176:13:176:22 | ... == ... | false | E.cs:176:13:176:14 | (...) ... | non-null |
| E.cs:176:13:176:22 | ... == ... | true | E.cs:176:13:176:14 | (...) ... | null |
| E.cs:176:13:176:22 | ... == ... | true | E.cs:176:19:176:22 | null | non-null |
| E.cs:180:13:180:23 | ... == ... | false | E.cs:180:13:180:15 | access to parameter obj | non-null |
| E.cs:180:13:180:23 | ... == ... | true | E.cs:180:13:180:15 | access to parameter obj | null |
| E.cs:184:13:184:14 | (...) ... | non-null | E.cs:184:13:184:14 | access to parameter b1 | non-null |
| E.cs:184:13:184:14 | (...) ... | null | E.cs:184:13:184:14 | access to parameter b1 | null |
| E.cs:184:13:184:22 | ... == ... | false | E.cs:184:13:184:14 | (...) ... | non-null |
| E.cs:184:13:184:22 | ... == ... | true | E.cs:184:13:184:14 | (...) ... | null |
| E.cs:184:13:184:22 | ... == ... | true | E.cs:184:19:184:22 | null | non-null |
| E.cs:193:19:193:29 | call to method ToString | non-null | E.cs:193:17:193:17 | access to parameter o | non-null |
| E.cs:198:17:198:29 | ... ? ... : ... | non-null | E.cs:198:17:198:17 | access to parameter b | false |
| E.cs:198:17:198:29 | ... ? ... : ... | non-null | E.cs:198:28:198:29 | "" | non-null |
@@ -1256,6 +1258,26 @@
| E.cs:384:13:384:36 | ... && ... | true | E.cs:384:27:384:36 | ... == ... | true |
| E.cs:384:27:384:36 | ... == ... | false | E.cs:384:27:384:28 | access to parameter e2 | non-null |
| E.cs:384:27:384:36 | ... == ... | true | E.cs:384:27:384:28 | access to parameter e2 | null |
| E.cs:404:9:404:9 | access to local variable i | non-null | E.cs:403:18:403:21 | null | non-null |
| E.cs:404:9:404:9 | access to local variable i | null | E.cs:403:18:403:21 | null | null |
| E.cs:404:9:404:18 | ... = ... | non-null | E.cs:404:9:404:9 | access to local variable i | non-null |
| E.cs:404:9:404:18 | ... = ... | non-null | E.cs:404:9:404:18 | ... ?? ... | non-null |
| E.cs:404:9:404:18 | ... = ... | null | E.cs:404:9:404:9 | access to local variable i | null |
| E.cs:404:9:404:18 | ... = ... | null | E.cs:404:9:404:18 | ... ?? ... | null |
| E.cs:404:9:404:18 | ... ?? ... | null | E.cs:404:9:404:9 | access to local variable i | null |
| E.cs:404:9:404:18 | ... ?? ... | null | E.cs:404:15:404:18 | null | null |
| E.cs:405:16:405:16 | access to local variable i | non-null | E.cs:404:9:404:18 | ... ?? ... | non-null |
| E.cs:405:16:405:16 | access to local variable i | null | E.cs:404:9:404:18 | ... ?? ... | null |
| E.cs:411:9:411:9 | access to local variable i | non-null | E.cs:410:18:410:18 | (...) ... | non-null |
| E.cs:411:9:411:9 | access to local variable i | null | E.cs:410:18:410:18 | (...) ... | null |
| E.cs:411:9:411:18 | ... = ... | non-null | E.cs:411:9:411:9 | access to local variable i | non-null |
| E.cs:411:9:411:18 | ... = ... | non-null | E.cs:411:9:411:18 | ... ?? ... | non-null |
| E.cs:411:9:411:18 | ... = ... | null | E.cs:411:9:411:9 | access to local variable i | null |
| E.cs:411:9:411:18 | ... = ... | null | E.cs:411:9:411:18 | ... ?? ... | null |
| E.cs:411:9:411:18 | ... ?? ... | null | E.cs:411:9:411:9 | access to local variable i | null |
| E.cs:411:9:411:18 | ... ?? ... | null | E.cs:411:15:411:18 | null | null |
| E.cs:412:16:412:16 | access to local variable i | non-null | E.cs:411:9:411:18 | ... ?? ... | non-null |
| E.cs:412:16:412:16 | access to local variable i | null | E.cs:411:9:411:18 | ... ?? ... | null |
| Forwarding.cs:9:13:9:30 | !... | false | Forwarding.cs:9:14:9:30 | call to method IsNullOrEmpty | true |
| Forwarding.cs:9:13:9:30 | !... | true | Forwarding.cs:9:14:9:30 | call to method IsNullOrEmpty | false |
| Forwarding.cs:9:14:9:14 | access to local variable s | empty | Forwarding.cs:7:20:7:23 | null | empty |

View File

@@ -36,6 +36,7 @@
| E.cs:323:13:323:14 | access to parameter s1 | Variable $@ is always null here. | E.cs:319:29:319:30 | s1 | s1 |
| E.cs:324:13:324:14 | access to parameter s2 | Variable $@ is always null here. | E.cs:319:40:319:41 | s2 | s2 |
| E.cs:331:9:331:9 | access to local variable x | Variable $@ is always null here. | E.cs:330:13:330:13 | x | x |
| E.cs:405:16:405:16 | access to local variable i | Variable $@ is always null here. | E.cs:403:14:403:14 | i | i |
| Forwarding.cs:36:31:36:31 | access to local variable s | Variable $@ is always null here. | Forwarding.cs:7:16:7:16 | s | s |
| Forwarding.cs:40:27:40:27 | access to local variable s | Variable $@ is always null here. | Forwarding.cs:7:16:7:16 | s | s |
| NullAlwaysBad.cs:9:30:9:30 | access to parameter s | Variable $@ is always null here. | NullAlwaysBad.cs:7:29:7:29 | s | s |

View File

@@ -219,10 +219,12 @@
| E.cs:175:19:175:29 | ... == ... | E.cs:175:19:175:21 | access to parameter obj | true | true |
| E.cs:176:13:176:22 | ... == ... | E.cs:176:13:176:14 | (...) ... | false | false |
| E.cs:176:13:176:22 | ... == ... | E.cs:176:13:176:14 | (...) ... | true | true |
| E.cs:176:13:176:22 | ... == ... | E.cs:176:19:176:22 | null | true | false |
| E.cs:180:13:180:23 | ... == ... | E.cs:180:13:180:15 | access to parameter obj | false | false |
| E.cs:180:13:180:23 | ... == ... | E.cs:180:13:180:15 | access to parameter obj | true | true |
| E.cs:184:13:184:22 | ... == ... | E.cs:184:13:184:14 | (...) ... | false | false |
| E.cs:184:13:184:22 | ... == ... | E.cs:184:13:184:14 | (...) ... | true | true |
| E.cs:184:13:184:22 | ... == ... | E.cs:184:19:184:22 | null | true | false |
| E.cs:193:17:193:17 | access to parameter o | E.cs:193:17:193:17 | access to parameter o | non-null | false |
| E.cs:193:17:193:17 | access to parameter o | E.cs:193:17:193:17 | access to parameter o | null | true |
| E.cs:208:13:208:23 | ... is ... | E.cs:208:13:208:13 | access to parameter s | false | true |
@@ -276,6 +278,14 @@
| E.cs:384:13:384:22 | ... == ... | E.cs:384:13:384:14 | access to parameter e1 | true | true |
| E.cs:384:27:384:36 | ... == ... | E.cs:384:27:384:28 | access to parameter e2 | false | false |
| E.cs:384:27:384:36 | ... == ... | E.cs:384:27:384:28 | access to parameter e2 | true | true |
| E.cs:391:9:391:9 | access to parameter i | E.cs:391:9:391:9 | access to parameter i | non-null | false |
| E.cs:391:9:391:9 | access to parameter i | E.cs:391:9:391:9 | access to parameter i | null | true |
| E.cs:397:9:397:13 | access to parameter color | E.cs:397:9:397:13 | access to parameter color | non-null | false |
| E.cs:397:9:397:13 | access to parameter color | E.cs:397:9:397:13 | access to parameter color | null | true |
| E.cs:404:9:404:9 | access to local variable i | E.cs:404:9:404:9 | access to local variable i | non-null | false |
| E.cs:404:9:404:9 | access to local variable i | E.cs:404:9:404:9 | access to local variable i | null | true |
| E.cs:411:9:411:9 | access to local variable i | E.cs:411:9:411:9 | access to local variable i | non-null | false |
| E.cs:411:9:411:9 | access to local variable i | E.cs:411:9:411:9 | access to local variable i | null | true |
| Forwarding.cs:9:14:9:30 | call to method IsNullOrEmpty | Forwarding.cs:9:14:9:14 | access to local variable s | false | false |
| Forwarding.cs:14:13:14:32 | call to method IsNotNullOrEmpty | Forwarding.cs:14:13:14:13 | access to local variable s | true | false |
| Forwarding.cs:19:14:19:23 | call to method IsNull | Forwarding.cs:19:14:19:14 | access to local variable s | false | false |

View File

@@ -370,6 +370,9 @@ nodes
| E.cs:384:27:384:28 | access to parameter e2 |
| E.cs:386:16:386:17 | access to parameter e1 |
| E.cs:386:27:386:28 | access to parameter e2 |
| E.cs:404:9:404:18 | SSA def(i) |
| E.cs:404:9:404:18 | SSA def(i) |
| E.cs:405:16:405:16 | access to local variable i |
| Forwarding.cs:7:16:7:23 | SSA def(s) |
| Forwarding.cs:14:9:17:9 | if (...) ... |
| Forwarding.cs:19:9:22:9 | if (...) ... |
@@ -719,6 +722,8 @@ edges
| E.cs:384:9:385:24 | if (...) ... | E.cs:384:27:384:28 | access to parameter e2 |
| E.cs:384:9:385:24 | if (...) ... | E.cs:386:27:386:28 | access to parameter e2 |
| E.cs:384:27:384:28 | access to parameter e2 | E.cs:386:16:386:17 | access to parameter e1 |
| E.cs:404:9:404:18 | SSA def(i) | E.cs:405:16:405:16 | access to local variable i |
| E.cs:404:9:404:18 | SSA def(i) | E.cs:405:16:405:16 | access to local variable i |
| Forwarding.cs:7:16:7:23 | SSA def(s) | Forwarding.cs:14:9:17:9 | if (...) ... |
| Forwarding.cs:14:9:17:9 | if (...) ... | Forwarding.cs:19:9:22:9 | if (...) ... |
| Forwarding.cs:19:9:22:9 | if (...) ... | Forwarding.cs:24:9:27:9 | if (...) ... |

View File

@@ -0,0 +1,9 @@
@echo off
SETLOCAL EnableDelayedExpansion
rem The autobuilder is already being traced
set CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING=true
type NUL && "%CODEQL_EXTRACTOR_CSHARP_ROOT%/tools/%CODEQL_PLATFORM%/Semmle.Autobuild.CSharp.exe" || exit /b %ERRORLEVEL%
ENDLOCAL

14
csharp/tools/autobuild.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
set -eu
if [ "$CODEQL_PLATFORM" != "linux64" ] && [ "$CODEQL_PLATFORM" != "osx64" ] ; then
echo "Automatic build detection for $CODEQL_PLATFORM is not implemented."
exit 1
fi
# The autobuilder is already being traced
CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING="true"
export CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING
"$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/$CODEQL_PLATFORM/Semmle.Autobuild.CSharp" || exit $?

View File

@@ -0,0 +1,9 @@
**/mcs.exe:
**/csc.exe:
invoke ${config_dir}/Semmle.Extraction.CSharp.Driver
prepend --compiler
prepend "${compiler}"
prepend --cil
**/mono*:
**/dotnet:
invoke ${config_dir}/extract-csharp.sh

View File

@@ -0,0 +1,16 @@
#!/bin/bash
echo extract-csharp.sh: Called with arguments: "$@"
extractor="$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/$CODEQL_PLATFORM/Semmle.Extraction.CSharp.Driver"
for i in "$@"
do
shift
if [[ `basename -- "$i"` =~ csc.exe|mcs.exe|csc.dll ]]
then
echo extract-csharp.sh: exec $extractor --cil $@
exec "$extractor" --compiler $i --cil $@
fi
done
echo extract-csharp.sh: Not a compiler invocation

View File

@@ -0,0 +1,14 @@
**/mcs.exe:
**/csc.exe:
invoke ${config_dir}/Semmle.Extraction.CSharp.Driver
prepend --compiler
prepend "${compiler}"
prepend --cil
**/mono*:
**/dotnet:
invoke ${config_dir}/extract-csharp.sh
/usr/bin/codesign:
replace yes
invoke /usr/bin/env
prepend /usr/bin/codesign
trace no

View File

@@ -0,0 +1,16 @@
#!/bin/bash
echo extract-csharp.sh: Called with arguments: "$@"
extractor="$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/$CODEQL_PLATFORM/Semmle.Extraction.CSharp.Driver"
for i in "$@"
do
shift
if [[ `basename -- "$i"` =~ csc.exe|mcs.exe|csc.dll ]]
then
echo extract-csharp.sh: exec $extractor --cil $@
exec "$extractor" --compiler $i --cil $@
fi
done
echo extract-csharp.sh: Not a compiler invocation

View File

@@ -0,0 +1,14 @@
@echo off
SETLOCAL EnableDelayedExpansion
type NUL && "%CODEQL_DIST%\codeql" database index-files ^
--include-extension=.config ^
--include-extension=.csproj ^
--include-extension=.props ^
--include-extension=.xml ^
--size-limit 10m ^
--language xml ^
-- ^
"%CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE%"
ENDLOCAL

13
csharp/tools/pre-finalize.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
set -eu
"$CODEQL_DIST/codeql" database index-files \
--include-extension=.config \
--include-extension=.csproj \
--include-extension=.props \
--include-extension=.xml \
--size-limit 10m \
--language xml \
-- \
"$CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE"

View File

@@ -0,0 +1,9 @@
**\fakes*.exe:
**\moles*.exe:
order compiler
trace no
**\csc*.exe:
invoke ${config_dir}\Semmle.Extraction.CSharp.Driver.exe
prepend --compiler
prepend "${compiler}"
prepend --cil

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,145 @@
Basic query for C and C++ code
==============================
Learn to write and run a simple CodeQL query using LGTM.
About the query
---------------
The query we're going to run performs a basic search of the code for ``if`` statements that are redundant, in the sense that they have an empty then branch. For example, code such as:
.. code-block:: cpp
if (error) { }
Running the query
-----------------
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
#. Click the project in the search results.
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **C/C++** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
.. code-block:: ql
import cpp
from IfStmt ifstmt, Block block
where ifstmt.getThen() = block and
block.getNumStmt() = 0
select ifstmt, "This 'if' statement is redundant."
LGTM checks whether your query compiles and, if all is well, the **Run** button changes to green to indicate that you can go ahead and run the query.
#. Click **Run**.
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
.. image:: ../../images/query-progress.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to the expression ``ifstmt`` and is linked to the location in the source code of the project where ``ifstmt`` occurs. The second column is the alert message.
`Example query results <https://lgtm.com/query/4242591143131494898/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``ifstmt`` column to view the ``if`` statement in the code viewer.
The matching ``if`` statement is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code.
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
After the initial ``import`` statement, this simple query comprises three parts that serve similar purposes to the FROM, WHERE, and SELECT parts of an SQL query.
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| Query part | Purpose | Details |
+===============================================================+===================================================================================================================+========================================================================================================================+
| ``import cpp`` | Imports the standard CodeQL libraries for C/C++. | Every query begins with one or more ``import`` statements. |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``from IfStmt ifstmt, Block block`` | Defines the variables for the query. | We use: |
| | Declarations are of the form: | |
| | ``<type> <variable name>`` | - an ``IfStmt`` variable for ``if`` statements |
| | | - a ``Block`` variable for the statement block |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``where ifstmt.getThen() = block and block.getNumStmt() = 0`` | Defines a condition on the variables. | ``ifstmt.getThen() = block`` relates the two variables. The block must be the ``then`` branch of the ``if`` statement. |
| | | |
| | | ``block.getNumStmt() = 0`` states that the block must be empty (that is, it contains no statements). |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``select ifstmt, "This 'if' statement is redundant."`` | Defines what to report for each match. | Reports the resulting ``if`` statement with a string that explains the problem. |
| | | |
| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | |
| | ``select <program element>, "<alert message>"`` | |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
Extend the query
----------------
Query writing is an inherently iterative process. You write a simple query and then, when you run it, you discover examples that you had not previously considered, or opportunities for improvement.
Remove false positive results
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Browsing the results of our basic query shows that it could be improved. Among the results you are likely to find examples of ``if`` statements with an ``else`` branch, where an empty ``then`` branch does serve a purpose. For example:
.. code-block:: cpp
if (...) {
...
} else if (!strcmp(option, "-verbose") {
// nothing to do - handled earlier
} else {
error("unrecognized option");
}
In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to ignore empty ``then`` branches if the ``if`` statement has an ``else`` branch.
To exclude ``if`` statements that have an ``else`` branch:
#. Extend the ``where`` clause to include the following extra condition:
.. code-block:: ql
and not ifstmt.hasElse()
The ``where`` clause is now:
.. code-block:: ql
where ifstmt.getThen() = block and
block.getNumStmt() = 0 and
not ifstmt.hasElse()
#. Click **Run**.
There are now fewer results because ``if`` statements with an ``else`` branch are no longer reported.
`See this in the query console <https://lgtm.com/query/1899933116489579248/>`__
Further reading
---------------
.. include:: ../../reusables/cpp-further-reading.rst
.. include:: ../../reusables/codeql-ref-tools-further-reading.rst

View File

@@ -6,6 +6,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
.. toctree::
:hidden:
basic-query-cpp
introduce-libraries-cpp
function-classes
expressions-types
@@ -18,7 +19,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
value-numbering-hash-cons
- `Basic C/C++ query <https://lgtm.com/help/lgtm/console/ql-cpp-basic-example>`__: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for C and C++ code <basic-query-cpp>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`CodeQL library for C and C++ <introduce-libraries-cpp>`: When analyzing C or C++ code, you can use the large collection of classes in the CodeQL library for C and C++.

View File

@@ -0,0 +1,150 @@
Basic query for C# code
=======================
Learn to write and run a simple CodeQL query using LGTM.
About the query
---------------
The query we're going to run performs a basic search of the code for ``if`` statements that are redundant, in the sense that they have an empty then branch. For example, code such as:
.. code-block:: csharp
if (error) { }
Running the query
-----------------
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
#. Click the project in the search results.
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **C#** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
.. code-block:: ql
import csharp
from IfStmt ifstmt, BlockStmt block
where ifstmt.getThen() = block and
block.isEmpty()
select ifstmt, "This 'if' statement is redundant."
LGTM checks whether your query compiles and, if all is well, the **Run** button changes to green to indicate that you can go ahead and run the query.
#. Click **Run**.
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
.. image:: ../../images/query-progress.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to the expression ``ifstmt`` and is linked to the location in the source code of the project where ``ifstmt`` occurs. The second column is the alert message.
`Example query results <https://lgtm.com/query/1214010107827821393/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``ifstmt`` column to view the ``if`` statement in the code viewer.
The matching ``if`` statement is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code.
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
After the initial ``import`` statement, this simple query comprises three parts that serve similar purposes to the FROM, WHERE, and SELECT parts of an SQL query.
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| Query part | Purpose | Details |
+===============================================================+===================================================================================================================+========================================================================================================================+
| ``import csharp`` | Imports the standard CodeQL libraries for C#. | Every query begins with one or more ``import`` statements. |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``from IfStmt ifstmt, BlockStmt block`` | Defines the variables for the query. | We use: |
| | Declarations are of the form: | |
| | ``<type> <variable name>`` | - an ``IfStmt`` variable for ``if`` statements |
| | | - a ``BlockStmt`` variable for the then block |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``where ifstmt.getThen() = block and block.isEmpty()`` | Defines a condition on the variables. | ``ifstmt.getThen() = block`` relates the two variables. The block must be the ``then`` branch of the ``if`` statement. |
| | | |
| | | ``block.isEmpty()`` states that the block must be empty (that is, it contains no statements). |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``select ifstmt, "This 'if' statement is redundant."`` | Defines what to report for each match. | Reports the resulting ``if`` statement with a string that explains the problem. |
| | | |
| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | |
| | ``select <program element>, "<alert message>"`` | |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
Extend the query
----------------
Query writing is an inherently iterative process. You write a simple query and then, when you run it, you discover examples that you had not previously considered, or opportunities for improvement.
Remove false positive results
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Browsing the results of our basic query shows that it could be improved. Among the results you are likely to find examples of ``if`` statements with an ``else`` branch, where an empty ``then`` branch does serve a purpose. For example:
.. code-block:: csharp
if (...)
{
...
}
else if (option == "-verbose")
{
// nothing to do - handled earlier
}
else
{
error("unrecognized option");
}
In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to ignore empty ``then`` branches if the ``if`` statement has an ``else`` branch.
To exclude ``if`` statements that have an ``else`` branch:
#. Add the following to the where clause:
.. code-block:: ql
and not exists(ifstmt.getElse())
The ``where`` clause is now:
.. code-block:: ql
where ifstmt.getThen() = block and
block.isEmpty() and
not exists(ifstmt.getElse())
#. Click **Run**.
There are now fewer results because ``if`` statements with an ``else`` branch are no longer included.
`See this in the query console <https://lgtm.com/query/6233102733683510530/>`__
Further reading
---------------
.. include:: ../../reusables/csharp-further-reading.rst
.. include:: ../../reusables/codeql-ref-tools-further-reading.rst

View File

@@ -6,10 +6,11 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
.. toctree::
:hidden:
basic-query-csharp
introduce-libraries-csharp
dataflow
- `Basic C# query <https://lgtm.com/help/lgtm/console/ql-csharp-basic-example>`__: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for C# code <basic-query-csharp>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`CodeQL library for C# <introduce-libraries-csharp>`: When you're analyzing a C# program, you can make use of the large collection of classes in the CodeQL library for C#.

View File

@@ -0,0 +1,151 @@
Basic query for Go code
=======================
Learn to write and run a simple CodeQL query using LGTM.
About the query
---------------
The query we're going to run searches the code for methods defined on value types that modify their receiver by writing a field:
.. code-block:: go
func (s MyStruct) valueMethod() { s.f = 1 } // method on value
This is problematic because the receiver argument is passed by value, not by reference. Consequently, valueMethod is called with a copy of the receiver object, so any changes it makes to the receiver will be invisible to the caller. To prevent this, the method should be defined on a pointer instead:
.. code-block:: go
func (s *MyStruct) pointerMethod() { s.f = 1 } // method on pointer
For further information on using methods on values or pointers in Go, see the `Go FAQ <https://golang.org/doc/faq#methods_on_values_or_pointers>`__.
Running the query
-----------------
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
#. Click the project in the search results.
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **Go** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
.. code-block:: ql
import go
from Method m, Variable recv, Write w, Field f
where
recv = m.getReceiver() and
w.writesField(recv.getARead(), f, _) and
not recv.getType() instanceof PointerType
select w, "This update to " + f + " has no effect, because " + recv + " is not a pointer."
LGTM checks whether your query compiles and, if all is well, the **Run** button changes to green to indicate that you can go ahead and run the query.
#. Click **Run**.
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
.. image:: ../../images/query-progress.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to ``w``, which is the location in the source code where the receiver ``recv`` is modified. The second column is the alert message.
`Example query results <https://lgtm.com/query/6221190009056970603/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``w`` column to view it in the code viewer.
The matching ``w`` is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code.
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
After the initial ``import`` statement, this simple query comprises three parts that serve similar purposes to the FROM, WHERE, and SELECT parts of an SQL query.
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| Query part | Purpose | Details |
+===============================================================+===================================================================================================================+======================================================================================================================================+
| ``import go`` | Imports the standard CodeQL libraries for Go. | Every query begins with one or more ``import`` statements. |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| ``from Method m, Variable recv, Write w, Field f`` | Defines the variables for the query. | We declare: |
| | Declarations are of the form: | |
| | ``<type> <variable name>`` | - ``m`` as a variable for all methods |
| | | - a ``recv`` variable, which is the receiver of ``m`` |
| | | - ``w`` as the location in the code where the receiver is modified |
| | | - ``f`` as the field that is written when ``m`` is called |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| ``where recv = m.getReceiver() and | Defines a condition on the variables. | ``recv = m.getReceiver()`` states that ``recv`` must be the receiver variable of ``m``. |
| w.writesField(recv.getARead(), f, _) and | | |
| not recv.getType() instanceof PointerType`` | | ``w.writesField(recv.getARead(), f, _)`` states that ``w`` must be a location in the code where field ``f`` of ``recv`` is modified. |
| | | We use a `'don't-care' expression <https://help.semmle.com/QL/ql-handbook/expressions.html#don-t-care-expressions>`__ _ for the |
| | | value that is written to ``f``—the actual value doesn't matter in this query. |
| | | |
| | | ``not recv.getType() instanceof PointerType`` states that ``m`` is not a pointer method. |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| ``select w, "This update to " + f + | Defines what to report for each match. | Reports ``w`` with a message that explains the potential problem. |
| " has no effect, because " + recv + " is not a pointer."`` | | |
| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | |
| | ``select <program element>, "<alert message>"`` | |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
Extend the query
----------------
Query writing is an inherently iterative process. You write a simple query and then, when you run it, you discover examples that you had not previously considered, or opportunities for improvement.
Remove false positive results
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Among the results generated by the first iteration of this query, you can find cases where a value method is called but the receiver variable is returned. In such cases, the change to the receiver is not invisible to the caller, so a pointer method is not required. These are false positive results and you can improve the query by adding an extra condition to remove them.
To exclude these values:
#. Extend the where clause to include the following extra condition:
.. code-block:: ql
not exists(ReturnStmt ret | ret.getExpr() = recv.getARead().asExpr())
The ``where`` clause is now:
.. code-block:: ql
where e.isPure() and
recv = m.getReceiver() and
w.writesField(recv.getARead(), f, _) and
not recv.getType() instanceof PointerType and
not exists(ReturnStmt ret | ret.getExpr() = recv.getARead().asExpr())
#. Click **Run**.
There are now fewer results because value methods that return their receiver variable are no longer reported.
`See this in the query console <https://lgtm.com/query/9110448975027954322/>`__
Further reading
---------------
.. include:: ../../reusables/go-further-reading.rst
.. include:: ../../reusables/codeql-ref-tools-further-reading.rst

View File

@@ -554,7 +554,7 @@ fact that ``p != nil`` is ``true`` at this point:
|cfg2|
A typical use of this information would be in an analyis that looks for ``nil`` dereferences: such
A typical use of this information would be in an analysis that looks for ``nil`` dereferences: such
an analysis would be able to conclude that the field read ``p.f`` is safe because it is immediately
preceded by a condition guard node guaranteeing that ``p`` is not ``nil``.

View File

@@ -0,0 +1,122 @@
Modeling data flow in Go libraries
==================================
When analyzing a Go program, CodeQL does not examine the source code for
external packages. To track the flow of untrusted data through a library, you
can create a model of the library.
You can find existing models in the ``ql/src/semmle/go/frameworks/`` folder of the
`CodeQL for Go repository <https://github.com/github/codeql-go/tree/main/ql/src/semmle/go/frameworks>`__.
To add a new model, you should make a new file in that folder, named after the library.
Sources
-------
To mark a source of data that is controlled by an untrusted user, we
create a class extending ``UntrustedFlowSource::Range``. Inheritance and
the characteristic predicate of the class should be used to specify
exactly the dataflow node that introduces the data. Here is a short
example from ``Mux.qll``.
.. code-block:: ql
class RequestVars extends DataFlow::UntrustedFlowSource::Range, DataFlow::CallNode {
RequestVars() { this.getTarget().hasQualifiedName("github.com/gorilla/mux", "Vars") }
}
This has the effect that all calls to `the function Vars from the
package mux <http://www.gorillatoolkit.org/pkg/mux#Vars>`__ are
treated as sources of untrusted data.
Flow propagation
----------------
By default, we assume that all functions in libraries do not have
any data flow. To indicate that a particular function does have data flow,
create a class extending ``TaintTracking::FunctionModel`` (or
``DataFlow::FunctionModel`` if the untrusted user data is passed on
without being modified).
Inheritance and the characteristic predicate of the class should specify
the function. The class should also have a member predicate with the signature
``override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp)``
(or
``override predicate hasDataFlow(FunctionInput inp, FunctionOutput outp)``
if extending ``DataFlow::FunctionModel``). The body should constrain
``inp`` and ``outp``.
``FunctionInput`` is an abstract representation of the inputs to a
function. The options are:
* the receiver (``inp.isReceiver()``)
* one of the parameters (``inp.isParameter(i)``)
* one of the results (``inp.isResult(i)``, or ``inp.isResult`` if there is only one result)
Note that it may seem strange that the result of a function could be
considered as a function input, but it is needed in some cases. For
instance, the function ``bufio.NewWriter`` returns a writer ``bw`` that
buffers write operations to an underlying writer ``w``. If tainted data
is written to ``bw``, then it makes sense to propagate that taint back
to the underlying writer ``w``, which can be modeled by saying that
``bufio.NewWriter`` propagates taint from its result to its first
argument.
Similarly, ``FunctionOutput`` is an abstract representation of the
outputs to a function. The options are:
* the receiver (``outp.isReceiver()``)
* one of the parameters (``outp.isParameter(i)``)
* one of the results (``outp.isResult(i)``, or ``outp.isResult`` if there is only one result)
Here is an example from ``Gin.qll``, which has been slightly simplified.
.. code-block:: ql
private class ParamsGet extends TaintTracking::FunctionModel, Method {
ParamsGet() { this.hasQualifiedName("github.com/gin-gonic/gin", "Params", "Get") }
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
inp.isReceiver() and outp.isResult(0)
}
}
This has the effect that calls to the ``Get`` method with receiver type
``Params`` from the ``gin-gonic/gin`` package allow taint to flow from
the receiver to the first result. In other words, if ``p`` has type
``Params`` and taint can flow to it, then after the line
``x := p.Get("foo")`` taint can also flow to ``x``.
Sanitizers
----------
It is not necessary to indicate that library functions are sanitizers.
Their bodies are not analyzed, so it is assumed that data does not
flow through them.
Sinks
-----
Data-flow sinks are specified by queries rather than by library models.
However, you can use library models to indicate when functions belong to
special categories. Queries can then use these categories when specifying
sinks. Classes representing these special categories are contained in
``ql/src/semmle/go/Concepts.qll`` in the `CodeQL for Go repository
<https://github.com/github/codeql-go/blob/main/ql/src/semmle/go/Concepts.qll>`__.
``Concepts.qll`` includes classes for logger mechanisms,
HTTP response writers, HTTP redirects, and marshaling and unmarshaling
functions.
Here is a short example from ``Stdlib.qll``, which has been slightly simplified.
.. code-block:: ql
private class PrintfCall extends LoggerCall::Range, DataFlow::CallNode {
PrintfCall() { this.getTarget().hasQualifiedName("fmt", ["Print", "Printf", "Println"]) }
override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
}
This has the effect that any call to ``Print``, ``Printf``, or
``Println`` in the package ``fmt`` is recognized as a logger call.
Any query that uses logger calls as a sink will then identify when tainted data
has been passed as an argument to ``Print``, ``Printf``, or ``Println``.

View File

@@ -6,11 +6,16 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
.. toctree::
:hidden:
basic-query-go
introduce-libraries-go
ast-class-reference
library-modeling-go
- `Basic Go query <https://lgtm.com/help/lgtm/console/ql-go-basic-example>`__: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for Go code <basic-query-go>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`CodeQL library for Go <introduce-libraries-go>`: When you're analyzing a Go program, you can make use of the large collection of classes in the CodeQL library for Go.
- :doc:`Abstract syntax tree classes for working with Go programs <ast-class-reference>`: CodeQL has a large selection of classes for representing the abstract syntax tree of Go programs.
- :doc:`Modeling data flow in Go libraries <library-modeling-go>`: When analyzing a Go program, CodeQL does not examine the source code for external packages.
To track the flow of untrusted data through a library, you can create a model of the library.

View File

@@ -0,0 +1,145 @@
Basic query for Java code
=========================
Learn to write and run a simple CodeQL query using LGTM.
About the query
---------------
The query we're going to run performs a basic search of the code for ``if`` statements that are redundant, in the sense that they have an empty then branch. For example, code such as:
.. code-block:: java
if (error) { }
Running the query
-----------------
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
#. Click the project in the search results.
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **Java** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
.. code-block:: ql
import java
from IfStmt ifstmt, Block block
where ifstmt.getThen() = block and
block.getNumStmt() = 0
select ifstmt, "This 'if' statement is redundant."
LGTM checks whether your query compiles and, if all is well, the **Run** button changes to green to indicate that you can go ahead and run the query.
#. Click **Run**.
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
.. image:: ../../images/query-progress.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to the expression ``ifstmt`` and is linked to the location in the source code of the project where ``ifstmt`` occurs. The second column is the alert message.
`Example query results <https://lgtm.com/query/3235645104630320782/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``ifstmt`` column to view the ``if`` statement in the code viewer.
The matching ``if`` statement is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code.
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
After the initial ``import`` statement, this simple query comprises three parts that serve similar purposes to the FROM, WHERE, and SELECT parts of an SQL query.
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| Query part | Purpose | Details |
+===============================================================+===================================================================================================================+========================================================================================================================+
| ``import java`` | Imports the standard CodeQL libraries for Java. | Every query begins with one or more ``import`` statements. |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``from IfStmt ifstmt, Block block`` | Defines the variables for the query. | We use: |
| | Declarations are of the form: | |
| | ``<type> <variable name>`` | - an ``IfStmt`` variable for ``if`` statements |
| | | - a ``Block`` variable for the then block |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``where ifstmt.getThen() = block and block.getNumStmt() = 0`` | Defines a condition on the variables. | ``ifstmt.getThen() = block`` relates the two variables. The block must be the ``then`` branch of the ``if`` statement. |
| | | |
| | | ``block.getNumStmt() = 0`` states that the block must be empty (that is, it contains no statements). |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``select ifstmt, "This 'if' statement is redundant."`` | Defines what to report for each match. | Reports the resulting ``if`` statement with a string that explains the problem. |
| | | |
| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | |
| | ``select <program element>, "<alert message>"`` | |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
Extend the query
----------------
Query writing is an inherently iterative process. You write a simple query and then, when you run it, you discover examples that you had not previously considered, or opportunities for improvement.
Remove false positive results
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Browsing the results of our basic query shows that it could be improved. Among the results you are likely to find examples of ``if`` statements with an ``else`` branch, where an empty ``then`` branch does serve a purpose. For example:
.. code-block:: java
if (...) {
...
} else if ("-verbose".equals(option)) {
// nothing to do - handled earlier
} else {
error("unrecognized option");
}
In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to ignore empty ``then`` branches if the ``if`` statement has an ``else`` branch.
To exclude ``if`` statements that have an ``else`` branch:
#. Extend the where clause to include the following extra condition:
.. code-block:: ql
and not exists(ifstmt.getElse())
The ``where`` clause is now:
.. code-block:: ql
where ifstmt.getThen() = block and
block.getNumStmt() = 0 and
not exists(ifstmt.getElse())
#. Click **Run**.
There are now fewer results because ``if`` statements with an ``else`` branch are no longer included.
`See this in the query console <https://lgtm.com/query/6382189874776576029/>`__
Further reading
---------------
.. include:: ../../reusables/java-further-reading.rst
.. include:: ../../reusables/codeql-ref-tools-further-reading.rst

View File

@@ -6,6 +6,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
.. toctree::
:hidden:
basic-query-java
introduce-libraries-java
dataflow
types-class-hierarchy
@@ -16,7 +17,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
source-locations
ast-class-reference
- `Basic Java query <https://lgtm.com/help/lgtm/console/ql-java-basic-example>`__: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for Java code <basic-query-java>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`CodeQL library for Java <introduce-libraries-java>`: When analyzing Java code, you can use the large collection of classes in the CodeQL library for Java.

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