diff --git a/.github/workflows/close-stale.yml b/.github/workflows/close-stale.yml new file mode 100644 index 00000000000..c63a6be690c --- /dev/null +++ b/.github/workflows/close-stale.yml @@ -0,0 +1,30 @@ +name: Mark stale issues + +on: + workflow_dispatch: + schedule: + - cron: "30 1 * * *" + +jobs: + stale: + if: github.repository == 'github/codeql' + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `Stale` label in order to avoid having this issue closed in 7 days.' + close-issue-message: 'This issue was closed because it has been inactive for 7 days.' + days-before-stale: 14 + days-before-close: 7 + only-labels: awaiting-response + + # do not mark PRs as stale + days-before-pr-stale: -1 + days-before-pr-close: -1 + + # Uncomment for dry-run + # debug-only: true + # operations-per-run: 1000 diff --git a/config/identical-files.json b/config/identical-files.json index 6c1c0c7409d..3916e95a0cf 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -56,6 +56,10 @@ "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll", "python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll" ], + "DataFlow Java/C# Flow Summaries": [ + "java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll", + "csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll" + ], "SsaReadPosition Java/C#": [ "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll", "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll" diff --git a/cpp/change-notes/2021-04-13-arithmetic-queries.md b/cpp/change-notes/2021-04-13-arithmetic-queries.md new file mode 100644 index 00000000000..4d0f8833adc --- /dev/null +++ b/cpp/change-notes/2021-04-13-arithmetic-queries.md @@ -0,0 +1,2 @@ +lgtm +* The queries cpp/tainted-arithmetic, cpp/uncontrolled-arithmetic, and cpp/arithmetic-with-extreme-values have been improved to produce fewer false positives. diff --git a/cpp/change-notes/2021-04-21-return-stack-allocated-object.md b/cpp/change-notes/2021-04-21-return-stack-allocated-object.md new file mode 100644 index 00000000000..1876f4cf5f7 --- /dev/null +++ b/cpp/change-notes/2021-04-21-return-stack-allocated-object.md @@ -0,0 +1,2 @@ +codescanning +* The 'Pointer to stack object used as return value' (cpp/return-stack-allocated-object) query has been deprecated, and any uses should be replaced with `Returning stack-allocated memory` (cpp/return-stack-allocated-memory). \ No newline at end of file diff --git a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql index 353e51daa71..e3873e487dd 100644 --- a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql +++ b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql @@ -7,6 +7,8 @@ * @tags reliability * security * external/cwe/cwe-562 + * @deprecated This query is not suitable for production use and has been deprecated. Use + * cpp/return-stack-allocated-memory instead. */ import semmle.code.cpp.pointsto.PointsTo diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql index fc06ed0a500..f5dda53d484 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql @@ -13,6 +13,7 @@ import cpp import semmle.code.cpp.dataflow.EscapesTree +import semmle.code.cpp.models.interfaces.PointerWrapper import semmle.code.cpp.dataflow.DataFlow /** @@ -39,6 +40,10 @@ predicate hasNontrivialConversion(Expr e) { e instanceof ParenthesisExpr ) or + // A smart pointer can be stack-allocated while the data it points to is heap-allocated. + // So we exclude such "conversions" from this predicate. + e = any(PointerWrapper wrapper).getAnUnwrapperFunction().getACallToThisFunction() + or hasNontrivialConversion(e.getConversion()) } diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.c b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.c new file mode 100644 index 00000000000..1f1f10b89cc --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.c @@ -0,0 +1,17 @@ +while(flagsLoop) +{ + ... + if(flagsIf) break; + ... +}while(flagsLoop); // BAD: when exiting through `break`, it is possible to get into an eternal loop. +... +while(flagsLoop) +{ + ... + if(flagsIf) break; + ... +} // GOOD: correct cycle +... +if(intA+intB) return 1; // BAD: possibly no comparison +... +if(intA+intB>intC) return 1; // GOOD: correct comparison diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.qhelp new file mode 100644 index 00000000000..4167ce57d65 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.qhelp @@ -0,0 +1,28 @@ + + + +

In some situations, after code refactoring, parts of the old constructs may remain. They are correctly accepted by the compiler, but can critically affect program execution. For example, if you switch from `do {...} while ();` to `while () {...}` forgetting to remove the old construct completely, you get `while(){...}while();` which may be vulnerable. These code snippets look suspicious and require the developer's attention.

+ + +
+ + +

We recommend that you use more explicit code transformations.

+ +
+ +

The following example demonstrates the erroneous and corrected sections of the code.

+ + +
+ + +
  • + CWE Common Weakness Enumeration: + CWE-691: Insufficient Control Flow Management. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql new file mode 100644 index 00000000000..163305dd039 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql @@ -0,0 +1,119 @@ +/** + * @name Errors After Refactoring + * @description --In some situations, after code refactoring, parts of the old constructs may remain. + * --They are correctly accepted by the compiler, but can critically affect program execution. + * --For example, if you switch from `do {...} while ();` to `while () {...}` with errors, you run the risk of running out of resources. + * --These code snippets look suspicious and require the developer's attention. + * @kind problem + * @id cpp/errors-after-refactoring + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-691 + */ + +import cpp +import semmle.code.cpp.valuenumbering.HashCons +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +/** + * Using `while` directly after the body of another` while`. + */ +class UsingWhileAfterWhile extends WhileStmt { + /** + * Using a loop call after another loop has finished running can result in an eternal loop. + * For example, perhaps as a result of refactoring, the `do ... while ()` loop was incorrectly corrected. + * Even in the case of deliberate use of such an expression, it is better to correct it. + */ + UsingWhileAfterWhile() { + exists(WhileStmt wh1 | + wh1.getStmt().getAChild*().(BreakStmt).(ControlFlowNode).getASuccessor().getASuccessor() = + this and + hashCons(wh1.getCondition()) = hashCons(this.getCondition()) and + this.getStmt() instanceof EmptyStmt + ) + or + exists(ForStmt fr1 | + fr1.getStmt().getAChild*().(BreakStmt).(ControlFlowNode).getASuccessor().getASuccessor() = + this and + hashCons(fr1.getCondition()) = hashCons(this.getCondition()) and + this.getStmt() instanceof EmptyStmt + ) + } +} + +/** + * Using arithmetic in a condition. + */ +class UsingArithmeticInComparison extends BinaryArithmeticOperation { + /** + * Using arithmetic operations in a comparison operation can be dangerous. + * For example, part of the comparison may have been lost as a result of refactoring. + * Even if you deliberately use such an expression, it is better to add an explicit comparison. + */ + UsingArithmeticInComparison() { + this.getParent*() instanceof IfStmt and + not this.getAChild*().isConstant() and + not this.getParent*() instanceof Call and + not this.getParent*() instanceof AssignExpr and + not this.getParent*() instanceof ArrayExpr and + not this.getParent*() instanceof RemExpr and + not this.getParent*() instanceof AssignBitwiseOperation and + not this.getParent*() instanceof AssignArithmeticOperation and + not this.getParent*() instanceof EqualityOperation and + not this.getParent*() instanceof RelationalOperation + } + + /** Holds when the expression is inside the loop body. */ + predicate insideTheLoop() { exists(Loop lp | lp.getStmt().getAChild*() = this.getParent*()) } + + /** Holds when the expression is used in binary operations. */ + predicate workingWithValue() { + this.getParent*() instanceof BinaryBitwiseOperation or + this.getParent*() instanceof NotExpr + } + + /** Holds when the expression contains a pointer. */ + predicate workingWithPointer() { + this.getAChild*().getFullyConverted().getType() instanceof DerivedType + } + + /** Holds when a null comparison expression exists. */ + predicate compareWithZero() { + exists(Expr exp | + exp instanceof ComparisonOperation and + ( + globalValueNumber(exp.getAChild*()) = globalValueNumber(this) or + hashCons(exp.getAChild*()) = hashCons(this) + ) and + ( + exp.(ComparisonOperation).getLeftOperand().getValue() = "0" or + exp.(ComparisonOperation).getRightOperand().getValue() = "0" + ) + ) + } + + /** Holds when a comparison expression exists. */ + predicate compareWithOutZero() { + exists(Expr exp | + exp instanceof ComparisonOperation and + ( + globalValueNumber(exp.getAChild*()) = globalValueNumber(this) or + hashCons(exp.getAChild*()) = hashCons(this) + ) + ) + } +} + +from Expr exp +where + exp instanceof UsingArithmeticInComparison and + not exp.(UsingArithmeticInComparison).workingWithValue() and + not exp.(UsingArithmeticInComparison).workingWithPointer() and + not exp.(UsingArithmeticInComparison).insideTheLoop() and + not exp.(UsingArithmeticInComparison).compareWithZero() and + exp.(UsingArithmeticInComparison).compareWithOutZero() + or + exists(WhileStmt wst | wst instanceof UsingWhileAfterWhile and exp = wst.getCondition()) +select exp, "this expression needs your attention" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.c b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.c new file mode 100644 index 00000000000..27631843118 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.c @@ -0,0 +1,4 @@ +if(len>0 & memset(buf,0,len)) return 1; // BAD: `memset` will be called regardless of the value of the `len` variable. moreover, one cannot be sure that it will happen after verification +... +if(len>0 && memset(buf,0,len)) return 1; // GOOD: `memset` will be called after the `len` variable has been checked. +... diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.qhelp new file mode 100644 index 00000000000..f49d5a4fe78 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.qhelp @@ -0,0 +1,28 @@ + + + +

    Using bitwise operations can be a mistake in some situations. For example, if parameters are evaluated in an expression and the function should be called only upon certain test results. These bitwise operations look suspicious and require developer attention.

    + + +
    + + +

    We recommend that you evaluate the correctness of using the specified bit operations.

    + +
    + +

    The following example demonstrates the erroneous and fixed use of bit and logical operations.

    + + +
    + + +
  • + CWE Common Weakness Enumeration: + CWE-691: Insufficient Control Flow Management. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.ql b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.ql new file mode 100644 index 00000000000..72d7625b517 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.ql @@ -0,0 +1,78 @@ +/** + * @name Errors When Using Bit Operations + * @description Unlike the binary operations `||` and `&&`, there is no sequence point after evaluating an + * operand of a bitwise operation like `|` or `&`. If left-to-right evaluation is expected this may be confusing. + * @kind problem + * @id cpp/errors-when-using-bit-operations + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-691 + */ + +import cpp +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +/** + * Dangerous uses of bit operations. + * For example: `if(intA>0 & intA<10 & charBuf&myFunc(charBuf[intA]))`. + * In this case, the function will be called in any case, and even the sequence of the call is not guaranteed. + */ +class DangerousBitOperations extends BinaryBitwiseOperation { + FunctionCall bfc; + + /** + * The assignment indicates the conscious use of the bit operator. + * Use in comparison, conversion, or return value indicates conscious use of the bit operator. + * The use of shifts and bitwise operations on any element of an expression indicates a conscious use of the bitwise operator. + */ + DangerousBitOperations() { + bfc = this.getRightOperand() and + not this.getParent*() instanceof Assignment and + not this.getParent*() instanceof Initializer and + not this.getParent*() instanceof ReturnStmt and + not this.getParent*() instanceof EqualityOperation and + not this.getParent*() instanceof UnaryLogicalOperation and + not this.getParent*() instanceof BinaryLogicalOperation and + not this.getAChild*() instanceof BitwiseXorExpr and + not this.getAChild*() instanceof LShiftExpr and + not this.getAChild*() instanceof RShiftExpr + } + + /** Holds when part of a bit expression is used in a logical operation. */ + predicate useInLogicalOperations() { + exists(BinaryLogicalOperation blop, Expr exp | + blop.getAChild*() = exp and + exp.(FunctionCall).getTarget() = bfc.getTarget() and + not exp.getParent() instanceof ComparisonOperation and + not exp.getParent() instanceof BinaryBitwiseOperation + ) + } + + /** Holds when part of a bit expression is used as part of another supply. For example, as an argument to another function. */ + predicate useInOtherCalls() { + bfc.hasQualifier() or + bfc.getTarget() instanceof Operator or + exists(FunctionCall fc | fc.getAnArgument().getAChild*() = this) or + bfc.getTarget() instanceof BuiltInFunction + } + + /** Holds when the bit expression contains both arguments and a function call. */ + predicate dangerousArgumentChecking() { + not this.getLeftOperand() instanceof Call and + globalValueNumber(this.getLeftOperand().getAChild*()) = globalValueNumber(bfc.getAnArgument()) + } + + /** Holds when function calls are present in the bit expression. */ + predicate functionCallsInBitsExpression() { + this.getLeftOperand().getAChild*() instanceof FunctionCall + } +} + +from DangerousBitOperations dbo +where + not dbo.useInOtherCalls() and + dbo.useInLogicalOperations() and + (not dbo.functionCallsInBitsExpression() or dbo.dangerousArgumentChecking()) +select dbo, "This bitwise operation appears in a context where a Boolean operation is expected." diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.c b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.c new file mode 100644 index 00000000000..8458d82f7ad --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.c @@ -0,0 +1,11 @@ +if(len=funcReadData()==0) return 1; // BAD: variable `len` will not equal the value returned by function `funcReadData()` +... +if((len=funcReadData())==0) return 1; // GOOD: variable `len` equal the value returned by function `funcReadData()` +... +bool a=true; +a++;// BAD: variable `a` does not change its meaning +bool b; +b=-a;// BAD: variable `b` equal `true` +... +a=false;// GOOD: variable `a` equal `false` +b=!a;// GOOD: variable `b` equal `false` diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.qhelp new file mode 100644 index 00000000000..8114da831fe --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.qhelp @@ -0,0 +1,28 @@ + + + +

    Finding places of confusing use of boolean type. For example, a unary minus does not work before a boolean type and an increment always gives true.

    + + +
    + + +

    we recommend making the code simpler.

    + +
    + +

    The following example demonstrates erroneous and fixed methods for using a boolean data type.

    + + +
    + + +
  • + CERT C Coding Standard: + EXP00-C. Use parentheses for precedence of operation. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql new file mode 100644 index 00000000000..4f30f112eb0 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql @@ -0,0 +1,54 @@ +/** + * @name Operator Precedence Logic Error When Use Bool Type + * @description --Finding places of confusing use of boolean type. + * --For example, a unary minus does not work before a boolean type and an increment always gives true. + * @kind problem + * @id cpp/operator-precedence-logic-error-when-use-bool-type + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-783 + * external/cwe/cwe-480 + */ + +import cpp +import semmle.code.cpp.valuenumbering.HashCons + +/** Holds if `exp` increments a boolean value. */ +predicate incrementBoolType(IncrementOperation exp) { + exp.getOperand().getType() instanceof BoolType +} + +/** Holds if `exp` applies the unary minus operator to a boolean type. */ +predicate revertSignBoolType(UnaryMinusExpr exp) { + exp.getAnOperand().getType() instanceof BoolType and + exp.getFullyConverted().getType() instanceof BoolType +} + +/** Holds, if this is an expression, uses comparison and assignment outside of execution precedence. */ +predicate assignBoolType(Expr exp) { + exists(ComparisonOperation co | + exp.(AssignExpr).getRValue() = co and + exp.isCondition() and + not co.isParenthesised() and + not exp.(AssignExpr).getLValue().getType() instanceof BoolType and + not exists(Expr exbl | + hashCons(exbl.(AssignExpr).getLValue()) = hashCons(exp.(AssignExpr).getLValue()) and + not exbl.isCondition() and + exbl.(AssignExpr).getRValue().getType() instanceof BoolType and + exbl.(AssignExpr).getLValue().getType() = exp.(AssignExpr).getLValue().getType() + ) and + co.getLeftOperand() instanceof FunctionCall and + not co.getRightOperand().getType() instanceof BoolType and + not co.getRightOperand().getValue() = "0" and + not co.getRightOperand().getValue() = "1" + ) +} + +from Expr exp +where + incrementBoolType(exp) or + revertSignBoolType(exp) or + assignBoolType(exp) +select exp, "this expression needs attention" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.ql b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.ql index 012109074e9..e4577968730 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.ql @@ -3,7 +3,7 @@ * @description The expression `buffer [strlen (buffer)] = 0` is potentially dangerous, if the variable `buffer` does not have a terminal zero, then access beyond the bounds of the allocated memory is possible, which will lead to undefined behavior. * If terminal zero is present, then the specified expression is meaningless. * @kind problem - * @id cpp/access-memory-location-after-end-buffer + * @id cpp/access-memory-location-after-end-buffer-strlen * @problem.severity warning * @precision medium * @tags correctness diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql index 5311ffe2708..f68395f29bf 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql @@ -2,7 +2,7 @@ * @name Access Of Memory Location After The End Of A Buffer Using Strncat * @description Calls of the form `strncat(dest, source, sizeof (dest) - strlen (dest))` set the third argument to one more than possible. So when `dest` is full, the expression `sizeof(dest) - strlen (dest)` will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the `dest` buffer. * @kind problem - * @id cpp/access-memory-location-after-end-buffer + * @id cpp/access-memory-location-after-end-buffer-strncat * @problem.severity warning * @precision medium * @tags correctness @@ -11,54 +11,32 @@ */ import cpp +import semmle.code.cpp.models.implementations.Strcat import semmle.code.cpp.valuenumbering.GlobalValueNumbering /** - * A call to `strncat` of the form `strncat(buff, str, someExpr - strlen(buf))`, for some expression `someExpr` equal to `sizeof(buff)`. + * Holds if `call` is a call to `strncat` such that `sizeArg` and `destArg` are the size and + * destination arguments, respectively. */ -class WrongCallStrncat extends FunctionCall { - Expr leftsomeExpr; - - WrongCallStrncat() { - this.getTarget().hasGlobalOrStdName("strncat") and - // the expression of the first argument in `strncat` and `strnlen` is identical - globalValueNumber(this.getArgument(0)) = - globalValueNumber(this.getArgument(2).(SubExpr).getRightOperand().(StrlenCall).getStringExpr()) and - // using a string constant often speaks of manually calculating the length of the required buffer. - ( - not this.getArgument(1) instanceof StringLiteral and - not this.getArgument(1) instanceof CharLiteral - ) and - // for use in predicates - leftsomeExpr = this.getArgument(2).(SubExpr).getLeftOperand() - } - - /** - * Holds if the left side of the expression `someExpr` equal to `sizeof(buf)`. - */ - predicate isExpressionEqualSizeof() { - // the left side of the expression `someExpr` is `sizeof(buf)`. - globalValueNumber(this.getArgument(0)) = - globalValueNumber(leftsomeExpr.(SizeofExprOperator).getExprOperand()) - or - // value of the left side of the expression `someExpr` equal `sizeof(buf)` value, and `buf` is array. - leftsomeExpr.getValue().toInt() = this.getArgument(0).getType().getSize() - } - - /** - * Holds if the left side of the expression `someExpr` equal to variable containing the length of the memory allocated for the buffer. - */ - predicate isVariableEqualValueSizegBuffer() { - // the left side of expression `someExpr` is the variable that was used in the function of allocating memory for the buffer`. - exists(AllocationExpr alc | - leftsomeExpr.(VariableAccess).getTarget() = - alc.(FunctionCall).getArgument(0).(VariableAccess).getTarget() - ) - } +predicate interestringCallWithArgs(Call call, Expr sizeArg, Expr destArg) { + exists(StrcatFunction strcat | + strcat = call.getTarget() and + sizeArg = call.getArgument(strcat.getParamSize()) and + destArg = call.getArgument(strcat.getParamDest()) + ) } -from WrongCallStrncat sc +from FunctionCall call, Expr sizeArg, Expr destArg, SubExpr sub, int n where - sc.isExpressionEqualSizeof() or - sc.isVariableEqualValueSizegBuffer() -select sc, "if the used buffer is full, writing out of the buffer is possible" + interestringCallWithArgs(call, sizeArg, destArg) and + // The destination buffer is an array of size n + destArg.getUnspecifiedType().(ArrayType).getSize() = n and + // The size argument is equivalent to a subtraction + globalValueNumber(sizeArg).getAnExpr() = sub and + // ... where the left side of the subtraction is the constant n + globalValueNumber(sub.getLeftOperand()).getAnExpr().getValue().toInt() = n and + // ... and the right side of the subtraction is a call to `strlen` where the argument is the + // destination buffer. + globalValueNumber(sub.getRightOperand()).getAnExpr().(StrlenCall).getStringExpr() = + globalValueNumber(destArg).getAnExpr() +select call, "Possible out-of-bounds write due to incorrect size argument." diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll index 7024f9d8598..f6072763e1a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll @@ -15,6 +15,7 @@ */ private import cpp +private import semmle.code.cpp.models.interfaces.PointerWrapper /** * Holds if `f` is an instantiation of the `std::move` or `std::forward` @@ -94,6 +95,12 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) { private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) { lvalueIn.getConversion() = referenceOut.(ReferenceToExpr) + or + exists(PointerWrapper wrapper, Call call | call = referenceOut | + referenceOut.getUnspecifiedType() instanceof ReferenceType and + call = wrapper.getAnUnwrapperFunction().getACallToThisFunction() and + lvalueIn = call.getQualifier().getFullyConverted() + ) } private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) { @@ -106,6 +113,13 @@ private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) { stdAddressOf(call.getTarget()) and referenceIn = call.getArgument(0).getFullyConverted() ) + or + exists(CopyConstructor copy, Call call | call = pointerOut | + copy.getDeclaringType() instanceof PointerWrapper and + call.getTarget() = copy and + // The 0'th argument is the value being copied. + referenceIn = call.getArgument(0).getFullyConverted() + ) } private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) { @@ -190,6 +204,19 @@ private predicate pointerToUpdate(Expr pointer, Expr outer, ControlFlowNode node // See the `lvalueToUpdate` case for an explanation of this conjunct. call.getType().isDeeplyConstBelow() ) + or + // Pointer wrappers behave as raw pointers for dataflow purposes. + outer = call.getAnArgument().getFullyConverted() and + exists(PointerWrapper wrapper | wrapper = outer.getType().stripTopLevelSpecifiers() | + not wrapper.pointsToConst() + ) + or + outer = call.getQualifier().getFullyConverted() and + outer.getUnspecifiedType() instanceof PointerWrapper and + not ( + call.getTarget().hasSpecifier("const") and + call.getType().isDeeplyConstBelow() + ) ) or exists(PointerFieldAccess fa | @@ -218,7 +245,9 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode not stdIdentityFunction(call.getTarget()) and not stdAddressOf(call.getTarget()) and exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() | - not rt.getBaseType().isConst() + not rt.getBaseType().isConst() or + rt.getBaseType().getUnspecifiedType() = + any(PointerWrapper wrapper | not wrapper.pointsToConst()) ) ) and reference = outer diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 8b446d28b86..9498e51e7e6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 8b446d28b86..9498e51e7e6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 8b446d28b86..9498e51e7e6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 8b446d28b86..9498e51e7e6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 8b446d28b86..9498e51e7e6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 0a6d459ec79..690f24fc59a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -46,7 +46,7 @@ class Node extends TNode { /** * INTERNAL: Do not use. Alternative name for `getFunction`. */ - final Function getEnclosingCallable() { result = unique(Function f | f = this.getFunction() | f) } + final Function getEnclosingCallable() { result = this.getFunction() } /** Gets the type of this node. */ Type getType() { none() } // overridden in subclasses @@ -324,7 +324,7 @@ private class VariablePartialDefinitionNode extends PartialDefinitionNode { * A synthetic data flow node used for flow into a collection when an iterator * write occurs in a callee. */ -class IteratorPartialDefinitionNode extends PartialDefinitionNode { +private class IteratorPartialDefinitionNode extends PartialDefinitionNode { override IteratorPartialDefinition pd; override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } @@ -715,6 +715,7 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) { } private module FieldFlow { + private import DataFlowImplCommon private import DataFlowImplLocal private import DataFlowPrivate @@ -747,7 +748,7 @@ private module FieldFlow { exists(FieldConfiguration cfg | cfg.hasFlow(node1, node2)) and // This configuration should not be able to cross function boundaries, but // we double-check here just to be sure. - node1.getEnclosingCallable() = node2.getEnclosingCallable() + getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) } } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index f3aa94a7992..e3f8b6f68fb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -7,6 +7,7 @@ private import semmle.code.cpp.controlflow.SSA private import semmle.code.cpp.dataflow.internal.SubBasicBlocks private import semmle.code.cpp.dataflow.internal.AddressFlow private import semmle.code.cpp.models.implementations.Iterator +private import semmle.code.cpp.models.interfaces.PointerWrapper /** * A conceptual variable that is assigned only once, like an SSA variable. This @@ -158,18 +159,14 @@ private module PartialDefinitions { Expr innerDefinedExpr; IteratorPartialDefinition() { - exists(Expr convertedInner | - not this instanceof Conversion and - valueToUpdate(convertedInner, this.getFullyConverted(), node) and - innerDefinedExpr = convertedInner.getUnconverted() and - ( - innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection) - or - innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and - collection instanceof IteratorParameter - ) and - innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator - ) + innerDefinedExpr = getInnerDefinedExpr(this, node) and + ( + innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection) + or + innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and + collection instanceof IteratorParameter + ) and + innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator or // iterators passed by value without a copy constructor exists(Call call | @@ -207,16 +204,18 @@ private module PartialDefinitions { } } + private Expr getInnerDefinedExpr(Expr e, ControlFlowNode node) { + not e instanceof Conversion and + exists(Expr convertedInner | + valueToUpdate(convertedInner, e.getFullyConverted(), node) and + result = convertedInner.getUnconverted() + ) + } + class VariablePartialDefinition extends PartialDefinition { Expr innerDefinedExpr; - VariablePartialDefinition() { - not this instanceof Conversion and - exists(Expr convertedInner | - valueToUpdate(convertedInner, this.getFullyConverted(), node) and - innerDefinedExpr = convertedInner.getUnconverted() - ) - } + VariablePartialDefinition() { innerDefinedExpr = getInnerDefinedExpr(this, node) } deprecated override predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() @@ -296,7 +295,8 @@ module FlowVar_internal { // treating them as immutable, but for data flow it gives better results in // practice to make the variable synonymous with its contents. not v.getUnspecifiedType() instanceof ReferenceType and - not v instanceof IteratorParameter + not v instanceof IteratorParameter and + not v instanceof PointerWrapperParameter } /** @@ -644,10 +644,19 @@ module FlowVar_internal { predicate parameterIsNonConstReference(Parameter p) { exists(ReferenceType refType | refType = p.getUnderlyingType() and - not refType.getBaseType().isConst() + ( + not refType.getBaseType().isConst() + or + // A field of a parameter of type `const std::shared_ptr& p` can still be changed even though + // the base type of the reference is `const`. + refType.getBaseType().getUnspecifiedType() = + any(PointerWrapper wrapper | not wrapper.pointsToConst()) + ) ) or p instanceof IteratorParameter + or + p instanceof PointerWrapperParameter } /** @@ -836,6 +845,10 @@ module FlowVar_internal { IteratorParameter() { this.getUnspecifiedType() instanceof Iterator } } + class PointerWrapperParameter extends Parameter { + PointerWrapperParameter() { this.getUnspecifiedType() instanceof PointerWrapper } + } + /** * Holds if `v` is initialized to have value `assignedExpr`. */ diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index 1ef340c4f21..591f461c8eb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -11,6 +11,7 @@ private import semmle.code.cpp.models.interfaces.DataFlow private import semmle.code.cpp.models.interfaces.Taint private import semmle.code.cpp.models.interfaces.Iterator +private import semmle.code.cpp.models.interfaces.PointerWrapper private module DataFlow { import semmle.code.cpp.dataflow.internal.DataFlowUtil @@ -141,7 +142,10 @@ private predicate noFlowFromChildExpr(Expr e) { or e instanceof LogicalOrExpr or - e instanceof Call + // Allow taint from `operator*` on smart pointers. + exists(Call call | e = call | + not call.getTarget() = any(PointerWrapper wrapper).getAnUnwrapperFunction() + ) or e instanceof SizeofOperator or diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll index 349efdcee10..6f6f710ac4b 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -314,6 +314,7 @@ class OverloadedPointerDereferenceFunction extends Function { * T1 operator*(const T2 &); * T1 a; T2 b; * a = *b; + * ``` */ class OverloadedPointerDereferenceExpr extends FunctionCall { OverloadedPointerDereferenceExpr() { diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 8b446d28b86..9498e51e7e6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 8b446d28b86..9498e51e7e6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 8b446d28b86..9498e51e7e6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 8b446d28b86..9498e51e7e6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 76ca7b215dc..f5fb7309cff 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -362,15 +362,22 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode { /** * Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to. - * For instance, an update to a field of a struct containing only one field. For these cases we - * attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case - * (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`. + * For instance, an update to a field of a struct containing only one field. Even if the store does + * have a chi instruction, a subsequent use of the result of the store may be linked directly to the + * result of the store as an inexact definition if the store totally overlaps the use. For these + * cases we attach the PostUpdateNode to the store instruction. There's no obvious pre update node + * for this case (as the entire memory is updated), so `getPreUpdateNode` is implemented as + * `none()`. */ private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode { override StoreInstruction instr; ExplicitSingleFieldStoreQualifierNode() { - not exists(ChiInstruction chi | chi.getPartial() = instr) and + ( + instr.getAUse().isDefinitionInexact() + or + not exists(ChiInstruction chi | chi.getPartial() = instr) + ) and // Without this condition any store would create a `PostUpdateNode`. instr.getDestinationAddress() instanceof FieldAddressInstruction } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll index 337dc71a3ca..16182296e40 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll @@ -6,34 +6,7 @@ private import semmle.code.cpp.ir.ValueNumbering private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.dataflow.DataFlow private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil - -/** - * Gets a short ID for an IR dataflow node. - * - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`). - * - For `Operand`s, this is the label of the operand, prefixed with the result ID of the - * instruction and a dot (e.g. `m128.left`). - * - For `Variable`s, this is the qualified name of the variable. - */ -private string nodeId(DataFlow::Node node, int order1, int order2) { - exists(Instruction instruction | instruction = node.asInstruction() | - result = instruction.getResultId() and - order1 = instruction.getBlock().getDisplayIndex() and - order2 = instruction.getDisplayIndexInBlock() - ) - or - exists(Operand operand, Instruction instruction | - operand = node.asOperand() and - instruction = operand.getUse() - | - result = instruction.getResultId() + "." + operand.getDumpId() and - order1 = instruction.getBlock().getDisplayIndex() and - order2 = instruction.getDisplayIndexInBlock() - ) - or - result = "var(" + node.asVariable().getQualifiedName() + ")" and - order1 = 1000000 and - order2 = 0 -} +private import PrintIRUtilities /** * Gets the local dataflow from other nodes in the same function to this node. diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRStoreSteps.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRStoreSteps.qll new file mode 100644 index 00000000000..8c318216217 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRStoreSteps.qll @@ -0,0 +1,33 @@ +/** + * Print the dataflow local store steps in IR dumps. + */ + +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil +private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate +private import PrintIRUtilities + +/** + * Property provider for local IR dataflow store steps. + */ +class LocalFlowPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instruction, string key) { + exists(DataFlow::Node objectNode, Content content | + key = "content[" + content.toString() + "]" and + instruction = objectNode.asInstruction() and + result = + strictconcat(string element, DataFlow::Node fieldNode | + storeStep(fieldNode, content, objectNode) and + element = nodeId(fieldNode, _, _) + | + element, ", " + ) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll new file mode 100644 index 00000000000..5fc15cf986c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll @@ -0,0 +1,39 @@ +/** + * Shared utilities used when printing dataflow annotations in IR dumps. + */ + +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow + +/** + * Gets a short ID for an IR dataflow node. + * - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`). + * - For `Operand`s, this is the label of the operand, prefixed with the result ID of the + * instruction and a dot (e.g. `m128.left`). + * - For `Variable`s, this is the qualified name of the variable. + */ +string nodeId(DataFlow::Node node, int order1, int order2) { + exists(Instruction instruction | instruction = node.asInstruction() | + result = instruction.getResultId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + exists(Operand operand, Instruction instruction | + operand = node.asOperand() and + instruction = operand.getUse() + | + result = instruction.getResultId() + "." + operand.getDumpId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + result = "var(" + node.asVariable().getQualifiedName() + ")" and + order1 = 1000000 and + order2 = 0 +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll new file mode 100644 index 00000000000..350127a58d1 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll @@ -0,0 +1,109 @@ +/** + * Predicates to compute the modeled side effects of calls during IR construction. + * + * These are used in `TranslatedElement.qll` to generate the `TTranslatedSideEffect` instances, and + * also in `TranslatedCall.qll` to inject the actual side effect instructions. + */ + +private import cpp +private import semmle.code.cpp.ir.implementation.Opcode +private import semmle.code.cpp.models.interfaces.SideEffect + +/** + * Holds if the specified call has a side effect that does not come from a `SideEffectFunction` + * model. + */ +private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buffer, boolean isWrite) { + not call.getTarget() instanceof SideEffectFunction and + ( + exists(MemberFunction mfunc | + // A non-static member function, including a constructor or destructor, may write to `*this`, + // and may also read from `*this` if it is not a constructor. + i = -1 and + mfunc = call.getTarget() and + not mfunc.isStatic() and + buffer = false and + ( + isWrite = false and not mfunc instanceof Constructor + or + isWrite = true and not mfunc instanceof ConstMemberFunction + ) + ) + or + exists(Expr expr | + // A pointer-like argument is assumed to read from the pointed-to buffer, and may write to the + // buffer as well unless the pointer points to a `const` value. + i >= 0 and + buffer = true and + expr = call.getArgument(i).getFullyConverted() and + exists(Type t | t = expr.getUnspecifiedType() | + t instanceof ArrayType or + t instanceof PointerType or + t instanceof ReferenceType + ) and + ( + isWrite = true and + not call.getTarget().getParameter(i).getType().isDeeplyConstBelow() + or + isWrite = false + ) + ) + ) +} + +/** + * Returns a side effect opcode for parameter index `i` of the specified call. + * + * This predicate will return at most two results: one read side effect, and one write side effect. + */ +Opcode getASideEffectOpcode(Call call, ParameterIndex i) { + exists(boolean buffer | + ( + call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(i, buffer) + or + not call.getTarget() instanceof SideEffectFunction and + hasDefaultSideEffect(call, i, buffer, false) + ) and + if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(i)) + then ( + buffer = true and + result instanceof Opcode::SizedBufferReadSideEffect + ) else ( + buffer = false and result instanceof Opcode::IndirectReadSideEffect + or + buffer = true and result instanceof Opcode::BufferReadSideEffect + ) + ) + or + exists(boolean buffer, boolean mustWrite | + ( + call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(i, buffer, mustWrite) + or + not call.getTarget() instanceof SideEffectFunction and + hasDefaultSideEffect(call, i, buffer, true) and + mustWrite = false + ) and + if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(i)) + then ( + buffer = true and + mustWrite = false and + result instanceof Opcode::SizedBufferMayWriteSideEffect + or + buffer = true and + mustWrite = true and + result instanceof Opcode::SizedBufferMustWriteSideEffect + ) else ( + buffer = false and + mustWrite = false and + result instanceof Opcode::IndirectMayWriteSideEffect + or + buffer = false and + mustWrite = true and + result instanceof Opcode::IndirectMustWriteSideEffect + or + buffer = true and mustWrite = false and result instanceof Opcode::BufferMayWriteSideEffect + or + buffer = true and mustWrite = true and result instanceof Opcode::BufferMustWriteSideEffect + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll index 7ad1dd2c01e..56d4c807ac8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll @@ -4,6 +4,7 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.models.interfaces.SideEffect private import InstructionTag +private import SideEffects private import TranslatedElement private import TranslatedExpr private import TranslatedFunction @@ -424,12 +425,15 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi } class TranslatedStructorCallSideEffects extends TranslatedCallSideEffects { - TranslatedStructorCallSideEffects() { getParent().(TranslatedStructorCall).hasQualifier() } + TranslatedStructorCallSideEffects() { + getParent().(TranslatedStructorCall).hasQualifier() and + getASideEffectOpcode(expr, -1) instanceof WriteSideEffectOpcode + } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) { - opcode instanceof Opcode::IndirectMayWriteSideEffect and tag instanceof OnlyInstructionTag and - t = getTypeForPRValue(expr.getTarget().getDeclaringType()) + t = getTypeForPRValue(expr.getTarget().getDeclaringType()) and + opcode = getASideEffectOpcode(expr, -1).(WriteSideEffectOpcode) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -460,9 +464,11 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff Call call; Expr arg; int index; - boolean write; + SideEffectOpcode sideEffectOpcode; - TranslatedSideEffect() { this = TTranslatedArgumentSideEffect(call, arg, index, write) } + TranslatedSideEffect() { + this = TTranslatedArgumentSideEffect(call, arg, index, sideEffectOpcode) + } override Locatable getAST() { result = arg } @@ -472,13 +478,13 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff int getArgumentIndex() { result = index } - predicate isWrite() { write = true } + predicate isWrite() { sideEffectOpcode instanceof WriteSideEffectOpcode } override string toString() { - write = true and + isWrite() and result = "(write side effect for " + arg.toString() + ")" or - write = false and + not isWrite() and result = "(read side effect for " + arg.toString() + ")" } @@ -489,29 +495,29 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { - isWrite() and - hasSpecificWriteSideEffect(opcode) and tag = OnlyInstructionTag() and + opcode = sideEffectOpcode and ( - opcode instanceof BufferAccessOpcode and - type = getUnknownType() - or - not opcode instanceof BufferAccessOpcode and - exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() | - if baseType instanceof VoidType - then type = getUnknownType() - else type = getTypeForPRValueOrUnknown(baseType) + isWrite() and + ( + opcode instanceof BufferAccessOpcode and + type = getUnknownType() + or + not opcode instanceof BufferAccessOpcode and + exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() | + if baseType instanceof VoidType + then type = getUnknownType() + else type = getTypeForPRValueOrUnknown(baseType) + ) + or + index = -1 and + not arg.getUnspecifiedType() instanceof DerivedType and + type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType()) ) or - index = -1 and - not arg.getUnspecifiedType() instanceof DerivedType and - type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType()) + not isWrite() and + type = getVoidType() ) - or - not isWrite() and - hasSpecificReadSideEffect(opcode) and - tag = OnlyInstructionTag() and - type = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -535,7 +541,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff override CppType getInstructionMemoryOperandType(InstructionTag tag, TypedOperandTag operandTag) { not isWrite() and - if hasSpecificReadSideEffect(any(BufferAccessOpcode op)) + if sideEffectOpcode instanceof BufferAccessOpcode then result = getUnknownType() and tag instanceof OnlyInstructionTag and @@ -557,56 +563,6 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff ) } - predicate hasSpecificWriteSideEffect(Opcode op) { - exists(boolean buffer, boolean mustWrite | - if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index)) - then - call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, true, mustWrite) and - buffer = true and - ( - mustWrite = false and op instanceof Opcode::SizedBufferMayWriteSideEffect - or - mustWrite = true and op instanceof Opcode::SizedBufferMustWriteSideEffect - ) - else ( - call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, buffer, mustWrite) and - ( - buffer = true and mustWrite = false and op instanceof Opcode::BufferMayWriteSideEffect - or - buffer = false and mustWrite = false and op instanceof Opcode::IndirectMayWriteSideEffect - or - buffer = true and mustWrite = true and op instanceof Opcode::BufferMustWriteSideEffect - or - buffer = false and mustWrite = true and op instanceof Opcode::IndirectMustWriteSideEffect - ) - ) - ) - or - not call.getTarget() instanceof SideEffectFunction and - getArgumentIndex() != -1 and - op instanceof Opcode::BufferMayWriteSideEffect - or - not call.getTarget() instanceof SideEffectFunction and - getArgumentIndex() = -1 and - op instanceof Opcode::IndirectMayWriteSideEffect - } - - predicate hasSpecificReadSideEffect(Opcode op) { - exists(boolean buffer | - call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(index, buffer) and - if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index)) - then buffer = true and op instanceof Opcode::SizedBufferReadSideEffect - else ( - buffer = true and op instanceof Opcode::BufferReadSideEffect - or - buffer = false and op instanceof Opcode::IndirectReadSideEffect - ) - ) - or - not call.getTarget() instanceof SideEffectFunction and - op instanceof Opcode::BufferReadSideEffect - } - override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) { tag = OnlyInstructionTag() and result = getTranslatedCallInstruction(call) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 1de9936ae1f..eca659914b0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -12,6 +12,7 @@ private import TranslatedStmt private import TranslatedExpr private import IRConstruction private import semmle.code.cpp.models.interfaces.SideEffect +private import SideEffects /** * Gets the "real" parent of `expr`. This predicate treats conversions as if @@ -635,46 +636,15 @@ newtype TTranslatedElement = // The side effects of an allocation, i.e. `new`, `new[]` or `malloc` TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or // A precise side effect of an argument to a `Call` - TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) { - ( - expr = call.getArgument(n).getFullyConverted() - or - expr = call.getQualifier().getFullyConverted() and - n = -1 and - // Exclude calls to static member functions. They don't modify the qualifier - not exists(MemberFunction func | func = call.getTarget() and func.isStatic()) - ) and - ( - call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(n, _) and - isWrite = false - or - call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(n, _, _) and - isWrite = true - or - not call.getTarget() instanceof SideEffectFunction and - exists(Type t | t = expr.getUnspecifiedType() | - t instanceof ArrayType or - t instanceof PointerType or - t instanceof ReferenceType - ) and - ( - isWrite = true and - not call.getTarget().getParameter(n).getType().isDeeplyConstBelow() - or - isWrite = false - ) - or - not call.getTarget() instanceof SideEffectFunction and - n = -1 and - ( - isWrite = true and - not call.getTarget() instanceof ConstMemberFunction - or - isWrite = false - ) - ) and + TTranslatedArgumentSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) { not ignoreExpr(expr) and - not ignoreExpr(call) + not ignoreExpr(call) and + ( + n >= 0 and expr = call.getArgument(n).getFullyConverted() + or + n = -1 and expr = call.getQualifier().getFullyConverted() + ) and + opcode = getASideEffectOpcode(call, n) } /** diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll index 7a1ad0403f7..adef44a1125 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll @@ -1,4 +1,5 @@ import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.PointerWrapper /** @@ -12,6 +13,25 @@ private class UniqueOrSharedPtr extends Class, PointerWrapper { or result.getClassAndName(["operator->", "get"]) = this } + + override predicate pointsToConst() { this.getTemplateArgument(0).(Type).isConst() } +} + +/** Any function that unwraps a pointer wrapper class to reveal the underlying pointer. */ +private class PointerWrapperFlow extends TaintFunction, DataFlowFunction { + PointerWrapperFlow() { + this = any(PointerWrapper wrapper).getAnUnwrapperFunction() and + not this.getUnspecifiedType() instanceof ReferenceType + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isReturnValueDeref() and + output.isQualifierObject() + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and output.isReturnValue() + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/PointerWrapper.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/PointerWrapper.qll index 447e7f7cede..8948aee424b 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/PointerWrapper.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/PointerWrapper.qll @@ -11,4 +11,7 @@ abstract class PointerWrapper extends Class { * that return a reference to the pointed-to object. */ abstract MemberFunction getAnUnwrapperFunction(); + + /** Holds if the type of the data that is pointed to by this pointer wrapper is `const`. */ + abstract predicate pointsToConst(); } diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll index e7ad1c559e6..465e64b9b6c 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll @@ -5,6 +5,8 @@ import cpp import semmle.code.cpp.controlflow.Dominance +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils /** * Holds if the value of `use` is guarded using `abs`. @@ -94,9 +96,15 @@ predicate guardedGreater(Operation e, Expr use) { VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() } /** - * Holds if `e` is not guarded against overflow by `use`. + * Holds if `e` potentially overflows and `use` is an operand of `e` that is not guarded. */ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) { + ( + convertedExprMightOverflowPositively(e) + or + // Ensure that the predicate holds when range analysis cannot determine an upper bound + upperBound(e.getFullyConverted()) = exprMaxVal(e.getFullyConverted()) + ) and use = e.getAnOperand() and exists(LocalScopeVariable v | use.getTarget() = v | // overflow possible if large @@ -115,9 +123,15 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) { } /** - * Holds if `e` is not guarded against underflow by `use`. + * Holds if `e` potentially underflows and `use` is an operand of `e` that is not guarded. */ predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) { + ( + convertedExprMightOverflowNegatively(e) + or + // Ensure that the predicate holds when range analysis cannot determine a lower bound + lowerBound(e.getFullyConverted()) = exprMinVal(e.getFullyConverted()) + ) and use = e.getAnOperand() and exists(LocalScopeVariable v | use.getTarget() = v | // underflow possible if use is left operand and small diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.expected new file mode 100644 index 00000000000..1eca77a526f --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.expected @@ -0,0 +1,4 @@ +| test.c:15:6:15:16 | ... + ... | this expression needs your attention | +| test.c:17:17:17:27 | ... + ... | this expression needs your attention | +| test.c:22:10:22:15 | ... > ... | this expression needs your attention | +| test.c:26:10:26:15 | ... > ... | this expression needs your attention | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.qlref new file mode 100644 index 00000000000..496d5f1b7be --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.expected new file mode 100644 index 00000000000..d11bbd446a6 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.expected @@ -0,0 +1,2 @@ +| test.c:8:6:8:51 | ... & ... | This bitwise operation appears in a context where a Boolean operation is expected. | +| test.c:10:6:10:30 | ... & ... | This bitwise operation appears in a context where a Boolean operation is expected. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.qlref new file mode 100644 index 00000000000..9bf28db3c8a --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c new file mode 100644 index 00000000000..1f41f499ded --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c @@ -0,0 +1,32 @@ +int tmpFunction(){ + return 5; +} +void workFunction_0(char *s) { + int intSize; + char buf[80]; + if(intSize>0 && intSize<80 && memset(buf,0,intSize)) return; // GOOD + if(intSize>0 & intSize<80 & memset(buf,0,intSize)) return; // BAD + if(intSize>0 && tmpFunction()) return; + if(intSize<0 & tmpFunction()) return; // BAD +} +void workFunction_1(char *s) { + int intA,intB; + + if(intA + intB) return; // BAD + if(intA + intB>4) return; // GOOD + if(intA>0 && (intA + intB)) return; // BAD + while(intA>0) + { + if(intB - intA<10) break; + intA--; + }while(intA>0); // BAD + for(intA=100; intA>0; intA--) + { + if(intB - intA<10) break; + }while(intA>0); // BAD + while(intA>0) + { + if(intB - intA<10) break; + intA--; + } // GOOD +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected index 103afd8ffd9..cd160a70638 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected @@ -1,9 +1,9 @@ -| test.c:42:3:42:24 | ... = ... | potential unsafe or redundant assignment. | -| test.c:43:3:43:40 | ... = ... | potential unsafe or redundant assignment. | -| test.c:44:3:44:40 | ... = ... | potential unsafe or redundant assignment. | -| test.c:45:3:45:44 | ... = ... | potential unsafe or redundant assignment. | -| test.c:46:3:46:44 | ... = ... | potential unsafe or redundant assignment. | -| test.c:47:3:47:48 | ... = ... | potential unsafe or redundant assignment. | -| test.c:48:3:48:48 | ... = ... | potential unsafe or redundant assignment. | -| test.c:49:3:49:50 | ... = ... | potential unsafe or redundant assignment. | -| test.c:50:3:50:50 | ... = ... | potential unsafe or redundant assignment. | +| test.c:54:3:54:24 | ... = ... | potential unsafe or redundant assignment. | +| test.c:55:3:55:40 | ... = ... | potential unsafe or redundant assignment. | +| test.c:56:3:56:44 | ... = ... | potential unsafe or redundant assignment. | +| test.c:57:3:57:44 | ... = ... | potential unsafe or redundant assignment. | +| test.c:58:3:58:48 | ... = ... | potential unsafe or redundant assignment. | +| test.c:59:3:59:48 | ... = ... | potential unsafe or redundant assignment. | +| test.c:60:3:60:52 | ... = ... | potential unsafe or redundant assignment. | +| test.c:61:3:61:50 | ... = ... | potential unsafe or redundant assignment. | +| test.c:62:3:62:54 | ... = ... | potential unsafe or redundant assignment. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected index af52dac0144..3e9791e1024 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected @@ -1,3 +1,5 @@ -| test.c:4:3:4:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible | -| test.c:11:3:11:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible | -| test.c:19:3:19:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible | +| test.c:8:3:8:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | +| test.c:9:3:9:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | +| test.c:17:3:17:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | +| test.c:18:3:18:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | +| test.c:46:3:46:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.expected new file mode 100644 index 00000000000..1209c7e7830 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.expected @@ -0,0 +1,5 @@ +| test.cpp:10:8:10:10 | - ... | this expression needs attention | +| test.cpp:12:3:12:6 | ... ++ | this expression needs attention | +| test.cpp:13:3:13:6 | ++ ... | this expression needs attention | +| test.cpp:14:6:14:21 | ... = ... | this expression needs attention | +| test.cpp:16:6:16:21 | ... = ... | this expression needs attention | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.qlref new file mode 100644 index 00000000000..5189abcce5d --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c index d986bb3b13c..6d310875630 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c @@ -1,70 +1,84 @@ -void workFunction_0(char *s) { +char * strncat(char*, const char*, unsigned); +unsigned strlen(const char*); +void* malloc(unsigned); + +void strncat_test1(char *s) { char buf[80]; - strncat(buf, s, sizeof(buf)-strlen(buf)-1); // GOOD - strncat(buf, s, sizeof(buf)-strlen(buf)); // BAD - strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD [NOT DETECTED] + strncat(buf, s, sizeof(buf) - strlen(buf) - 1); // GOOD + strncat(buf, s, sizeof(buf) - strlen(buf)); // BAD + strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD } -void workFunction_1(char *s) { + #define MAX_SIZE 80 + +void strncat_test2(char *s) { char buf[MAX_SIZE]; - strncat(buf, s, MAX_SIZE-strlen(buf)-1); // GOOD - strncat(buf, s, MAX_SIZE-strlen(buf)); // BAD - strncat(buf, "fix", MAX_SIZE-strlen(buf)); // BAD [NOT DETECTED] + strncat(buf, s, MAX_SIZE - strlen(buf) - 1); // GOOD + strncat(buf, s, MAX_SIZE - strlen(buf)); // BAD + strncat(buf, "fix", MAX_SIZE - strlen(buf)); // BAD } -void workFunction_2_0(char *s) { - char * buf; - int len=80; - buf = (char *) malloc(len); - strncat(buf, s, len-strlen(buf)-1); // GOOD - strncat(buf, s, len-strlen(buf)); // BAD - strncat(buf, "fix", len-strlen(buf)); // BAD [NOT DETECTED] + +void strncat_test3(char *s) { + int len = 80; + char* buf = (char *) malloc(len); + strncat(buf, s, len - strlen(buf) - 1); // GOOD + strncat(buf, s, len - strlen(buf)); // BAD [NOT DETECTED] + strncat(buf, "fix", len - strlen(buf)); // BAD [NOT DETECTED] } -void workFunction_2_1(char *s) { - char * buf; - int len=80; - buf = (char *) malloc(len+1); - strncat(buf, s, len-strlen(buf)-1); // GOOD - strncat(buf, s, len-strlen(buf)); // GOOD + +void strncat_test4(char *s) { + int len = 80; + char* buf = (char *) malloc(len + 1); + strncat(buf, s, len - strlen(buf) - 1); // GOOD + strncat(buf, s, len - strlen(buf)); // GOOD } struct buffers { - unsigned char buff1[50]; - unsigned char *buff2; + unsigned char array[50]; + unsigned char *pointer; } globalBuff1,*globalBuff2,globalBuff1_c,*globalBuff2_c; +void strncat_test5(char* s, struct buffers* buffers) { + unsigned len_array = strlen(buffers->array); + unsigned max_size = sizeof(buffers->array); + unsigned free_size = max_size - len_array; + strncat(buffers->array, s, free_size); // BAD +} -void badFunc0(){ +void strlen_test1(){ unsigned char buff1[12]; struct buffers buffAll; struct buffers * buffAll1; buff1[strlen(buff1)]=0; // BAD - buffAll.buff1[strlen(buffAll.buff1)]=0; // BAD - buffAll.buff2[strlen(buffAll.buff2)]=0; // BAD - buffAll1->buff1[strlen(buffAll1->buff1)]=0; // BAD - buffAll1->buff2[strlen(buffAll1->buff2)]=0; // BAD - globalBuff1.buff1[strlen(globalBuff1.buff1)]=0; // BAD - globalBuff1.buff2[strlen(globalBuff1.buff2)]=0; // BAD - globalBuff2->buff1[strlen(globalBuff2->buff1)]=0; // BAD - globalBuff2->buff2[strlen(globalBuff2->buff2)]=0; // BAD + buffAll.array[strlen(buffAll.array)]=0; // BAD + buffAll.pointer[strlen(buffAll.pointer)]=0; // BAD + buffAll1->array[strlen(buffAll1->array)]=0; // BAD + buffAll1->pointer[strlen(buffAll1->pointer)]=0; // BAD + globalBuff1.array[strlen(globalBuff1.array)]=0; // BAD + globalBuff1.pointer[strlen(globalBuff1.pointer)]=0; // BAD + globalBuff2->array[strlen(globalBuff2->array)]=0; // BAD + globalBuff2->pointer[strlen(globalBuff2->pointer)]=0; // BAD } -void noBadFunc0(){ + +void strlen_test2(){ unsigned char buff1[12],buff1_c[12]; struct buffers buffAll,buffAll_c; struct buffers * buffAll1,*buffAll1_c; buff1[strlen(buff1_c)]=0; // GOOD - buffAll.buff1[strlen(buffAll_c.buff1)]=0; // GOOD - buffAll.buff2[strlen(buffAll.buff1)]=0; // GOOD - buffAll1->buff1[strlen(buffAll1_c->buff1)]=0; // GOOD - buffAll1->buff2[strlen(buffAll1->buff1)]=0; // GOOD - globalBuff1.buff1[strlen(globalBuff1_c.buff1)]=0; // GOOD - globalBuff1.buff2[strlen(globalBuff1.buff1)]=0; // GOOD - globalBuff2->buff1[strlen(globalBuff2_c->buff1)]=0; // GOOD - globalBuff2->buff2[strlen(globalBuff2->buff1)]=0; // GOOD + buffAll.array[strlen(buffAll_c.array)]=0; // GOOD + buffAll.pointer[strlen(buffAll.array)]=0; // GOOD + buffAll1->array[strlen(buffAll1_c->array)]=0; // GOOD + buffAll1->pointer[strlen(buffAll1->array)]=0; // GOOD + globalBuff1.array[strlen(globalBuff1_c.array)]=0; // GOOD + globalBuff1.pointer[strlen(globalBuff1.array)]=0; // GOOD + globalBuff2->array[strlen(globalBuff2_c->array)]=0; // GOOD + globalBuff2->pointer[strlen(globalBuff2->array)]=0; // GOOD } -void goodFunc0(){ + +void strlen_test3(){ unsigned char buffer[12]; int i; for(i = 0; i < 6; i++) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.cpp new file mode 100644 index 00000000000..f08d2a45757 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.cpp @@ -0,0 +1,26 @@ +int tmpFunc() +{ + return 12; +} +void testFunction() +{ + int i1,i2,i3; + bool b1,b2,b3; + char c1,c2,c3; + b1 = -b2; //BAD + b1 = !b2; //GOOD + b1++; //BAD + ++b1; //BAD + if(i1=tmpFunc()!=i2) //BAD + return; + if(i1=tmpFunc()!=11) //BAD + return; + if((i1=tmpFunc())!=i2) //GOOD + return; + if((i1=tmpFunc())!=11) //GOOD + return; + if(i1=tmpFunc()!=1) //GOOD + return; + if(i1=tmpFunc()==b1) //GOOD + return; +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index fc6c97aa2a6..db9a86fbb57 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -26,6 +26,7 @@ unreachableNodeCCtx localCallNodes postIsNotPre postHasUniquePre +| test.cpp:373:5:373:20 | Store | PostUpdateNode should have one pre-update node but has 0. | uniquePostUpdate postIsInSameCallable reverseRead @@ -82,4 +83,5 @@ postWithInFlow | test.cpp:125:3:125:11 | Chi | PostUpdateNode should not be the target of local flow. | | test.cpp:359:5:359:20 | Chi | PostUpdateNode should not be the target of local flow. | | test.cpp:373:5:373:20 | Chi | PostUpdateNode should not be the target of local flow. | +| test.cpp:373:5:373:20 | Store | PostUpdateNode should not be the target of local flow. | | test.cpp:465:3:465:15 | Chi | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index 63d3b2c0f48..fe7d8360403 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -20,7 +20,9 @@ unreachableNodeCCtx localCallNodes postIsNotPre postHasUniquePre +| D.cpp:57:5:57:42 | Store | PostUpdateNode should have one pre-update node but has 0. | | simple.cpp:65:5:65:22 | Store | PostUpdateNode should have one pre-update node but has 0. | +| simple.cpp:83:9:83:28 | Store | PostUpdateNode should have one pre-update node but has 0. | | simple.cpp:92:5:92:22 | Store | PostUpdateNode should have one pre-update node but has 0. | uniquePostUpdate postIsInSameCallable @@ -54,6 +56,7 @@ postWithInFlow | D.cpp:49:15:49:24 | Chi | PostUpdateNode should not be the target of local flow. | | D.cpp:56:15:56:24 | Chi | PostUpdateNode should not be the target of local flow. | | D.cpp:57:5:57:42 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:57:5:57:42 | Store | PostUpdateNode should not be the target of local flow. | | aliasing.cpp:9:3:9:22 | Chi | PostUpdateNode should not be the target of local flow. | | aliasing.cpp:13:3:13:21 | Chi | PostUpdateNode should not be the target of local flow. | | aliasing.cpp:17:3:17:21 | Chi | PostUpdateNode should not be the target of local flow. | @@ -150,6 +153,7 @@ postWithInFlow | simple.cpp:23:35:23:35 | Chi | PostUpdateNode should not be the target of local flow. | | simple.cpp:65:5:65:22 | Store | PostUpdateNode should not be the target of local flow. | | simple.cpp:83:9:83:28 | Chi | PostUpdateNode should not be the target of local flow. | +| simple.cpp:83:9:83:28 | Store | PostUpdateNode should not be the target of local flow. | | simple.cpp:92:5:92:22 | Store | PostUpdateNode should not be the target of local flow. | | struct_init.c:20:20:20:29 | Chi | PostUpdateNode should not be the target of local flow. | | struct_init.c:20:34:20:34 | Chi | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index 37a0dc3832a..e6234ca17f7 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -228,8 +228,8 @@ edges | simple.cpp:65:5:65:22 | Store [i] | simple.cpp:66:12:66:12 | Store [i] | | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | Store [i] | | simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i | -| simple.cpp:83:9:83:28 | Chi [f1] | simple.cpp:84:14:84:20 | this indirection [f1] | -| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Chi [f1] | +| simple.cpp:83:9:83:28 | Store [f1] | simple.cpp:84:14:84:20 | this indirection [f1] | +| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Store [f1] | | simple.cpp:84:14:84:20 | this indirection [f1] | simple.cpp:84:14:84:20 | call to getf2f1 | | simple.cpp:92:5:92:22 | Store [i] | simple.cpp:93:20:93:20 | Store [i] | | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | Store [i] | @@ -494,7 +494,7 @@ nodes | simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input | | simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] | | simple.cpp:67:13:67:13 | i | semmle.label | i | -| simple.cpp:83:9:83:28 | Chi [f1] | semmle.label | Chi [f1] | +| simple.cpp:83:9:83:28 | Store [f1] | semmle.label | Store [f1] | | simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input | | simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 | | simple.cpp:84:14:84:20 | this indirection [f1] | semmle.label | this indirection [f1] | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql index d8b6b4e0e69..fae4b06da5a 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql @@ -19,15 +19,19 @@ class IRPartialDefNode extends IRNode { override string toString() { result = n.asPartialDefinition().toString() } } -from Node node, AST::Node astNode, IR::Node irNode, string msg +from Node node, string msg where - node.asIR() = irNode and - exists(irNode.asPartialDefinition()) and - not exists(AST::Node otherNode | otherNode.asPartialDefinition() = irNode.asPartialDefinition()) and + exists(IR::Node irNode, Expr partial | + node.asIR() = irNode and + partial = irNode.asPartialDefinition() and + not exists(AST::Node otherNode | otherNode.asPartialDefinition() = partial) + ) and msg = "IR only" or - node.asAST() = astNode and - exists(astNode.asPartialDefinition()) and - not exists(IR::Node otherNode | otherNode.asPartialDefinition() = astNode.asPartialDefinition()) and + exists(AST::Node astNode, Expr partial | + node.asAST() = astNode and + partial = astNode.asPartialDefinition() and + not exists(IR::Node otherNode | otherNode.asPartialDefinition() = partial) + ) and msg = "AST only" select node, msg diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 4444ea13267..b003dc5a12b 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3223,52 +3223,224 @@ | smart_pointer.cpp:11:30:11:50 | call to make_shared | smart_pointer.cpp:12:11:12:11 | p | | | smart_pointer.cpp:11:30:11:50 | call to make_shared | smart_pointer.cpp:13:10:13:10 | p | | | smart_pointer.cpp:11:52:11:57 | call to source | smart_pointer.cpp:11:30:11:50 | call to make_shared | TAINT | -| smart_pointer.cpp:12:11:12:11 | p | smart_pointer.cpp:12:10:12:10 | call to operator* | TAINT | +| smart_pointer.cpp:12:11:12:11 | p | smart_pointer.cpp:12:10:12:10 | call to operator* | | | smart_pointer.cpp:12:11:12:11 | ref arg p | smart_pointer.cpp:13:10:13:10 | p | | | smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:18:11:18:11 | p | | | smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:19:10:19:10 | p | | +| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:18:11:18:11 | p [inner post update] | | +| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:19:10:19:10 | p | | | smart_pointer.cpp:18:11:18:11 | p | smart_pointer.cpp:18:10:18:10 | call to operator* | TAINT | +| smart_pointer.cpp:18:11:18:11 | ref arg p | smart_pointer.cpp:18:11:18:11 | p [inner post update] | | | smart_pointer.cpp:18:11:18:11 | ref arg p | smart_pointer.cpp:19:10:19:10 | p | | | smart_pointer.cpp:23:30:23:50 | call to make_unique | smart_pointer.cpp:24:11:24:11 | p | | | smart_pointer.cpp:23:30:23:50 | call to make_unique | smart_pointer.cpp:25:10:25:10 | p | | | smart_pointer.cpp:23:52:23:57 | call to source | smart_pointer.cpp:23:30:23:50 | call to make_unique | TAINT | -| smart_pointer.cpp:24:11:24:11 | p | smart_pointer.cpp:24:10:24:10 | call to operator* | TAINT | +| smart_pointer.cpp:24:11:24:11 | p | smart_pointer.cpp:24:10:24:10 | call to operator* | | | smart_pointer.cpp:24:11:24:11 | ref arg p | smart_pointer.cpp:25:10:25:10 | p | | | smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:30:11:30:11 | p | | | smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:31:10:31:10 | p | | +| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:30:11:30:11 | p [inner post update] | | +| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:31:10:31:10 | p | | | smart_pointer.cpp:30:11:30:11 | p | smart_pointer.cpp:30:10:30:10 | call to operator* | TAINT | +| smart_pointer.cpp:30:11:30:11 | ref arg p | smart_pointer.cpp:30:11:30:11 | p [inner post update] | | | smart_pointer.cpp:30:11:30:11 | ref arg p | smart_pointer.cpp:31:10:31:10 | p | | | smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:37:6:37:6 | p | | | smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:38:10:38:10 | p | | | smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:39:11:39:11 | p | | +| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:37:6:37:6 | p [inner post update] | | +| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:38:10:38:10 | p | | +| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:39:11:39:11 | p | | | smart_pointer.cpp:37:5:37:17 | ... = ... | smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | | -| smart_pointer.cpp:37:6:37:6 | p | smart_pointer.cpp:37:5:37:5 | call to operator* | TAINT | +| smart_pointer.cpp:37:6:37:6 | p | smart_pointer.cpp:37:5:37:5 | call to operator* | | +| smart_pointer.cpp:37:6:37:6 | ref arg p | smart_pointer.cpp:37:6:37:6 | p [inner post update] | | | smart_pointer.cpp:37:6:37:6 | ref arg p | smart_pointer.cpp:38:10:38:10 | p | | | smart_pointer.cpp:37:6:37:6 | ref arg p | smart_pointer.cpp:39:11:39:11 | p | | | smart_pointer.cpp:37:10:37:15 | call to source | smart_pointer.cpp:37:5:37:17 | ... = ... | | | smart_pointer.cpp:38:10:38:10 | ref arg p | smart_pointer.cpp:39:11:39:11 | p | | -| smart_pointer.cpp:39:11:39:11 | p | smart_pointer.cpp:39:10:39:10 | call to operator* | TAINT | +| smart_pointer.cpp:39:11:39:11 | p | smart_pointer.cpp:39:10:39:10 | call to operator* | | | smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:45:6:45:6 | p | | | smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:46:10:46:10 | p | | | smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:47:11:47:11 | p | | +| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:45:6:45:6 | p [inner post update] | | +| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:46:10:46:10 | p | | +| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:47:11:47:11 | p | | | smart_pointer.cpp:45:5:45:17 | ... = ... | smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | | -| smart_pointer.cpp:45:6:45:6 | p | smart_pointer.cpp:45:5:45:5 | call to operator* | TAINT | +| smart_pointer.cpp:45:6:45:6 | p | smart_pointer.cpp:45:5:45:5 | call to operator* | | +| smart_pointer.cpp:45:6:45:6 | ref arg p | smart_pointer.cpp:45:6:45:6 | p [inner post update] | | | smart_pointer.cpp:45:6:45:6 | ref arg p | smart_pointer.cpp:46:10:46:10 | p | | | smart_pointer.cpp:45:6:45:6 | ref arg p | smart_pointer.cpp:47:11:47:11 | p | | | smart_pointer.cpp:45:10:45:15 | call to source | smart_pointer.cpp:45:5:45:17 | ... = ... | | | smart_pointer.cpp:46:10:46:10 | ref arg p | smart_pointer.cpp:47:11:47:11 | p | | -| smart_pointer.cpp:47:11:47:11 | p | smart_pointer.cpp:47:10:47:10 | call to operator* | TAINT | +| smart_pointer.cpp:47:11:47:11 | p | smart_pointer.cpp:47:10:47:10 | call to operator* | | | smart_pointer.cpp:51:30:51:50 | call to make_shared | smart_pointer.cpp:52:10:52:10 | p | | | smart_pointer.cpp:51:52:51:57 | call to source | smart_pointer.cpp:51:30:51:50 | call to make_shared | TAINT | -| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | TAINT | +| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | | +| smart_pointer.cpp:52:12:52:14 | ref arg call to get | smart_pointer.cpp:52:10:52:10 | ref arg p | TAINT | | smart_pointer.cpp:56:30:56:50 | call to make_unique | smart_pointer.cpp:57:10:57:10 | p | | | smart_pointer.cpp:56:52:56:57 | call to source | smart_pointer.cpp:56:30:56:50 | call to make_unique | TAINT | -| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | TAINT | +| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | | +| smart_pointer.cpp:57:12:57:14 | ref arg call to get | smart_pointer.cpp:57:10:57:10 | ref arg p | TAINT | | smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:66:10:66:10 | p | | | smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:67:10:67:10 | p | | | smart_pointer.cpp:65:48:65:53 | call to source | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT | | smart_pointer.cpp:65:58:65:58 | 0 | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT | +| smart_pointer.cpp:66:10:66:10 | p | smart_pointer.cpp:66:11:66:11 | call to operator-> | | | smart_pointer.cpp:66:10:66:10 | ref arg p | smart_pointer.cpp:67:10:67:10 | p | | +| smart_pointer.cpp:67:10:67:10 | p | smart_pointer.cpp:67:11:67:11 | call to operator-> | | +| smart_pointer.cpp:70:37:70:39 | ptr | smart_pointer.cpp:70:37:70:39 | ptr | | +| smart_pointer.cpp:70:37:70:39 | ptr | smart_pointer.cpp:71:4:71:6 | ptr | | +| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:70:37:70:39 | ptr | | +| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:71:4:71:6 | ptr [inner post update] | | +| smart_pointer.cpp:71:3:71:17 | ... = ... | smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | | +| smart_pointer.cpp:71:4:71:6 | ptr | smart_pointer.cpp:71:3:71:3 | call to operator* | | +| smart_pointer.cpp:71:4:71:6 | ref arg ptr | smart_pointer.cpp:70:37:70:39 | ptr | | +| smart_pointer.cpp:71:4:71:6 | ref arg ptr | smart_pointer.cpp:71:4:71:6 | ptr [inner post update] | | +| smart_pointer.cpp:71:10:71:15 | call to source | smart_pointer.cpp:71:3:71:17 | ... = ... | | +| smart_pointer.cpp:75:26:75:33 | call to shared_ptr | smart_pointer.cpp:76:13:76:13 | p | | +| smart_pointer.cpp:75:26:75:33 | call to shared_ptr | smart_pointer.cpp:77:9:77:9 | p | | +| smart_pointer.cpp:76:13:76:13 | p | smart_pointer.cpp:76:13:76:13 | call to shared_ptr | | +| smart_pointer.cpp:76:13:76:13 | ref arg call to shared_ptr | smart_pointer.cpp:76:13:76:13 | p [inner post update] | | +| smart_pointer.cpp:76:13:76:13 | ref arg call to shared_ptr | smart_pointer.cpp:77:9:77:9 | p | | +| smart_pointer.cpp:76:13:76:13 | ref arg p | smart_pointer.cpp:76:13:76:13 | p [inner post update] | | +| smart_pointer.cpp:76:13:76:13 | ref arg p | smart_pointer.cpp:77:9:77:9 | p | | +| smart_pointer.cpp:77:9:77:9 | p | smart_pointer.cpp:77:8:77:8 | call to operator* | | +| smart_pointer.cpp:86:45:86:45 | p | smart_pointer.cpp:86:45:86:45 | p | | +| smart_pointer.cpp:86:45:86:45 | p | smart_pointer.cpp:87:3:87:3 | p | | +| smart_pointer.cpp:86:45:86:45 | p | smart_pointer.cpp:88:8:88:8 | p | | +| smart_pointer.cpp:86:45:86:45 | p | smart_pointer.cpp:89:8:89:8 | p | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:91:3:91:3 | q | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:92:8:92:8 | q | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:93:8:93:8 | q | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:94:8:94:8 | q | | +| smart_pointer.cpp:87:3:87:3 | p | smart_pointer.cpp:87:4:87:4 | call to operator-> | | +| smart_pointer.cpp:87:3:87:3 | ref arg p | smart_pointer.cpp:86:45:86:45 | p | | +| smart_pointer.cpp:87:3:87:3 | ref arg p | smart_pointer.cpp:88:8:88:8 | p | | +| smart_pointer.cpp:87:3:87:3 | ref arg p | smart_pointer.cpp:89:8:89:8 | p | | +| smart_pointer.cpp:87:3:87:17 | ... = ... | smart_pointer.cpp:87:6:87:6 | x [post update] | | +| smart_pointer.cpp:87:3:87:17 | ... = ... | smart_pointer.cpp:88:11:88:11 | x | | +| smart_pointer.cpp:87:4:87:4 | call to operator-> [post update] | smart_pointer.cpp:87:3:87:3 | ref arg p | TAINT | +| smart_pointer.cpp:87:10:87:15 | call to source | smart_pointer.cpp:87:3:87:17 | ... = ... | | +| smart_pointer.cpp:88:8:88:8 | p | smart_pointer.cpp:88:9:88:9 | call to operator-> | | +| smart_pointer.cpp:88:8:88:8 | ref arg p | smart_pointer.cpp:86:45:86:45 | p | | +| smart_pointer.cpp:88:8:88:8 | ref arg p | smart_pointer.cpp:89:8:89:8 | p | | +| smart_pointer.cpp:89:8:89:8 | p | smart_pointer.cpp:89:9:89:9 | call to operator-> | | +| smart_pointer.cpp:89:8:89:8 | ref arg p | smart_pointer.cpp:86:45:86:45 | p | | +| smart_pointer.cpp:91:3:91:3 | q | smart_pointer.cpp:91:4:91:4 | call to operator-> | | +| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:92:8:92:8 | q | | +| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:93:8:93:8 | q | | +| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | | +| smart_pointer.cpp:91:3:91:20 | ... = ... | smart_pointer.cpp:91:9:91:9 | x [post update] | | +| smart_pointer.cpp:91:3:91:20 | ... = ... | smart_pointer.cpp:92:14:92:14 | x | | +| smart_pointer.cpp:91:4:91:4 | call to operator-> [post update] | smart_pointer.cpp:91:3:91:3 | ref arg q | TAINT | +| smart_pointer.cpp:91:13:91:18 | call to source | smart_pointer.cpp:91:3:91:20 | ... = ... | | +| smart_pointer.cpp:92:8:92:8 | q | smart_pointer.cpp:92:9:92:9 | call to operator-> | | +| smart_pointer.cpp:92:8:92:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:92:8:92:8 | ref arg q | smart_pointer.cpp:93:8:93:8 | q | | +| smart_pointer.cpp:92:8:92:8 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | | +| smart_pointer.cpp:93:8:93:8 | q | smart_pointer.cpp:93:9:93:9 | call to operator-> | | +| smart_pointer.cpp:93:8:93:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:93:8:93:8 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | | +| smart_pointer.cpp:94:8:94:8 | q | smart_pointer.cpp:94:9:94:9 | call to operator-> | | +| smart_pointer.cpp:94:8:94:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:97:17:97:18 | pa | smart_pointer.cpp:98:5:98:6 | pa | | +| smart_pointer.cpp:98:5:98:20 | ... = ... | smart_pointer.cpp:98:9:98:9 | x [post update] | | +| smart_pointer.cpp:98:13:98:18 | call to source | smart_pointer.cpp:98:5:98:20 | ... = ... | | +| smart_pointer.cpp:102:25:102:50 | call to unique_ptr | smart_pointer.cpp:103:11:103:11 | p | | +| smart_pointer.cpp:102:25:102:50 | call to unique_ptr | smart_pointer.cpp:104:8:104:8 | p | | +| smart_pointer.cpp:103:11:103:11 | p | smart_pointer.cpp:103:13:103:15 | call to get | | +| smart_pointer.cpp:103:11:103:11 | ref arg p | smart_pointer.cpp:104:8:104:8 | p | | +| smart_pointer.cpp:103:13:103:15 | ref arg call to get | smart_pointer.cpp:103:11:103:11 | ref arg p | TAINT | +| smart_pointer.cpp:104:8:104:8 | p | smart_pointer.cpp:104:9:104:9 | call to operator-> | | +| smart_pointer.cpp:112:40:112:42 | ptr | smart_pointer.cpp:112:40:112:42 | ptr | | +| smart_pointer.cpp:112:40:112:42 | ptr | smart_pointer.cpp:113:2:113:4 | ptr | | +| smart_pointer.cpp:113:2:113:4 | ptr | smart_pointer.cpp:113:5:113:5 | call to operator-> | | +| smart_pointer.cpp:113:2:113:4 | ref arg ptr | smart_pointer.cpp:112:40:112:42 | ptr | | +| smart_pointer.cpp:113:2:113:18 | ... = ... | smart_pointer.cpp:113:7:113:7 | x [post update] | | +| smart_pointer.cpp:113:5:113:5 | call to operator-> [post update] | smart_pointer.cpp:113:2:113:4 | ref arg ptr | TAINT | +| smart_pointer.cpp:113:11:113:16 | call to source | smart_pointer.cpp:113:2:113:18 | ... = ... | | +| smart_pointer.cpp:116:52:116:54 | ptr | smart_pointer.cpp:116:52:116:54 | ptr | | +| smart_pointer.cpp:116:52:116:54 | ptr | smart_pointer.cpp:117:2:117:4 | ptr | | +| smart_pointer.cpp:117:2:117:4 | ptr | smart_pointer.cpp:117:5:117:5 | call to operator-> | | +| smart_pointer.cpp:117:2:117:4 | ref arg ptr | smart_pointer.cpp:116:52:116:54 | ptr | | +| smart_pointer.cpp:117:2:117:18 | ... = ... | smart_pointer.cpp:117:7:117:7 | x [post update] | | +| smart_pointer.cpp:117:5:117:5 | call to operator-> [post update] | smart_pointer.cpp:117:2:117:4 | ref arg ptr | TAINT | +| smart_pointer.cpp:117:11:117:16 | call to source | smart_pointer.cpp:117:2:117:18 | ... = ... | | +| smart_pointer.cpp:120:48:120:50 | ptr | smart_pointer.cpp:120:48:120:50 | ptr | | +| smart_pointer.cpp:120:48:120:50 | ptr | smart_pointer.cpp:121:4:121:6 | ptr | | +| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:120:48:120:50 | ptr | | +| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:121:4:121:6 | ptr [inner post update] | | +| smart_pointer.cpp:121:3:121:17 | ... = ... | smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | | +| smart_pointer.cpp:121:4:121:6 | ptr | smart_pointer.cpp:121:3:121:3 | call to operator* | | +| smart_pointer.cpp:121:4:121:6 | ref arg ptr | smart_pointer.cpp:120:48:120:50 | ptr | | +| smart_pointer.cpp:121:4:121:6 | ref arg ptr | smart_pointer.cpp:121:4:121:6 | ptr [inner post update] | | +| smart_pointer.cpp:121:10:121:15 | call to source | smart_pointer.cpp:121:3:121:17 | ... = ... | | +| smart_pointer.cpp:124:48:124:49 | p1 | smart_pointer.cpp:124:48:124:49 | p1 | | +| smart_pointer.cpp:124:48:124:49 | p1 | smart_pointer.cpp:125:18:125:19 | p1 | | +| smart_pointer.cpp:124:48:124:49 | p1 | smart_pointer.cpp:126:8:126:9 | p1 | | +| smart_pointer.cpp:124:90:124:91 | p2 | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:124:90:124:91 | p2 | smart_pointer.cpp:128:14:128:15 | p2 | | +| smart_pointer.cpp:124:90:124:91 | p2 | smart_pointer.cpp:129:10:129:11 | p2 | | +| smart_pointer.cpp:125:18:125:19 | p1 | smart_pointer.cpp:125:20:125:20 | call to operator-> | | +| smart_pointer.cpp:125:18:125:19 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | | +| smart_pointer.cpp:125:18:125:19 | ref arg p1 | smart_pointer.cpp:126:8:126:9 | p1 | | +| smart_pointer.cpp:125:18:125:22 | ref arg call to shared_ptr | smart_pointer.cpp:125:22:125:22 | q [inner post update] | | +| smart_pointer.cpp:125:20:125:20 | call to operator-> [post update] | smart_pointer.cpp:125:18:125:19 | ref arg p1 | TAINT | +| smart_pointer.cpp:125:22:125:22 | q | smart_pointer.cpp:125:18:125:22 | call to shared_ptr | | +| smart_pointer.cpp:125:22:125:22 | ref arg q | smart_pointer.cpp:125:22:125:22 | q [inner post update] | | +| smart_pointer.cpp:126:8:126:9 | p1 | smart_pointer.cpp:126:10:126:10 | call to operator-> | | +| smart_pointer.cpp:126:8:126:9 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | | +| smart_pointer.cpp:126:10:126:10 | call to operator-> [post update] | smart_pointer.cpp:126:8:126:9 | ref arg p1 | TAINT | +| smart_pointer.cpp:126:12:126:12 | q | smart_pointer.cpp:126:13:126:13 | call to operator-> | | +| smart_pointer.cpp:128:13:128:13 | call to operator* | smart_pointer.cpp:128:13:128:15 | call to shared_ptr | TAINT | +| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | | +| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:14:128:15 | p2 [inner post update] | | +| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | p2 | | +| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | | +| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:128:14:128:15 | p2 [inner post update] | | +| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:129:10:129:11 | p2 | | +| smart_pointer.cpp:128:14:128:15 | p2 | smart_pointer.cpp:128:13:128:13 | call to operator* | TAINT | +| smart_pointer.cpp:128:14:128:15 | ref arg p2 | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:128:14:128:15 | ref arg p2 | smart_pointer.cpp:128:14:128:15 | p2 [inner post update] | | +| smart_pointer.cpp:128:14:128:15 | ref arg p2 | smart_pointer.cpp:129:10:129:11 | p2 | | +| smart_pointer.cpp:129:9:129:9 | call to operator* | smart_pointer.cpp:129:8:129:8 | call to operator* | TAINT | +| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | p2 [inner post update] | | +| smart_pointer.cpp:129:10:129:11 | p2 | smart_pointer.cpp:129:8:129:8 | call to operator* | | +| smart_pointer.cpp:129:10:129:11 | p2 | smart_pointer.cpp:129:9:129:9 | call to operator* | TAINT | +| smart_pointer.cpp:129:10:129:11 | ref arg p2 | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:129:10:129:11 | ref arg p2 | smart_pointer.cpp:129:10:129:11 | p2 [inner post update] | | +| smart_pointer.cpp:132:53:132:54 | p1 | smart_pointer.cpp:132:53:132:54 | p1 | | +| smart_pointer.cpp:132:53:132:54 | p1 | smart_pointer.cpp:133:23:133:24 | p1 | | +| smart_pointer.cpp:132:53:132:54 | p1 | smart_pointer.cpp:134:8:134:9 | p1 | | +| smart_pointer.cpp:132:95:132:96 | p2 | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:132:95:132:96 | p2 | smart_pointer.cpp:136:18:136:19 | p2 | | +| smart_pointer.cpp:132:95:132:96 | p2 | smart_pointer.cpp:137:10:137:11 | p2 | | +| smart_pointer.cpp:133:23:133:24 | p1 | smart_pointer.cpp:133:25:133:25 | call to operator-> | | +| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | | +| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:134:8:134:9 | p1 | | +| smart_pointer.cpp:133:25:133:25 | call to operator-> [post update] | smart_pointer.cpp:133:23:133:24 | ref arg p1 | TAINT | +| smart_pointer.cpp:134:8:134:9 | p1 | smart_pointer.cpp:134:10:134:10 | call to operator-> | | +| smart_pointer.cpp:134:8:134:9 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | | +| smart_pointer.cpp:134:10:134:10 | call to operator-> [post update] | smart_pointer.cpp:134:8:134:9 | ref arg p1 | TAINT | +| smart_pointer.cpp:134:12:134:12 | q | smart_pointer.cpp:134:13:134:13 | call to operator-> | | +| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:136:18:136:19 | p2 [inner post update] | | +| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | p2 | | +| smart_pointer.cpp:136:18:136:19 | p2 | smart_pointer.cpp:136:17:136:17 | call to operator* | TAINT | +| smart_pointer.cpp:136:18:136:19 | ref arg p2 | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:136:18:136:19 | ref arg p2 | smart_pointer.cpp:136:18:136:19 | p2 [inner post update] | | +| smart_pointer.cpp:136:18:136:19 | ref arg p2 | smart_pointer.cpp:137:10:137:11 | p2 | | +| smart_pointer.cpp:137:9:137:9 | call to operator* | smart_pointer.cpp:137:8:137:8 | call to operator* | TAINT | +| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | p2 [inner post update] | | +| smart_pointer.cpp:137:10:137:11 | p2 | smart_pointer.cpp:137:8:137:8 | call to operator* | | +| smart_pointer.cpp:137:10:137:11 | p2 | smart_pointer.cpp:137:9:137:9 | call to operator* | TAINT | +| smart_pointer.cpp:137:10:137:11 | ref arg p2 | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:137:10:137:11 | ref arg p2 | smart_pointer.cpp:137:10:137:11 | p2 [inner post update] | | | standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:39:45:39:51 | source1 | | | standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:40:11:40:17 | source1 | | | standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:41:12:41:18 | source1 | | @@ -3388,125 +3560,125 @@ | stl.h:292:30:292:40 | call to allocator | stl.h:292:21:292:41 | noexcept(...) | TAINT | | stl.h:292:30:292:40 | call to allocator | stl.h:292:21:292:41 | noexcept(...) | TAINT | | stl.h:292:53:292:63 | 0 | stl.h:292:46:292:64 | (no string representation) | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:6 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:36:395:43 | call to unknown function | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:46:395:54 | call to unknown function | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:6 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:36:396:43 | call to unknown function | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:46:396:54 | call to unknown function | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | | | string.cpp:25:12:25:17 | call to source | string.cpp:29:7:29:7 | a | | | string.cpp:26:16:26:20 | 123 | string.cpp:26:16:26:21 | call to basic_string | TAINT | | string.cpp:26:16:26:21 | call to basic_string | string.cpp:30:7:30:7 | b | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp index f45260228e8..39cff39e3ed 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp @@ -35,16 +35,16 @@ void test_reverse_taint_shared() { std::shared_ptr p = std::make_shared(); *p = source(); - sink(p); // $ MISSING: ast,ir - sink(*p); // $ MISSING: ast,ir + sink(p); // $ ast MISSING: ir + sink(*p); // $ ast MISSING: ir } void test_reverse_taint_unique() { std::unique_ptr p = std::unique_ptr(); *p = source(); - sink(p); // $ MISSING: ast,ir - sink(*p); // $ MISSING: ast,ir + sink(p); // $ ast MISSING: ir + sink(*p); // $ ast MISSING: ir } void test_shared_get() { @@ -65,4 +65,74 @@ void test_shared_field_member() { std::unique_ptr p = std::make_unique(source(), 0); sink(p->x); // $ MISSING: ast,ir sink(p->y); // not tainted +} + +void getNumber(std::shared_ptr ptr) { + *ptr = source(); +} + +int test_from_issue_5190() { + std::shared_ptr p(new int); + getNumber(p); + sink(*p); // $ ast MISSING: ir +} + +struct B { + A a1; + A a2; + int z; +}; + +void test_operator_arrow(std::unique_ptr p, std::unique_ptr q) { + p->x = source(); + sink(p->x); // $ ast MISSING: ir + sink(p->y); + + q->a1.x = source(); + sink(q->a1.x); // $ ast MISSING: ir + sink(q->a1.y); + sink(q->a2.x); +} + +void taint_x(A* pa) { + pa->x = source(); +} + +void reverse_taint_smart_pointer() { + std::unique_ptr p = std::unique_ptr(new A); + taint_x(p.get()); + sink(p->x); // $ ast MISSING: ir +} + +struct C { + int z; + std::shared_ptr q; +}; + +void taint_x_shared(std::shared_ptr ptr) { + ptr->x = source(); +} + +void taint_x_shared_cref(const std::shared_ptr& ptr) { + ptr->x = source(); +} + +void getNumberCRef(const std::shared_ptr& ptr) { + *ptr = source(); +} + +int nested_shared_ptr_taint(std::shared_ptr p1, std::unique_ptr> p2) { + taint_x_shared(p1->q); + sink(p1->q->x); // $ ast MISSING: ir + + getNumber(*p2); + sink(**p2); // $ ast MISSING: ir +} + +int nested_shared_ptr_taint_cref(std::shared_ptr p1, std::unique_ptr> p2) { + taint_x_shared_cref(p1->q); + sink(p1->q->x); // $ ast MISSING: ir + + getNumberCRef(*p2); + sink(**p2); // $ ast MISSING: ir } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h index 09e77c5a3b6..44550c3df76 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h @@ -349,6 +349,7 @@ namespace std { public: shared_ptr() noexcept; explicit shared_ptr(T*); + shared_ptr(const shared_ptr&) noexcept; template shared_ptr(const shared_ptr&) noexcept; template shared_ptr(shared_ptr&&) noexcept; diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index a7dac56cc96..e008e2de659 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -42,7 +42,7 @@ bad_asts.cpp: # 16| r16_3(int) = Constant[1] : # 16| r16_4(int) = Call[MemberFunction] : func:r16_2, this:r16_1, 0:r16_3 # 16| mu16_5(unknown) = ^CallSideEffect : ~m? -# 16| v16_6(void) = ^BufferReadSideEffect[-1] : &:r16_1, ~m? +# 16| v16_6(void) = ^IndirectReadSideEffect[-1] : &:r16_1, ~m? # 16| mu16_7(S) = ^IndirectMayWriteSideEffect[-1] : &:r16_1 # 17| v17_1(void) = NoOp : # 14| v14_4(void) = ReturnVoid : @@ -3385,47 +3385,46 @@ ir.cpp: # 622| void CallMethods(String&, String*, String) # 622| Block 0 -# 622| v622_1(void) = EnterFunction : -# 622| mu622_2(unknown) = AliasedDefinition : -# 622| mu622_3(unknown) = InitializeNonLocal : -# 622| r622_4(glval) = VariableAddress[r] : -# 622| mu622_5(String &) = InitializeParameter[r] : &:r622_4 -# 622| r622_6(String &) = Load[r] : &:r622_4, ~m? -# 622| mu622_7(unknown) = InitializeIndirection[r] : &:r622_6 -# 622| r622_8(glval) = VariableAddress[p] : -# 622| mu622_9(String *) = InitializeParameter[p] : &:r622_8 -# 622| r622_10(String *) = Load[p] : &:r622_8, ~m? -# 622| mu622_11(unknown) = InitializeIndirection[p] : &:r622_10 -# 622| r622_12(glval) = VariableAddress[s] : -# 622| mu622_13(String) = InitializeParameter[s] : &:r622_12 -# 623| r623_1(glval) = VariableAddress[r] : -# 623| r623_2(String &) = Load[r] : &:r623_1, ~m? -# 623| r623_3(glval) = CopyValue : r623_2 -# 623| r623_4(glval) = Convert : r623_3 -# 623| r623_5(glval) = FunctionAddress[c_str] : -# 623| r623_6(char *) = Call[c_str] : func:r623_5, this:r623_4 -# 623| mu623_7(unknown) = ^CallSideEffect : ~m? -# 623| v623_8(void) = ^BufferReadSideEffect[-1] : &:r623_4, ~m? -# 624| r624_1(glval) = VariableAddress[p] : -# 624| r624_2(String *) = Load[p] : &:r624_1, ~m? -# 624| r624_3(String *) = Convert : r624_2 -# 624| r624_4(glval) = FunctionAddress[c_str] : -# 624| r624_5(char *) = Call[c_str] : func:r624_4, this:r624_3 -# 624| mu624_6(unknown) = ^CallSideEffect : ~m? -# 624| v624_7(void) = ^BufferReadSideEffect[-1] : &:r624_3, ~m? -# 624| mu624_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r624_3 -# 625| r625_1(glval) = VariableAddress[s] : -# 625| r625_2(glval) = Convert : r625_1 -# 625| r625_3(glval) = FunctionAddress[c_str] : -# 625| r625_4(char *) = Call[c_str] : func:r625_3, this:r625_2 -# 625| mu625_5(unknown) = ^CallSideEffect : ~m? -# 625| v625_6(void) = ^BufferReadSideEffect[-1] : &:r625_2, ~m? -# 626| v626_1(void) = NoOp : -# 622| v622_14(void) = ReturnIndirection[r] : &:r622_6, ~m? -# 622| v622_15(void) = ReturnIndirection[p] : &:r622_10, ~m? -# 622| v622_16(void) = ReturnVoid : -# 622| v622_17(void) = AliasedUse : ~m? -# 622| v622_18(void) = ExitFunction : +# 622| v622_1(void) = EnterFunction : +# 622| mu622_2(unknown) = AliasedDefinition : +# 622| mu622_3(unknown) = InitializeNonLocal : +# 622| r622_4(glval) = VariableAddress[r] : +# 622| mu622_5(String &) = InitializeParameter[r] : &:r622_4 +# 622| r622_6(String &) = Load[r] : &:r622_4, ~m? +# 622| mu622_7(unknown) = InitializeIndirection[r] : &:r622_6 +# 622| r622_8(glval) = VariableAddress[p] : +# 622| mu622_9(String *) = InitializeParameter[p] : &:r622_8 +# 622| r622_10(String *) = Load[p] : &:r622_8, ~m? +# 622| mu622_11(unknown) = InitializeIndirection[p] : &:r622_10 +# 622| r622_12(glval) = VariableAddress[s] : +# 622| mu622_13(String) = InitializeParameter[s] : &:r622_12 +# 623| r623_1(glval) = VariableAddress[r] : +# 623| r623_2(String &) = Load[r] : &:r623_1, ~m? +# 623| r623_3(glval) = CopyValue : r623_2 +# 623| r623_4(glval) = Convert : r623_3 +# 623| r623_5(glval) = FunctionAddress[c_str] : +# 623| r623_6(char *) = Call[c_str] : func:r623_5, this:r623_4 +# 623| mu623_7(unknown) = ^CallSideEffect : ~m? +# 623| v623_8(void) = ^IndirectReadSideEffect[-1] : &:r623_4, ~m? +# 624| r624_1(glval) = VariableAddress[p] : +# 624| r624_2(String *) = Load[p] : &:r624_1, ~m? +# 624| r624_3(String *) = Convert : r624_2 +# 624| r624_4(glval) = FunctionAddress[c_str] : +# 624| r624_5(char *) = Call[c_str] : func:r624_4, this:r624_3 +# 624| mu624_6(unknown) = ^CallSideEffect : ~m? +# 624| v624_7(void) = ^IndirectReadSideEffect[-1] : &:r624_3, ~m? +# 625| r625_1(glval) = VariableAddress[s] : +# 625| r625_2(glval) = Convert : r625_1 +# 625| r625_3(glval) = FunctionAddress[c_str] : +# 625| r625_4(char *) = Call[c_str] : func:r625_3, this:r625_2 +# 625| mu625_5(unknown) = ^CallSideEffect : ~m? +# 625| v625_6(void) = ^IndirectReadSideEffect[-1] : &:r625_2, ~m? +# 626| v626_1(void) = NoOp : +# 622| v622_14(void) = ReturnIndirection[r] : &:r622_6, ~m? +# 622| v622_15(void) = ReturnIndirection[p] : &:r622_10, ~m? +# 622| v622_16(void) = ReturnVoid : +# 622| v622_17(void) = AliasedUse : ~m? +# 622| v622_18(void) = ExitFunction : # 628| void C::~C() # 628| Block 0 @@ -3575,7 +3574,7 @@ ir.cpp: # 653| r653_4(int) = Constant[0] : # 653| r653_5(int) = Call[InstanceMemberFunction] : func:r653_3, this:r653_2, 0:r653_4 # 653| mu653_6(unknown) = ^CallSideEffect : ~m? -# 653| v653_7(void) = ^BufferReadSideEffect[-1] : &:r653_2, ~m? +# 653| v653_7(void) = ^IndirectReadSideEffect[-1] : &:r653_2, ~m? # 653| mu653_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r653_2 # 654| r654_1(glval) = VariableAddress[#this] : # 654| r654_2(C *) = Load[#this] : &:r654_1, ~m? @@ -3584,7 +3583,7 @@ ir.cpp: # 654| r654_5(int) = Constant[1] : # 654| r654_6(int) = Call[InstanceMemberFunction] : func:r654_4, this:r654_3, 0:r654_5 # 654| mu654_7(unknown) = ^CallSideEffect : ~m? -# 654| v654_8(void) = ^BufferReadSideEffect[-1] : &:r654_3, ~m? +# 654| v654_8(void) = ^IndirectReadSideEffect[-1] : &:r654_3, ~m? # 654| mu654_9(C) = ^IndirectMayWriteSideEffect[-1] : &:r654_3 # 655| r655_1(glval) = VariableAddress[#this] : # 655| r655_2(C *) = Load[#this] : &:r655_1, ~m? @@ -3592,7 +3591,7 @@ ir.cpp: # 655| r655_4(int) = Constant[2] : # 655| r655_5(int) = Call[InstanceMemberFunction] : func:r655_3, this:r655_2, 0:r655_4 # 655| mu655_6(unknown) = ^CallSideEffect : ~m? -# 655| v655_7(void) = ^BufferReadSideEffect[-1] : &:r655_2, ~m? +# 655| v655_7(void) = ^IndirectReadSideEffect[-1] : &:r655_2, ~m? # 655| mu655_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r655_2 # 656| v656_1(void) = NoOp : # 652| v652_8(void) = ReturnIndirection[#this] : &:r652_6, ~m? @@ -4006,7 +4005,7 @@ ir.cpp: #-----| r0_6(String &) = CopyValue : r745_15 # 745| r745_16(String &) = Call[operator=] : func:r745_12, this:r745_11, 0:r0_6 # 745| mu745_17(unknown) = ^CallSideEffect : ~m? -# 745| v745_18(void) = ^BufferReadSideEffect[-1] : &:r745_11, ~m? +# 745| v745_18(void) = ^IndirectReadSideEffect[-1] : &:r745_11, ~m? #-----| v0_7(void) = ^BufferReadSideEffect[0] : &:r0_6, ~m? # 745| mu745_19(String) = ^IndirectMayWriteSideEffect[-1] : &:r745_11 #-----| r0_8(glval) = CopyValue : r745_16 @@ -4113,7 +4112,7 @@ ir.cpp: #-----| r0_8(Base &) = CopyValue : r754_14 # 754| r754_15(Base &) = Call[operator=] : func:r754_10, this:r0_5, 0:r0_8 # 754| mu754_16(unknown) = ^CallSideEffect : ~m? -#-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? +#-----| v0_9(void) = ^IndirectReadSideEffect[-1] : &:r0_5, ~m? #-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m? #-----| mu0_11(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_5 #-----| r0_12(glval) = CopyValue : r754_15 @@ -4129,7 +4128,7 @@ ir.cpp: #-----| r0_14(String &) = CopyValue : r754_24 # 754| r754_25(String &) = Call[operator=] : func:r754_21, this:r754_20, 0:r0_14 # 754| mu754_26(unknown) = ^CallSideEffect : ~m? -# 754| v754_27(void) = ^BufferReadSideEffect[-1] : &:r754_20, ~m? +# 754| v754_27(void) = ^IndirectReadSideEffect[-1] : &:r754_20, ~m? #-----| v0_15(void) = ^BufferReadSideEffect[0] : &:r0_14, ~m? # 754| mu754_28(String) = ^IndirectMayWriteSideEffect[-1] : &:r754_20 #-----| r0_16(glval) = CopyValue : r754_25 @@ -4220,7 +4219,7 @@ ir.cpp: #-----| r0_8(Middle &) = CopyValue : r763_14 # 763| r763_15(Middle &) = Call[operator=] : func:r763_10, this:r0_5, 0:r0_8 # 763| mu763_16(unknown) = ^CallSideEffect : ~m? -#-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? +#-----| v0_9(void) = ^IndirectReadSideEffect[-1] : &:r0_5, ~m? #-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m? #-----| mu0_11(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r0_5 #-----| r0_12(glval) = CopyValue : r763_15 @@ -4236,7 +4235,7 @@ ir.cpp: #-----| r0_14(String &) = CopyValue : r763_24 # 763| r763_25(String &) = Call[operator=] : func:r763_21, this:r763_20, 0:r0_14 # 763| mu763_26(unknown) = ^CallSideEffect : ~m? -# 763| v763_27(void) = ^BufferReadSideEffect[-1] : &:r763_20, ~m? +# 763| v763_27(void) = ^IndirectReadSideEffect[-1] : &:r763_20, ~m? #-----| v0_15(void) = ^BufferReadSideEffect[0] : &:r0_14, ~m? # 763| mu763_28(String) = ^IndirectMayWriteSideEffect[-1] : &:r763_20 #-----| r0_16(glval) = CopyValue : r763_25 @@ -4505,7 +4504,7 @@ ir.cpp: # 808| r808_5(Base &) = CopyValue : r808_4 # 808| r808_6(Base &) = Call[operator=] : func:r808_2, this:r808_1, 0:r808_5 # 808| mu808_7(unknown) = ^CallSideEffect : ~m? -# 808| v808_8(void) = ^BufferReadSideEffect[-1] : &:r808_1, ~m? +# 808| v808_8(void) = ^IndirectReadSideEffect[-1] : &:r808_1, ~m? # 808| v808_9(void) = ^BufferReadSideEffect[0] : &:r808_5, ~m? # 808| mu808_10(Base) = ^IndirectMayWriteSideEffect[-1] : &:r808_1 # 808| r808_11(glval) = CopyValue : r808_6 @@ -4525,7 +4524,7 @@ ir.cpp: # 809| r809_14(Base &) = CopyValue : r809_13 # 809| r809_15(Base &) = Call[operator=] : func:r809_2, this:r809_1, 0:r809_14 # 809| mu809_16(unknown) = ^CallSideEffect : ~m? -# 809| v809_17(void) = ^BufferReadSideEffect[-1] : &:r809_1, ~m? +# 809| v809_17(void) = ^IndirectReadSideEffect[-1] : &:r809_1, ~m? # 809| v809_18(void) = ^BufferReadSideEffect[0] : &:r809_14, ~m? # 809| mu809_19(Base) = ^IndirectMayWriteSideEffect[-1] : &:r809_1 # 809| r809_20(glval) = CopyValue : r809_15 @@ -4545,7 +4544,7 @@ ir.cpp: # 810| r810_14(Base &) = CopyValue : r810_13 # 810| r810_15(Base &) = Call[operator=] : func:r810_2, this:r810_1, 0:r810_14 # 810| mu810_16(unknown) = ^CallSideEffect : ~m? -# 810| v810_17(void) = ^BufferReadSideEffect[-1] : &:r810_1, ~m? +# 810| v810_17(void) = ^IndirectReadSideEffect[-1] : &:r810_1, ~m? # 810| v810_18(void) = ^BufferReadSideEffect[0] : &:r810_14, ~m? # 810| mu810_19(Base) = ^IndirectMayWriteSideEffect[-1] : &:r810_1 # 810| r810_20(glval) = CopyValue : r810_15 @@ -4577,7 +4576,7 @@ ir.cpp: # 816| r816_6(Middle &) = CopyValue : r816_5 # 816| r816_7(Middle &) = Call[operator=] : func:r816_2, this:r816_1, 0:r816_6 # 816| mu816_8(unknown) = ^CallSideEffect : ~m? -# 816| v816_9(void) = ^BufferReadSideEffect[-1] : &:r816_1, ~m? +# 816| v816_9(void) = ^IndirectReadSideEffect[-1] : &:r816_1, ~m? # 816| v816_10(void) = ^BufferReadSideEffect[0] : &:r816_6, ~m? # 816| mu816_11(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r816_1 # 816| r816_12(glval) = CopyValue : r816_7 @@ -4589,7 +4588,7 @@ ir.cpp: # 817| r817_6(Middle &) = CopyValue : r817_5 # 817| r817_7(Middle &) = Call[operator=] : func:r817_2, this:r817_1, 0:r817_6 # 817| mu817_8(unknown) = ^CallSideEffect : ~m? -# 817| v817_9(void) = ^BufferReadSideEffect[-1] : &:r817_1, ~m? +# 817| v817_9(void) = ^IndirectReadSideEffect[-1] : &:r817_1, ~m? # 817| v817_10(void) = ^BufferReadSideEffect[0] : &:r817_6, ~m? # 817| mu817_11(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r817_1 # 817| r817_12(glval) = CopyValue : r817_7 @@ -4616,7 +4615,7 @@ ir.cpp: # 822| r822_6(Base &) = CopyValue : r822_5 # 822| r822_7(Base &) = Call[operator=] : func:r822_2, this:r822_1, 0:r822_6 # 822| mu822_8(unknown) = ^CallSideEffect : ~m? -# 822| v822_9(void) = ^BufferReadSideEffect[-1] : &:r822_1, ~m? +# 822| v822_9(void) = ^IndirectReadSideEffect[-1] : &:r822_1, ~m? # 822| v822_10(void) = ^BufferReadSideEffect[0] : &:r822_6, ~m? # 822| mu822_11(Base) = ^IndirectMayWriteSideEffect[-1] : &:r822_1 # 822| r822_12(glval) = CopyValue : r822_7 @@ -4637,7 +4636,7 @@ ir.cpp: # 823| r823_15(Base &) = CopyValue : r823_14 # 823| r823_16(Base &) = Call[operator=] : func:r823_2, this:r823_1, 0:r823_15 # 823| mu823_17(unknown) = ^CallSideEffect : ~m? -# 823| v823_18(void) = ^BufferReadSideEffect[-1] : &:r823_1, ~m? +# 823| v823_18(void) = ^IndirectReadSideEffect[-1] : &:r823_1, ~m? # 823| v823_19(void) = ^BufferReadSideEffect[0] : &:r823_15, ~m? # 823| mu823_20(Base) = ^IndirectMayWriteSideEffect[-1] : &:r823_1 # 823| r823_21(glval) = CopyValue : r823_16 @@ -4658,7 +4657,7 @@ ir.cpp: # 824| r824_15(Base &) = CopyValue : r824_14 # 824| r824_16(Base &) = Call[operator=] : func:r824_2, this:r824_1, 0:r824_15 # 824| mu824_17(unknown) = ^CallSideEffect : ~m? -# 824| v824_18(void) = ^BufferReadSideEffect[-1] : &:r824_1, ~m? +# 824| v824_18(void) = ^IndirectReadSideEffect[-1] : &:r824_1, ~m? # 824| v824_19(void) = ^BufferReadSideEffect[0] : &:r824_15, ~m? # 824| mu824_20(Base) = ^IndirectMayWriteSideEffect[-1] : &:r824_1 # 824| r824_21(glval) = CopyValue : r824_16 @@ -4694,7 +4693,7 @@ ir.cpp: # 830| r830_7(Derived &) = CopyValue : r830_6 # 830| r830_8(Derived &) = Call[operator=] : func:r830_2, this:r830_1, 0:r830_7 # 830| mu830_9(unknown) = ^CallSideEffect : ~m? -# 830| v830_10(void) = ^BufferReadSideEffect[-1] : &:r830_1, ~m? +# 830| v830_10(void) = ^IndirectReadSideEffect[-1] : &:r830_1, ~m? # 830| v830_11(void) = ^BufferReadSideEffect[0] : &:r830_7, ~m? # 830| mu830_12(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r830_1 # 830| r830_13(glval) = CopyValue : r830_8 @@ -4707,7 +4706,7 @@ ir.cpp: # 831| r831_7(Derived &) = CopyValue : r831_6 # 831| r831_8(Derived &) = Call[operator=] : func:r831_2, this:r831_1, 0:r831_7 # 831| mu831_9(unknown) = ^CallSideEffect : ~m? -# 831| v831_10(void) = ^BufferReadSideEffect[-1] : &:r831_1, ~m? +# 831| v831_10(void) = ^IndirectReadSideEffect[-1] : &:r831_1, ~m? # 831| v831_11(void) = ^BufferReadSideEffect[0] : &:r831_7, ~m? # 831| mu831_12(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r831_1 # 831| r831_13(glval) = CopyValue : r831_8 @@ -5688,7 +5687,7 @@ ir.cpp: # 1044| r1044_4(float) = Constant[1.0] : # 1044| r1044_5(char) = Call[operator()] : func:r1044_3, this:r1044_2, 0:r1044_4 # 1044| mu1044_6(unknown) = ^CallSideEffect : ~m? -# 1044| v1044_7(void) = ^BufferReadSideEffect[-1] : &:r1044_2, ~m? +# 1044| v1044_7(void) = ^IndirectReadSideEffect[-1] : &:r1044_2, ~m? # 1045| r1045_1(glval) = VariableAddress[lambda_val] : # 1045| r1045_2(glval) = VariableAddress[#temp1045:20] : # 1045| mu1045_3(decltype([...](...){...})) = Uninitialized[#temp1045:20] : &:r1045_2 @@ -5709,7 +5708,7 @@ ir.cpp: # 1046| r1046_4(float) = Constant[2.0] : # 1046| r1046_5(char) = Call[operator()] : func:r1046_3, this:r1046_2, 0:r1046_4 # 1046| mu1046_6(unknown) = ^CallSideEffect : ~m? -# 1046| v1046_7(void) = ^BufferReadSideEffect[-1] : &:r1046_2, ~m? +# 1046| v1046_7(void) = ^IndirectReadSideEffect[-1] : &:r1046_2, ~m? # 1047| r1047_1(glval) = VariableAddress[lambda_ref_explicit] : # 1047| r1047_2(glval) = VariableAddress[#temp1047:29] : # 1047| mu1047_3(decltype([...](...){...})) = Uninitialized[#temp1047:29] : &:r1047_2 @@ -5727,7 +5726,7 @@ ir.cpp: # 1048| r1048_4(float) = Constant[3.0] : # 1048| r1048_5(char) = Call[operator()] : func:r1048_3, this:r1048_2, 0:r1048_4 # 1048| mu1048_6(unknown) = ^CallSideEffect : ~m? -# 1048| v1048_7(void) = ^BufferReadSideEffect[-1] : &:r1048_2, ~m? +# 1048| v1048_7(void) = ^IndirectReadSideEffect[-1] : &:r1048_2, ~m? # 1049| r1049_1(glval) = VariableAddress[lambda_val_explicit] : # 1049| r1049_2(glval) = VariableAddress[#temp1049:29] : # 1049| mu1049_3(decltype([...](...){...})) = Uninitialized[#temp1049:29] : &:r1049_2 @@ -5744,7 +5743,7 @@ ir.cpp: # 1050| r1050_4(float) = Constant[4.0] : # 1050| r1050_5(char) = Call[operator()] : func:r1050_3, this:r1050_2, 0:r1050_4 # 1050| mu1050_6(unknown) = ^CallSideEffect : ~m? -# 1050| v1050_7(void) = ^BufferReadSideEffect[-1] : &:r1050_2, ~m? +# 1050| v1050_7(void) = ^IndirectReadSideEffect[-1] : &:r1050_2, ~m? # 1051| r1051_1(glval) = VariableAddress[lambda_mixed_explicit] : # 1051| r1051_2(glval) = VariableAddress[#temp1051:31] : # 1051| mu1051_3(decltype([...](...){...})) = Uninitialized[#temp1051:31] : &:r1051_2 @@ -5766,7 +5765,7 @@ ir.cpp: # 1052| r1052_4(float) = Constant[5.0] : # 1052| r1052_5(char) = Call[operator()] : func:r1052_3, this:r1052_2, 0:r1052_4 # 1052| mu1052_6(unknown) = ^CallSideEffect : ~m? -# 1052| v1052_7(void) = ^BufferReadSideEffect[-1] : &:r1052_2, ~m? +# 1052| v1052_7(void) = ^IndirectReadSideEffect[-1] : &:r1052_2, ~m? # 1053| r1053_1(glval) = VariableAddress[r] : # 1053| r1053_2(glval) = VariableAddress[x] : # 1053| r1053_3(int) = Load[x] : &:r1053_2, ~m? @@ -5804,7 +5803,7 @@ ir.cpp: # 1055| r1055_4(float) = Constant[6.0] : # 1055| r1055_5(char) = Call[operator()] : func:r1055_3, this:r1055_2, 0:r1055_4 # 1055| mu1055_6(unknown) = ^CallSideEffect : ~m? -# 1055| v1055_7(void) = ^BufferReadSideEffect[-1] : &:r1055_2, ~m? +# 1055| v1055_7(void) = ^IndirectReadSideEffect[-1] : &:r1055_2, ~m? # 1056| v1056_1(void) = NoOp : # 1040| v1040_10(void) = ReturnIndirection[s] : &:r1040_8, ~m? # 1040| v1040_11(void) = ReturnVoid : @@ -5869,7 +5868,7 @@ ir.cpp: # 1043| r1043_16(glval) = FunctionAddress[c_str] : # 1043| r1043_17(char *) = Call[c_str] : func:r1043_16, this:r1043_15 # 1043| mu1043_18(unknown) = ^CallSideEffect : ~m? -# 1043| v1043_19(void) = ^BufferReadSideEffect[-1] : &:r1043_15, ~m? +# 1043| v1043_19(void) = ^IndirectReadSideEffect[-1] : &:r1043_15, ~m? # 1043| r1043_20(glval) = VariableAddress[#this] : # 1043| r1043_21(lambda [] type at line 1043, col. 21 *) = Load[#this] : &:r1043_20, ~m? # 1043| r1043_22(glval) = FieldAddress[x] : r1043_21 @@ -5921,7 +5920,7 @@ ir.cpp: # 1045| r1045_14(glval) = FunctionAddress[c_str] : # 1045| r1045_15(char *) = Call[c_str] : func:r1045_14, this:r1045_13 # 1045| mu1045_16(unknown) = ^CallSideEffect : ~m? -# 1045| v1045_17(void) = ^BufferReadSideEffect[-1] : &:r1045_13, ~m? +# 1045| v1045_17(void) = ^IndirectReadSideEffect[-1] : &:r1045_13, ~m? # 1045| r1045_18(glval) = VariableAddress[#this] : # 1045| r1045_19(lambda [] type at line 1045, col. 21 *) = Load[#this] : &:r1045_18, ~m? # 1045| r1045_20(glval) = FieldAddress[x] : r1045_19 @@ -5955,7 +5954,7 @@ ir.cpp: # 1047| r1047_16(glval) = FunctionAddress[c_str] : # 1047| r1047_17(char *) = Call[c_str] : func:r1047_16, this:r1047_15 # 1047| mu1047_18(unknown) = ^CallSideEffect : ~m? -# 1047| v1047_19(void) = ^BufferReadSideEffect[-1] : &:r1047_15, ~m? +# 1047| v1047_19(void) = ^IndirectReadSideEffect[-1] : &:r1047_15, ~m? # 1047| r1047_20(int) = Constant[0] : # 1047| r1047_21(glval) = PointerAdd[1] : r1047_17, r1047_20 # 1047| r1047_22(char) = Load[?] : &:r1047_21, ~m? @@ -6003,7 +6002,7 @@ ir.cpp: # 1049| r1049_14(glval) = FunctionAddress[c_str] : # 1049| r1049_15(char *) = Call[c_str] : func:r1049_14, this:r1049_13 # 1049| mu1049_16(unknown) = ^CallSideEffect : ~m? -# 1049| v1049_17(void) = ^BufferReadSideEffect[-1] : &:r1049_13, ~m? +# 1049| v1049_17(void) = ^IndirectReadSideEffect[-1] : &:r1049_13, ~m? # 1049| r1049_18(int) = Constant[0] : # 1049| r1049_19(glval) = PointerAdd[1] : r1049_15, r1049_18 # 1049| r1049_20(char) = Load[?] : &:r1049_19, ~m? @@ -6034,7 +6033,7 @@ ir.cpp: # 1051| r1051_16(glval) = FunctionAddress[c_str] : # 1051| r1051_17(char *) = Call[c_str] : func:r1051_16, this:r1051_15 # 1051| mu1051_18(unknown) = ^CallSideEffect : ~m? -# 1051| v1051_19(void) = ^BufferReadSideEffect[-1] : &:r1051_15, ~m? +# 1051| v1051_19(void) = ^IndirectReadSideEffect[-1] : &:r1051_15, ~m? # 1051| r1051_20(glval) = VariableAddress[#this] : # 1051| r1051_21(lambda [] type at line 1051, col. 32 *) = Load[#this] : &:r1051_20, ~m? # 1051| r1051_22(glval) = FieldAddress[x] : r1051_21 @@ -6068,7 +6067,7 @@ ir.cpp: # 1054| r1054_16(glval) = FunctionAddress[c_str] : # 1054| r1054_17(char *) = Call[c_str] : func:r1054_16, this:r1054_15 # 1054| mu1054_18(unknown) = ^CallSideEffect : ~m? -# 1054| v1054_19(void) = ^BufferReadSideEffect[-1] : &:r1054_15, ~m? +# 1054| v1054_19(void) = ^IndirectReadSideEffect[-1] : &:r1054_15, ~m? # 1054| r1054_20(glval) = VariableAddress[#this] : # 1054| r1054_21(lambda [] type at line 1054, col. 23 *) = Load[#this] : &:r1054_20, ~m? # 1054| r1054_22(glval) = FieldAddress[x] : r1054_21 @@ -6095,37 +6094,37 @@ ir.cpp: # 1077| void RangeBasedFor(vector const&) # 1077| Block 0 -# 1077| v1077_1(void) = EnterFunction : -# 1077| mu1077_2(unknown) = AliasedDefinition : -# 1077| mu1077_3(unknown) = InitializeNonLocal : -# 1077| r1077_4(glval &>) = VariableAddress[v] : -# 1077| mu1077_5(vector &) = InitializeParameter[v] : &:r1077_4 -# 1077| r1077_6(vector &) = Load[v] : &:r1077_4, ~m? -# 1077| mu1077_7(unknown) = InitializeIndirection[v] : &:r1077_6 -# 1078| r1078_1(glval &>) = VariableAddress[(__range)] : -# 1078| r1078_2(glval &>) = VariableAddress[v] : -# 1078| r1078_3(vector &) = Load[v] : &:r1078_2, ~m? -# 1078| r1078_4(glval>) = CopyValue : r1078_3 -# 1078| r1078_5(vector &) = CopyValue : r1078_4 -# 1078| mu1078_6(vector &) = Store[(__range)] : &:r1078_1, r1078_5 -# 1078| r1078_7(glval) = VariableAddress[(__begin)] : -# 1078| r1078_8(glval &>) = VariableAddress[(__range)] : -# 1078| r1078_9(vector &) = Load[(__range)] : &:r1078_8, ~m? -#-----| r0_1(glval>) = CopyValue : r1078_9 -# 1078| r1078_10(glval) = FunctionAddress[begin] : -# 1078| r1078_11(iterator) = Call[begin] : func:r1078_10, this:r0_1 -# 1078| mu1078_12(unknown) = ^CallSideEffect : ~m? -#-----| v0_2(void) = ^BufferReadSideEffect[-1] : &:r0_1, ~m? -# 1078| mu1078_13(iterator) = Store[(__begin)] : &:r1078_7, r1078_11 -# 1078| r1078_14(glval) = VariableAddress[(__end)] : -# 1078| r1078_15(glval &>) = VariableAddress[(__range)] : -# 1078| r1078_16(vector &) = Load[(__range)] : &:r1078_15, ~m? -#-----| r0_3(glval>) = CopyValue : r1078_16 -# 1078| r1078_17(glval) = FunctionAddress[end] : -# 1078| r1078_18(iterator) = Call[end] : func:r1078_17, this:r0_3 -# 1078| mu1078_19(unknown) = ^CallSideEffect : ~m? -#-----| v0_4(void) = ^BufferReadSideEffect[-1] : &:r0_3, ~m? -# 1078| mu1078_20(iterator) = Store[(__end)] : &:r1078_14, r1078_18 +# 1077| v1077_1(void) = EnterFunction : +# 1077| mu1077_2(unknown) = AliasedDefinition : +# 1077| mu1077_3(unknown) = InitializeNonLocal : +# 1077| r1077_4(glval &>) = VariableAddress[v] : +# 1077| mu1077_5(vector &) = InitializeParameter[v] : &:r1077_4 +# 1077| r1077_6(vector &) = Load[v] : &:r1077_4, ~m? +# 1077| mu1077_7(unknown) = InitializeIndirection[v] : &:r1077_6 +# 1078| r1078_1(glval &>) = VariableAddress[(__range)] : +# 1078| r1078_2(glval &>) = VariableAddress[v] : +# 1078| r1078_3(vector &) = Load[v] : &:r1078_2, ~m? +# 1078| r1078_4(glval>) = CopyValue : r1078_3 +# 1078| r1078_5(vector &) = CopyValue : r1078_4 +# 1078| mu1078_6(vector &) = Store[(__range)] : &:r1078_1, r1078_5 +# 1078| r1078_7(glval) = VariableAddress[(__begin)] : +# 1078| r1078_8(glval &>) = VariableAddress[(__range)] : +# 1078| r1078_9(vector &) = Load[(__range)] : &:r1078_8, ~m? +#-----| r0_1(glval>) = CopyValue : r1078_9 +# 1078| r1078_10(glval) = FunctionAddress[begin] : +# 1078| r1078_11(iterator) = Call[begin] : func:r1078_10, this:r0_1 +# 1078| mu1078_12(unknown) = ^CallSideEffect : ~m? +#-----| v0_2(void) = ^IndirectReadSideEffect[-1] : &:r0_1, ~m? +# 1078| mu1078_13(iterator) = Store[(__begin)] : &:r1078_7, r1078_11 +# 1078| r1078_14(glval) = VariableAddress[(__end)] : +# 1078| r1078_15(glval &>) = VariableAddress[(__range)] : +# 1078| r1078_16(vector &) = Load[(__range)] : &:r1078_15, ~m? +#-----| r0_3(glval>) = CopyValue : r1078_16 +# 1078| r1078_17(glval) = FunctionAddress[end] : +# 1078| r1078_18(iterator) = Call[end] : func:r1078_17, this:r0_3 +# 1078| mu1078_19(unknown) = ^CallSideEffect : ~m? +#-----| v0_4(void) = ^IndirectReadSideEffect[-1] : &:r0_3, ~m? +# 1078| mu1078_20(iterator) = Store[(__end)] : &:r1078_14, r1078_18 #-----| Goto -> Block 1 # 1078| Block 1 @@ -6136,26 +6135,26 @@ ir.cpp: # 1078| r1078_24(iterator) = Load[(__end)] : &:r1078_23, ~m? # 1078| r1078_25(bool) = Call[operator!=] : func:r1078_22, this:r0_5, 0:r1078_24 # 1078| mu1078_26(unknown) = ^CallSideEffect : ~m? -#-----| v0_6(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? +#-----| v0_6(void) = ^IndirectReadSideEffect[-1] : &:r0_5, ~m? # 1078| v1078_27(void) = ConditionalBranch : r1078_25 #-----| False -> Block 5 #-----| True -> Block 2 # 1078| Block 2 -# 1078| r1078_28(glval) = VariableAddress[e] : -# 1078| r1078_29(glval) = VariableAddress[(__begin)] : -#-----| r0_7(glval) = Convert : r1078_29 -# 1078| r1078_30(glval) = FunctionAddress[operator*] : -# 1078| r1078_31(int &) = Call[operator*] : func:r1078_30, this:r0_7 -# 1078| mu1078_32(unknown) = ^CallSideEffect : ~m? -#-----| v0_8(void) = ^BufferReadSideEffect[-1] : &:r0_7, ~m? -# 1078| r1078_33(int) = Load[?] : &:r1078_31, ~m? -# 1078| mu1078_34(int) = Store[e] : &:r1078_28, r1078_33 -# 1079| r1079_1(glval) = VariableAddress[e] : -# 1079| r1079_2(int) = Load[e] : &:r1079_1, ~m? -# 1079| r1079_3(int) = Constant[0] : -# 1079| r1079_4(bool) = CompareGT : r1079_2, r1079_3 -# 1079| v1079_5(void) = ConditionalBranch : r1079_4 +# 1078| r1078_28(glval) = VariableAddress[e] : +# 1078| r1078_29(glval) = VariableAddress[(__begin)] : +#-----| r0_7(glval) = Convert : r1078_29 +# 1078| r1078_30(glval) = FunctionAddress[operator*] : +# 1078| r1078_31(int &) = Call[operator*] : func:r1078_30, this:r0_7 +# 1078| mu1078_32(unknown) = ^CallSideEffect : ~m? +#-----| v0_8(void) = ^IndirectReadSideEffect[-1] : &:r0_7, ~m? +# 1078| r1078_33(int) = Load[?] : &:r1078_31, ~m? +# 1078| mu1078_34(int) = Store[e] : &:r1078_28, r1078_33 +# 1079| r1079_1(glval) = VariableAddress[e] : +# 1079| r1079_2(int) = Load[e] : &:r1079_1, ~m? +# 1079| r1079_3(int) = Constant[0] : +# 1079| r1079_4(bool) = CompareGT : r1079_2, r1079_3 +# 1079| v1079_5(void) = ConditionalBranch : r1079_4 #-----| False -> Block 4 #-----| True -> Block 3 @@ -6169,36 +6168,36 @@ ir.cpp: # 1078| r1078_37(glval) = FunctionAddress[operator++] : # 1078| r1078_38(iterator &) = Call[operator++] : func:r1078_37, this:r1078_36 # 1078| mu1078_39(unknown) = ^CallSideEffect : ~m? -# 1078| v1078_40(void) = ^BufferReadSideEffect[-1] : &:r1078_36, ~m? +# 1078| v1078_40(void) = ^IndirectReadSideEffect[-1] : &:r1078_36, ~m? # 1078| mu1078_41(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r1078_36 # 1078| r1078_42(glval) = CopyValue : r1078_38 #-----| Goto (back edge) -> Block 1 # 1084| Block 5 -# 1084| r1084_1(glval &>) = VariableAddress[(__range)] : -# 1084| r1084_2(glval &>) = VariableAddress[v] : -# 1084| r1084_3(vector &) = Load[v] : &:r1084_2, ~m? -# 1084| r1084_4(glval>) = CopyValue : r1084_3 -# 1084| r1084_5(vector &) = CopyValue : r1084_4 -# 1084| mu1084_6(vector &) = Store[(__range)] : &:r1084_1, r1084_5 -# 1084| r1084_7(glval) = VariableAddress[(__begin)] : -# 1084| r1084_8(glval &>) = VariableAddress[(__range)] : -# 1084| r1084_9(vector &) = Load[(__range)] : &:r1084_8, ~m? -#-----| r0_9(glval>) = CopyValue : r1084_9 -# 1084| r1084_10(glval) = FunctionAddress[begin] : -# 1084| r1084_11(iterator) = Call[begin] : func:r1084_10, this:r0_9 -# 1084| mu1084_12(unknown) = ^CallSideEffect : ~m? -#-----| v0_10(void) = ^BufferReadSideEffect[-1] : &:r0_9, ~m? -# 1084| mu1084_13(iterator) = Store[(__begin)] : &:r1084_7, r1084_11 -# 1084| r1084_14(glval) = VariableAddress[(__end)] : -# 1084| r1084_15(glval &>) = VariableAddress[(__range)] : -# 1084| r1084_16(vector &) = Load[(__range)] : &:r1084_15, ~m? -#-----| r0_11(glval>) = CopyValue : r1084_16 -# 1084| r1084_17(glval) = FunctionAddress[end] : -# 1084| r1084_18(iterator) = Call[end] : func:r1084_17, this:r0_11 -# 1084| mu1084_19(unknown) = ^CallSideEffect : ~m? -#-----| v0_12(void) = ^BufferReadSideEffect[-1] : &:r0_11, ~m? -# 1084| mu1084_20(iterator) = Store[(__end)] : &:r1084_14, r1084_18 +# 1084| r1084_1(glval &>) = VariableAddress[(__range)] : +# 1084| r1084_2(glval &>) = VariableAddress[v] : +# 1084| r1084_3(vector &) = Load[v] : &:r1084_2, ~m? +# 1084| r1084_4(glval>) = CopyValue : r1084_3 +# 1084| r1084_5(vector &) = CopyValue : r1084_4 +# 1084| mu1084_6(vector &) = Store[(__range)] : &:r1084_1, r1084_5 +# 1084| r1084_7(glval) = VariableAddress[(__begin)] : +# 1084| r1084_8(glval &>) = VariableAddress[(__range)] : +# 1084| r1084_9(vector &) = Load[(__range)] : &:r1084_8, ~m? +#-----| r0_9(glval>) = CopyValue : r1084_9 +# 1084| r1084_10(glval) = FunctionAddress[begin] : +# 1084| r1084_11(iterator) = Call[begin] : func:r1084_10, this:r0_9 +# 1084| mu1084_12(unknown) = ^CallSideEffect : ~m? +#-----| v0_10(void) = ^IndirectReadSideEffect[-1] : &:r0_9, ~m? +# 1084| mu1084_13(iterator) = Store[(__begin)] : &:r1084_7, r1084_11 +# 1084| r1084_14(glval) = VariableAddress[(__end)] : +# 1084| r1084_15(glval &>) = VariableAddress[(__range)] : +# 1084| r1084_16(vector &) = Load[(__range)] : &:r1084_15, ~m? +#-----| r0_11(glval>) = CopyValue : r1084_16 +# 1084| r1084_17(glval) = FunctionAddress[end] : +# 1084| r1084_18(iterator) = Call[end] : func:r1084_17, this:r0_11 +# 1084| mu1084_19(unknown) = ^CallSideEffect : ~m? +#-----| v0_12(void) = ^IndirectReadSideEffect[-1] : &:r0_11, ~m? +# 1084| mu1084_20(iterator) = Store[(__end)] : &:r1084_14, r1084_18 #-----| Goto -> Block 6 # 1084| Block 6 @@ -6209,7 +6208,7 @@ ir.cpp: # 1084| r1084_24(iterator) = Load[(__end)] : &:r1084_23, ~m? # 1084| r1084_25(bool) = Call[operator!=] : func:r1084_22, this:r0_13, 0:r1084_24 # 1084| mu1084_26(unknown) = ^CallSideEffect : ~m? -#-----| v0_14(void) = ^BufferReadSideEffect[-1] : &:r0_13, ~m? +#-----| v0_14(void) = ^IndirectReadSideEffect[-1] : &:r0_13, ~m? # 1084| v1084_27(void) = ConditionalBranch : r1084_25 #-----| False -> Block 10 #-----| True -> Block 8 @@ -6219,29 +6218,29 @@ ir.cpp: # 1084| r1084_29(glval) = FunctionAddress[operator++] : # 1084| r1084_30(iterator &) = Call[operator++] : func:r1084_29, this:r1084_28 # 1084| mu1084_31(unknown) = ^CallSideEffect : ~m? -# 1084| v1084_32(void) = ^BufferReadSideEffect[-1] : &:r1084_28, ~m? +# 1084| v1084_32(void) = ^IndirectReadSideEffect[-1] : &:r1084_28, ~m? # 1084| mu1084_33(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r1084_28 # 1084| r1084_34(glval) = CopyValue : r1084_30 #-----| Goto (back edge) -> Block 6 # 1084| Block 8 -# 1084| r1084_35(glval) = VariableAddress[e] : -# 1084| r1084_36(glval) = VariableAddress[(__begin)] : -#-----| r0_15(glval) = Convert : r1084_36 -# 1084| r1084_37(glval) = FunctionAddress[operator*] : -# 1084| r1084_38(int &) = Call[operator*] : func:r1084_37, this:r0_15 -# 1084| mu1084_39(unknown) = ^CallSideEffect : ~m? -#-----| v0_16(void) = ^BufferReadSideEffect[-1] : &:r0_15, ~m? -# 1084| r1084_40(glval) = CopyValue : r1084_38 -# 1084| r1084_41(glval) = Convert : r1084_40 -# 1084| r1084_42(int &) = CopyValue : r1084_41 -# 1084| mu1084_43(int &) = Store[e] : &:r1084_35, r1084_42 -# 1085| r1085_1(glval) = VariableAddress[e] : -# 1085| r1085_2(int &) = Load[e] : &:r1085_1, ~m? -# 1085| r1085_3(int) = Load[?] : &:r1085_2, ~m? -# 1085| r1085_4(int) = Constant[5] : -# 1085| r1085_5(bool) = CompareLT : r1085_3, r1085_4 -# 1085| v1085_6(void) = ConditionalBranch : r1085_5 +# 1084| r1084_35(glval) = VariableAddress[e] : +# 1084| r1084_36(glval) = VariableAddress[(__begin)] : +#-----| r0_15(glval) = Convert : r1084_36 +# 1084| r1084_37(glval) = FunctionAddress[operator*] : +# 1084| r1084_38(int &) = Call[operator*] : func:r1084_37, this:r0_15 +# 1084| mu1084_39(unknown) = ^CallSideEffect : ~m? +#-----| v0_16(void) = ^IndirectReadSideEffect[-1] : &:r0_15, ~m? +# 1084| r1084_40(glval) = CopyValue : r1084_38 +# 1084| r1084_41(glval) = Convert : r1084_40 +# 1084| r1084_42(int &) = CopyValue : r1084_41 +# 1084| mu1084_43(int &) = Store[e] : &:r1084_35, r1084_42 +# 1085| r1085_1(glval) = VariableAddress[e] : +# 1085| r1085_2(int &) = Load[e] : &:r1085_1, ~m? +# 1085| r1085_3(int) = Load[?] : &:r1085_2, ~m? +# 1085| r1085_4(int) = Constant[5] : +# 1085| r1085_5(bool) = CompareLT : r1085_3, r1085_4 +# 1085| v1085_6(void) = ConditionalBranch : r1085_5 #-----| False -> Block 7 #-----| True -> Block 9 @@ -7525,7 +7524,7 @@ ir.cpp: # 1373| r1373_8(glval) = FunctionAddress[c_str] : # 1373| r1373_9(char *) = Call[c_str] : func:r1373_8, this:r1373_7 # 1373| mu1373_10(unknown) = ^CallSideEffect : ~m? -# 1373| v1373_11(void) = ^BufferReadSideEffect[-1] : &:r1373_7, ~m? +# 1373| v1373_11(void) = ^IndirectReadSideEffect[-1] : &:r1373_7, ~m? # 1374| r1374_1(glval) = VariableAddress[#temp1374:5] : # 1374| r1374_2(glval) = FunctionAddress[returnValue] : # 1374| r1374_3(String) = Call[returnValue] : func:r1374_2 @@ -7535,7 +7534,7 @@ ir.cpp: # 1374| r1374_7(glval) = FunctionAddress[c_str] : # 1374| r1374_8(char *) = Call[c_str] : func:r1374_7, this:r1374_6 # 1374| mu1374_9(unknown) = ^CallSideEffect : ~m? -# 1374| v1374_10(void) = ^BufferReadSideEffect[-1] : &:r1374_6, ~m? +# 1374| v1374_10(void) = ^IndirectReadSideEffect[-1] : &:r1374_6, ~m? # 1376| r1376_1(glval) = VariableAddress[#temp1376:5] : # 1376| r1376_2(glval) = FunctionAddress[defaultConstruct] : # 1376| r1376_3(String) = Call[defaultConstruct] : func:r1376_2 @@ -7589,7 +7588,7 @@ ir.cpp: # 1385| r1385_4(glval) = FunctionAddress[method] : # 1385| v1385_5(void) = Call[method] : func:r1385_4, this:r1385_1 # 1385| mu1385_6(unknown) = ^CallSideEffect : ~m? -# 1385| v1385_7(void) = ^BufferReadSideEffect[-1] : &:r1385_1, ~m? +# 1385| v1385_7(void) = ^IndirectReadSideEffect[-1] : &:r1385_1, ~m? # 1385| mu1385_8(destructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1385_1 # 1386| r1386_1(glval) = VariableAddress[#temp1386:5] : # 1386| r1386_2(glval) = FunctionAddress[returnValue] : @@ -7599,7 +7598,7 @@ ir.cpp: # 1386| r1386_6(glval) = FunctionAddress[method] : # 1386| v1386_7(void) = Call[method] : func:r1386_6, this:r1386_1 # 1386| mu1386_8(unknown) = ^CallSideEffect : ~m? -# 1386| v1386_9(void) = ^BufferReadSideEffect[-1] : &:r1386_1, ~m? +# 1386| v1386_9(void) = ^IndirectReadSideEffect[-1] : &:r1386_1, ~m? # 1386| mu1386_10(destructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1386_1 # 1388| r1388_1(glval) = VariableAddress[#temp1388:5] : # 1388| r1388_2(glval) = FunctionAddress[defaultConstruct] : @@ -7667,7 +7666,7 @@ ir.cpp: # 1397| r1397_7(glval) = FunctionAddress[method] : # 1397| v1397_8(void) = Call[method] : func:r1397_7, this:r1397_1 # 1397| mu1397_9(unknown) = ^CallSideEffect : ~m? -# 1397| v1397_10(void) = ^BufferReadSideEffect[-1] : &:r1397_1, ~m? +# 1397| v1397_10(void) = ^IndirectReadSideEffect[-1] : &:r1397_1, ~m? # 1397| mu1397_11(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1397_1 # 1398| r1398_1(glval) = VariableAddress[#temp1398:5] : # 1398| r1398_2(glval) = FunctionAddress[returnValue] : @@ -7677,7 +7676,7 @@ ir.cpp: # 1398| r1398_6(glval) = FunctionAddress[method] : # 1398| v1398_7(void) = Call[method] : func:r1398_6, this:r1398_1 # 1398| mu1398_8(unknown) = ^CallSideEffect : ~m? -# 1398| v1398_9(void) = ^BufferReadSideEffect[-1] : &:r1398_1, ~m? +# 1398| v1398_9(void) = ^IndirectReadSideEffect[-1] : &:r1398_1, ~m? # 1398| mu1398_10(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1398_1 # 1399| r1399_1(glval) = VariableAddress[#temp1399:5] : # 1399| r1399_2(glval) = FunctionAddress[defaultConstruct] : @@ -7851,7 +7850,7 @@ ir.cpp: # 1447| r1447_9(glval) = FunctionAddress[f] : # 1447| r1447_10(float) = Call[f] : func:r1447_9, this:r1447_8 # 1447| mu1447_11(unknown) = ^CallSideEffect : ~m? -# 1447| v1447_12(void) = ^BufferReadSideEffect[-1] : &:r1447_8, ~m? +# 1447| v1447_12(void) = ^IndirectReadSideEffect[-1] : &:r1447_8, ~m? # 1447| mu1447_13(float) = Store[f] : &:r1447_1, r1447_10 # 1448| v1448_1(void) = NoOp : # 1443| v1443_4(void) = ReturnVoid : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index c4019a38251..76de50dd792 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -1059,7 +1059,7 @@ ssa.cpp: # 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| m241_4(unknown) = ^CallSideEffect : ~m240_7 # 241| m241_5(unknown) = Chi : total:m240_7, partial:m241_4 -# 241| v241_6(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m240_9 +# 241| v241_6(void) = ^IndirectReadSideEffect[-1] : &:r241_1, m240_9 # 241| m241_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 241| m241_8(Constructible) = Chi : total:m240_9, partial:m241_7 # 242| r242_1(glval) = VariableAddress[c] : @@ -1067,7 +1067,7 @@ ssa.cpp: # 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| m242_4(unknown) = ^CallSideEffect : ~m241_5 # 242| m242_5(unknown) = Chi : total:m241_5, partial:m242_4 -# 242| v242_6(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m241_8 +# 242| v242_6(void) = ^IndirectReadSideEffect[-1] : &:r242_1, m241_8 # 242| m242_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 # 242| m242_8(Constructible) = Chi : total:m241_8, partial:m242_7 # 243| r243_1(glval) = VariableAddress[c2] : @@ -1084,7 +1084,7 @@ ssa.cpp: # 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| m244_4(unknown) = ^CallSideEffect : ~m243_7 # 244| m244_5(unknown) = Chi : total:m243_7, partial:m244_4 -# 244| v244_6(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m243_9 +# 244| v244_6(void) = ^IndirectReadSideEffect[-1] : &:r244_1, m243_9 # 244| m244_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 # 244| m244_8(Constructible) = Chi : total:m243_9, partial:m244_7 # 245| v245_1(void) = NoOp : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index a101e638cd0..ab79d20214a 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1054,7 +1054,7 @@ ssa.cpp: # 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| m241_4(unknown) = ^CallSideEffect : ~m240_7 # 241| m241_5(unknown) = Chi : total:m240_7, partial:m241_4 -# 241| v241_6(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m240_9 +# 241| v241_6(void) = ^IndirectReadSideEffect[-1] : &:r241_1, m240_9 # 241| m241_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 241| m241_8(Constructible) = Chi : total:m240_9, partial:m241_7 # 242| r242_1(glval) = VariableAddress[c] : @@ -1062,7 +1062,7 @@ ssa.cpp: # 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| m242_4(unknown) = ^CallSideEffect : ~m241_5 # 242| m242_5(unknown) = Chi : total:m241_5, partial:m242_4 -# 242| v242_6(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m241_8 +# 242| v242_6(void) = ^IndirectReadSideEffect[-1] : &:r242_1, m241_8 # 242| m242_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 # 242| m242_8(Constructible) = Chi : total:m241_8, partial:m242_7 # 243| r243_1(glval) = VariableAddress[c2] : @@ -1079,7 +1079,7 @@ ssa.cpp: # 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| m244_4(unknown) = ^CallSideEffect : ~m243_7 # 244| m244_5(unknown) = Chi : total:m243_7, partial:m244_4 -# 244| v244_6(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m243_9 +# 244| v244_6(void) = ^IndirectReadSideEffect[-1] : &:r244_1, m243_9 # 244| m244_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 # 244| m244_8(Constructible) = Chi : total:m243_9, partial:m244_7 # 245| v245_1(void) = NoOp : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 8b6cf17dbbb..0b257db98a4 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -983,13 +983,13 @@ ssa.cpp: # 241| r241_2(glval) = FunctionAddress[g] : # 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| mu241_4(unknown) = ^CallSideEffect : ~m? -# 241| v241_5(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m? +# 241| v241_5(void) = ^IndirectReadSideEffect[-1] : &:r241_1, ~m? # 241| mu241_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : # 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| mu242_4(unknown) = ^CallSideEffect : ~m? -# 242| v242_5(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m? +# 242| v242_5(void) = ^IndirectReadSideEffect[-1] : &:r242_1, ~m? # 242| mu242_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 # 243| r243_1(glval) = VariableAddress[c2] : # 243| mu243_2(Constructible) = Uninitialized[c2] : &:r243_1 @@ -1002,7 +1002,7 @@ ssa.cpp: # 244| r244_2(glval) = FunctionAddress[g] : # 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| mu244_4(unknown) = ^CallSideEffect : ~m? -# 244| v244_5(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m? +# 244| v244_5(void) = ^IndirectReadSideEffect[-1] : &:r244_1, ~m? # 244| mu244_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 # 245| v245_1(void) = NoOp : # 239| v239_4(void) = ReturnVoid : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index 8b6cf17dbbb..0b257db98a4 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -983,13 +983,13 @@ ssa.cpp: # 241| r241_2(glval) = FunctionAddress[g] : # 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| mu241_4(unknown) = ^CallSideEffect : ~m? -# 241| v241_5(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m? +# 241| v241_5(void) = ^IndirectReadSideEffect[-1] : &:r241_1, ~m? # 241| mu241_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : # 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| mu242_4(unknown) = ^CallSideEffect : ~m? -# 242| v242_5(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m? +# 242| v242_5(void) = ^IndirectReadSideEffect[-1] : &:r242_1, ~m? # 242| mu242_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 # 243| r243_1(glval) = VariableAddress[c2] : # 243| mu243_2(Constructible) = Uninitialized[c2] : &:r243_1 @@ -1002,7 +1002,7 @@ ssa.cpp: # 244| r244_2(glval) = FunctionAddress[g] : # 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| mu244_4(unknown) = ^CallSideEffect : ~m? -# 244| v244_5(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m? +# 244| v244_5(void) = ^IndirectReadSideEffect[-1] : &:r244_1, ~m? # 244| mu244_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 # 245| v245_1(void) = NoOp : # 239| v239_4(void) = ReturnVoid : diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp index 03b202817ca..1ce2558a34f 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp @@ -189,3 +189,30 @@ int *&conversionInFlow() { int *&pRef = p; // has conversion in the middle of data flow return pRef; // BAD [NOT DETECTED] } + +namespace std { + template + class shared_ptr { + public: + shared_ptr() noexcept; + explicit shared_ptr(T*); + shared_ptr(const shared_ptr&) noexcept; + template shared_ptr(const shared_ptr&) noexcept; + template shared_ptr(shared_ptr&&) noexcept; + + shared_ptr& operator=(const shared_ptr&) noexcept; + shared_ptr& operator=(shared_ptr&&) noexcept; + + T& operator*() const noexcept; + T* operator->() const noexcept; + + T* get() const noexcept; + }; +} + +auto make_read_port() +{ + auto port = std::shared_ptr(new int); + auto ptr = port.get(); + return ptr; // GOOD +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected index a46371f36b6..77b08e62d83 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected @@ -4,5 +4,4 @@ | test.c:59:3:59:5 | sc6 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:58:9:58:16 | 127 | Extreme value | | test.c:63:3:63:5 | sc8 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:62:9:62:16 | - ... | Extreme value | | test.c:75:3:75:5 | sc1 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:74:9:74:16 | 127 | Extreme value | -| test.c:76:3:76:5 | sc1 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:74:9:74:16 | 127 | Extreme value | | test.c:124:9:124:9 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:118:17:118:23 | 2147483647 | Extreme value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c index 8c40d984ee0..ee2e041db3c 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c @@ -73,7 +73,7 @@ void test_negatives() { sc1 = CHAR_MAX; sc1 += 0; // GOOD [FALSE POSITIVE] - sc1 += -1; // GOOD [FALSE POSITIVE] + sc1 += -1; // GOOD sc2 = CHAR_MIN; sc2 += -1; // BAD [NOT DETECTED] sc3 = CHAR_MIN; diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected index 8bb25025b86..bdf00e0a5df 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected @@ -1,8 +1,5 @@ | test2.cpp:14:11:14:11 | v | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | | test2.cpp:14:11:14:11 | v | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | -| test3.c:15:10:15:10 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value | -| test3.c:15:14:15:14 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value | -| test3.c:15:18:15:18 | z | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value | | test5.cpp:17:6:17:18 | call to getTaintedInt | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs index 7b9f2e78881..99ad4c8f963 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -976,13 +976,11 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap TestAutobuilderScript(autobuilder, 0, 9); } - [Fact] - public void TestDotnetVersionWindows() + private void TestDotnetVersionWindows(Action action, int commandsRun) { actions.RunProcess["cmd.exe /C dotnet --list-sdks"] = 0; actions.RunProcessOut["cmd.exe /C dotnet --list-sdks"] = "2.1.3 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; - actions.RunProcess[@"cmd.exe /C powershell -NoProfile -ExecutionPolicy unrestricted -file C:\Project\install-dotnet.ps1 -Version 2.1.3 -InstallDir C:\Project\.dotnet"] = 0; - actions.RunProcess[@"cmd.exe /C del C:\Project\install-dotnet.ps1"] = 0; + action(); actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet --info"] = 0; actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean C:\Project\test.csproj"] = 0; actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore C:\Project\test.csproj"] = 0; @@ -1005,7 +1003,28 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.LoadXml[@"C:\Project\test.csproj"] = xml; var autobuilder = CreateAutoBuilder(true, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 7); + TestAutobuilderScript(autobuilder, 0, commandsRun); + } + + [Fact] + public void TestDotnetVersionWindowsWithPwsh() + { + TestDotnetVersionWindows(() => + { + actions.RunProcess[@"cmd.exe /C pwsh -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 0; + }, + 6); + } + + [Fact] + public void TestDotnetVersionWindowsWithoutPwsh() + { + TestDotnetVersionWindows(() => + { + actions.RunProcess[@"cmd.exe /C pwsh -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 1; + actions.RunProcess[@"cmd.exe /C powershell -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 0; + }, + 7); } [Fact] diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index 8787406c104..a456c9407db 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -179,53 +179,20 @@ namespace Semmle.Autobuild.CSharp if (builder.Actions.IsWindows()) { - var psScript = @"param([string]$Version, [string]$InstallDir) -add-type @"" -using System.Net; -using System.Security.Cryptography.X509Certificates; -public class TrustAllCertsPolicy : ICertificatePolicy -{ - public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) - { - return true; - } -} -""@ -$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12' -[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols -[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy -$Script = Invoke-WebRequest -useb 'https://dot.net/v1/dotnet-install.ps1' + var psCommand = $"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version {version} -InstallDir {path}"; -$arguments = @{ - Channel = 'release' - Version = $Version - InstallDir = $InstallDir -} - -$ScriptBlock = [scriptblock]::create("".{$($Script)} $(&{$args} @arguments)"") - -Invoke-Command -ScriptBlock $ScriptBlock"; - var psScriptFile = builder.Actions.PathCombine(builder.Options.RootDirectory, "install-dotnet.ps1"); - builder.Actions.WriteAllText(psScriptFile, psScript); - - var install = new CommandBuilder(builder.Actions). - RunCommand("powershell"). + BuildScript GetInstall(string pwsh) => + new CommandBuilder(builder.Actions). + RunCommand(pwsh). Argument("-NoProfile"). Argument("-ExecutionPolicy"). Argument("unrestricted"). - Argument("-file"). - Argument(psScriptFile). - Argument("-Version"). - Argument(version). - Argument("-InstallDir"). - Argument(path); + Argument("-Command"). + Argument("\"" + psCommand + "\""). + Script; - var removeScript = new CommandBuilder(builder.Actions). - RunCommand("del"). - Argument(psScriptFile); - - return install.Script & BuildScript.Try(removeScript.Script); + return GetInstall("pwsh") | GetInstall("powershell"); } else { diff --git a/csharp/change-notes/2021-04-09-dapper-support.md b/csharp/change-notes/2021-04-09-dapper-support.md new file mode 100644 index 00000000000..a7db9d045d2 --- /dev/null +++ b/csharp/change-notes/2021-04-09-dapper-support.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Support for the Dapper ORM library has been added to the SQL injection checks. \ No newline at end of file diff --git a/csharp/change-notes/2021-04-09-default-argument-values.md b/csharp/change-notes/2021-04-09-default-argument-values.md new file mode 100644 index 00000000000..ef34dca4f6a --- /dev/null +++ b/csharp/change-notes/2021-04-09-default-argument-values.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The extractor has been improved to store default argument values for parameters that are extracted from referenced assemblies. \ No newline at end of file diff --git a/csharp/change-notes/2021-04-14-customizations.md b/csharp/change-notes/2021-04-14-customizations.md new file mode 100644 index 00000000000..a2f957ca923 --- /dev/null +++ b/csharp/change-notes/2021-04-14-customizations.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* A new library, `Customizations.qll`, has been added, which allows for global customizations that affect all queries. \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index cdf17311440..103ea8cacda 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -164,6 +164,49 @@ namespace Semmle.Extraction.CSharp.Entities } } + /// + /// Creates a generated expression for a default argument value. + /// + public static Expression? CreateGenerated(Context cx, IParameterSymbol parameter, IExpressionParentEntity parent, + int childIndex, Extraction.Entities.Location location) + { + if (!parameter.HasExplicitDefaultValue) + { + return null; + } + + var defaultValue = parameter.ExplicitDefaultValue; + + if (parameter.Type is INamedTypeSymbol nt && nt.EnumUnderlyingType is not null) + { + // = (MyEnum)1, = MyEnum.Value1, = default(MyEnum), = new MyEnum() + // we're generating a (MyEnum)value cast expression: + defaultValue ??= 0; + Action createChild = (parent, index) => Literal.CreateGenerated(cx, parent, index, nt.EnumUnderlyingType, defaultValue, location); + return Cast.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, createChild, location); + } + + if (defaultValue is null) + { + // = null, = default, = default(T), = new MyStruct() + // we're generating a default expression: + return Default.CreateGenerated(cx, parent, childIndex, location, parameter.Type.IsReferenceType ? ValueAsString(null) : null); + } + + if (parameter.Type.SpecialType == SpecialType.System_Object) + { + // this can happen in VB.NET + cx.ExtractionError($"Extracting default argument value 'object {parameter.Name} = default' instead of 'object {parameter.Name} = {defaultValue}'. The latter is not supported in C#.", + null, null, severity: Util.Logging.Severity.Warning); + + // we're generating a default expression: + return Default.CreateGenerated(cx, parent, childIndex, location, ValueAsString(null)); + } + + // const literal: + return Literal.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, location); + } + /// /// Adapt the operator kind depending on whether it's a dynamic call or a user-operator call. /// diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs index 9e1b42834b1..9fdfd18d3bd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs @@ -14,5 +14,20 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { TypeAccess.Create(Context, Syntax.Type, this, 0); } + + public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, Extraction.Entities.Location location, string? value) + { + var info = new ExpressionInfo( + cx, + null, + location, + ExprKind.DEFAULT, + parent, + childIndex, + true, + value); + + return new Expression(info); + } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index 3bbf38a71eb..462c8bbb297 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -4,6 +4,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Entities; using System.IO; +using System; namespace Semmle.Extraction.CSharp.Entities { @@ -124,6 +125,17 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.param_location(this, Context.CreateLocation()); } + if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol)) + { + var defaultValueSyntax = GetDefaultValueFromSyntax(Symbol); + + Action defaultValueExpressionCreation = defaultValueSyntax is not null + ? () => Expression.Create(Context, defaultValueSyntax.Value, this, 0) + : () => Expression.CreateGenerated(Context, Symbol, this, 0, Location); + + Context.PopulateLater(defaultValueExpressionCreation); + } + if (!IsSourceDeclaration || !Symbol.FromSource()) return; @@ -139,36 +151,28 @@ namespace Semmle.Extraction.CSharp.Entities TypeMention.Create(Context, syntax.Type!, this, type); } } + } - if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol)) + private static EqualsValueClauseSyntax? GetDefaultValueFromSyntax(IParameterSymbol symbol) + { + // This is a slight bug in the dbscheme + // We should really define param_default(param, string) + // And use parameter child #0 to encode the default expression. + var defaultValue = GetParameterDefaultValue(symbol); + if (defaultValue is null) { - // This is a slight bug in the dbscheme - // We should really define param_default(param, string) - // And use parameter child #0 to encode the default expression. - var defaultValue = GetParameterDefaultValue(Symbol); - if (defaultValue is null) + // In case this parameter belongs to an accessor of an indexer, we need + // to get the default value from the corresponding parameter belonging + // to the indexer itself + if (symbol.ContainingSymbol is IMethodSymbol method) { - // In case this parameter belongs to an accessor of an indexer, we need - // to get the default value from the corresponding parameter belonging - // to the indexer itself - var method = (IMethodSymbol)Symbol.ContainingSymbol; - if (method is not null) - { - var i = method.Parameters.IndexOf(Symbol); - var indexer = (IPropertySymbol?)method.AssociatedSymbol; - if (indexer is not null) - defaultValue = GetParameterDefaultValue(indexer.Parameters[i]); - } - } - - if (defaultValue is not null) - { - Context.PopulateLater(() => - { - Expression.Create(Context, defaultValue.Value, this, 0); - }); + var i = method.Parameters.IndexOf(symbol); + if (method.AssociatedSymbol is IPropertySymbol indexer) + defaultValue = GetParameterDefaultValue(indexer.Parameters[i]); } } + + return defaultValue; } public override bool IsSourceDeclaration => Symbol.IsSourceDeclaration(); diff --git a/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql b/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql index 7469cbc59e7..abb962449b9 100644 --- a/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql +++ b/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql @@ -18,36 +18,6 @@ import csharp import Dispose import semmle.code.csharp.frameworks.System -/** - * Gets an exception type that may be thrown during the execution of method `m`. - * Assumes any exception may be thrown by library types. - */ -Class getAThrownException(Method m) { - m.fromLibrary() and - result = any(SystemExceptionClass sc) - or - exists(ControlFlowElement cfe | - cfe = any(ThrowElement te | result = te.getExpr().getType()) or - cfe = any(MethodCall mc | result = getAThrownException(mc.getARuntimeTarget())) - | - cfe.getEnclosingCallable() = m and - not isTriedAgainstException(cfe, result) - ) -} - -/** - * Holds if control flow element is tried against throwing an exception of type - * `ec`. - */ -pragma[noinline] -predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) { - (cfe instanceof ThrowElement or cfe instanceof MethodCall) and - exists(TryStmt ts | - ts.getATriedElement() = cfe and - exists(ts.getAnExceptionHandler(ec)) - ) -} - private class DisposeCall extends MethodCall { DisposeCall() { this.getTarget() instanceof DisposeMethod } } @@ -78,8 +48,17 @@ predicate disposeReachableFromDisposableCreation(DisposeCall disposeCall, Expr d reachesDisposeCall(disposeCall, DataFlow::exprNode(disposableCreation)) } -class MethodCallThatMayThrow extends MethodCall { - MethodCallThatMayThrow() { exists(getAThrownException(this.getARuntimeTarget())) } +/** + * Holds if control flow element is tried against throwing an exception of type + * `ec`. + */ +pragma[noinline] +predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) { + (cfe instanceof ThrowElement or cfe instanceof MethodCall) and + exists(TryStmt ts | + ts.getATriedElement() = cfe and + exists(ts.getAnExceptionHandler(ec)) + ) } ControlFlowElement getACatchOrFinallyClauseChild() { @@ -88,15 +67,71 @@ ControlFlowElement getACatchOrFinallyClauseChild() { result = getACatchOrFinallyClauseChild().getAChild() } -from DisposeCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows -where +private predicate candidate(DisposeCall disposeCall, Call call, Expr disposableCreation) { disposeReachableFromDisposableCreation(disposeCall, disposableCreation) and // The dispose call is not, itself, within a dispose method. not disposeCall.getEnclosingCallable() instanceof DisposeMethod and // Dispose call not within a finally or catch block not getACatchOrFinallyClauseChild() = disposeCall and // At least one method call exists between the allocation and disposal that could throw - disposableCreation.getAReachableElement() = callThatThrows and - callThatThrows.getAReachableElement() = disposeCall + disposableCreation.getAReachableElement() = call and + call.getAReachableElement() = disposeCall +} + +private class RelevantMethod extends Method { + RelevantMethod() { + exists(Call call | + candidate(_, call, _) and + this = call.getARuntimeTarget() + ) + or + exists(RelevantMethod other | other.calls(this)) + } + + pragma[noinline] + private RelevantMethod callsNoTry() { + exists(MethodCall mc | + result = mc.getARuntimeTarget() and + not isTriedAgainstException(mc, _) and + mc.getEnclosingCallable() = this + ) + } + + pragma[noinline] + private RelevantMethod callsInTry(MethodCall mc) { + result = mc.getARuntimeTarget() and + isTriedAgainstException(mc, _) and + mc.getEnclosingCallable() = this + } + + /** + * Gets an exception type that may be thrown during the execution of this method. + * Assumes any exception may be thrown by library types. + */ + Class getAThrownException() { + this.fromLibrary() and + result instanceof SystemExceptionClass + or + exists(ControlFlowElement cfe | + result = cfe.(ThrowElement).getExpr().getType() and + cfe.getEnclosingCallable() = this + or + result = this.callsInTry(cfe).getAThrownException() + | + not isTriedAgainstException(cfe, result) + ) + or + result = this.callsNoTry().getAThrownException() + } +} + +class MethodCallThatMayThrow extends MethodCall { + MethodCallThatMayThrow() { + exists(this.getARuntimeTarget().(RelevantMethod).getAThrownException()) + } +} + +from DisposeCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows +where candidate(disposeCall, callThatThrows, disposableCreation) select disposeCall, "Dispose missed if exception is thrown by $@.", callThatThrows, callThatThrows.toString() diff --git a/csharp/ql/src/Customizations.qll b/csharp/ql/src/Customizations.qll new file mode 100644 index 00000000000..d5378a44f0d --- /dev/null +++ b/csharp/ql/src/Customizations.qll @@ -0,0 +1,12 @@ +/** + * Contains customizations to the standard library. + * + * This module is imported by `csharp.qll`, so any customizations defined here automatically + * apply to all queries. + * + * Typical examples of customizations include adding new subclasses of abstract classes such as + * the `RemoteFlowSource` and `SummarizedCallable` classes associated with the security queries + * to model frameworks that are not covered by the standard library. + */ + +import csharp diff --git a/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql b/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql new file mode 100644 index 00000000000..9156a5e4a7f --- /dev/null +++ b/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql @@ -0,0 +1,11 @@ +/** + * @id cs/summary/lines-of-code + * @name Total lines of code in the database + * @description The total number of lines of code across all files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments. + * @kind metric + * @tags summary + */ + +import csharp + +select sum(File f | f.fromSource() | f.getNumberOfLinesOfCode()) diff --git a/csharp/ql/src/Stubs/make_stubs.py b/csharp/ql/src/Stubs/make_stubs.py index e94cf0c7261..7d2ce9f7ed7 100644 --- a/csharp/ql/src/Stubs/make_stubs.py +++ b/csharp/ql/src/Stubs/make_stubs.py @@ -43,12 +43,6 @@ if not foundCS: print("Test directory does not contain .cs files. Please specify a working qltest directory.") exit(1) -cmd = ['odasa', 'selfTest'] -print('Running ' + ' '.join(cmd)) -if subprocess.check_call(cmd): - print("odasa selfTest failed. Ensure odasa is on your current path.") - exit(1) - csharpQueries = os.path.abspath(os.path.dirname(sys.argv[0])) outputFile = os.path.join(testDir, 'stubs.cs') @@ -58,56 +52,75 @@ if os.path.isfile(outputFile): os.remove(outputFile) # It would interfere with the test. print("Removed previous", outputFile) -cmd = ['odasa', 'qltest', '--optimize', '--leave-temp-files', testDir] +cmd = ['codeql', 'test', 'run', '--keep-databases', testDir] print('Running ' + ' '.join(cmd)) if subprocess.check_call(cmd): - print("qltest failed. Please fix up the test before proceeding.") + print("codeql test failed. Please fix up the test before proceeding.") exit(1) -dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj", "db-csharp") +dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj") if not os.path.isdir(dbDir): - print("Expected database directory " + dbDir + " not found. Please contact Semmle.") + print("Expected database directory " + dbDir + " not found.") exit(1) -cmd = ['odasa', 'runQuery', '--query', os.path.join(csharpQueries, 'MinimalStubsFromSource.ql'), '--db', dbDir, '--output-file', outputFile] +cmd = ['codeql', 'query', 'run', os.path.join( + csharpQueries, 'MinimalStubsFromSource.ql'), '--database', dbDir, '--output', outputFile] print('Running ' + ' '.join(cmd)) if subprocess.check_call(cmd): - print('Failed to run the query to generate output file. Please contact Semmle.') + print('Failed to run the query to generate output file.') exit(1) -# Remove the leading " and trailing " bytes from the file -len = os.stat(outputFile).st_size -f = open(outputFile, "rb") -try: - quote = f.read(1) - if quote != b'"': - print("Unexpected character in file. Please contact Semmle.") - contents = f.read(len-3) - quote = f.read(1) - if quote != b'"': - print("Unexpected end character. Please contact Semmle.", quote) -finally: - f.close() +# Remove the leading and trailing bytes from the file +length = os.stat(outputFile).st_size +if length < 20: + contents = b'' +else: + f = open(outputFile, "rb") + try: + countTillSlash = 0 + foundSlash = False + slash = f.read(1) + while slash != b'': + if slash == b'/': + foundSlash = True + break + countTillSlash += 1 + slash = f.read(1) + + if not foundSlash: + countTillSlash = 0 + + f.seek(0) + quote = f.read(countTillSlash) + print("Start characters in file skipped.", quote) + post = b'\x0e\x01\x08#select\x01\x01\x00s\x00' + contents = f.read(length - len(post) - countTillSlash) + quote = f.read(len(post)) + if quote != post: + print("Unexpected end character in file.", quote) + finally: + f.close() f = open(outputFile, "wb") f.write(contents) f.close() -cmd = ['odasa', 'qltest', '--optimize', testDir] +cmd = ['codeql', 'test', 'run', testDir] print('Running ' + ' '.join(cmd)) if subprocess.check_call(cmd): - print('\nTest failed. You may need to fix up', outputFile) - print('It may help to view', outputFile, ' in Visual Studio') - print("Next steps:") - print('1. Look at the compilation errors, and fix up', outputFile, 'so that the test compiles') - print('2. Re-run odasa qltest --optimize "' + testDir + '"') - print('3. git add "' + outputFile + '"') - exit(1) + print('\nTest failed. You may need to fix up', outputFile) + print('It may help to view', outputFile, ' in Visual Studio') + print("Next steps:") + print('1. Look at the compilation errors, and fix up', + outputFile, 'so that the test compiles') + print('2. Re-run codeql test run "' + testDir + '"') + print('3. git add "' + outputFile + '"') + exit(1) print("\nStub generation successful! Next steps:") print('1. Edit "semmle-extractor-options" in the .cs files to remove unused references') -print('2. Re-run odasa qltest --optimize "' + testDir + '"') +print('2. Re-run codeql test run "' + testDir + '"') print('3. git add "' + outputFile + '"') print('4. Commit your changes.') diff --git a/csharp/ql/src/csharp.qll b/csharp/ql/src/csharp.qll index 3c821c9a443..dc187fc8d92 100644 --- a/csharp/ql/src/csharp.qll +++ b/csharp/ql/src/csharp.qll @@ -2,6 +2,7 @@ * The default C# QL library. */ +import Customizations import semmle.code.csharp.Attribute import semmle.code.csharp.Callable import semmle.code.csharp.Comments diff --git a/csharp/ql/src/definitions.qll b/csharp/ql/src/definitions.qll index 2004ad0d218..c1b456f4dbf 100644 --- a/csharp/ql/src/definitions.qll +++ b/csharp/ql/src/definitions.qll @@ -187,5 +187,11 @@ cached Declaration definitionOf(Use use, string kind) { result = use.getDefinition() and result.fromSource() and - kind = use.getUseType() + kind = use.getUseType() and + // Some entities have many locations. This can arise for files that + // are duplicated multiple times in the database at different + // locations. Rather than letting the result set explode, we just + // exclude results that are "too ambiguous" -- we could also arbitrarily + // pick one location later on. + strictcount(result.getLocation()) < 10 } diff --git a/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll b/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll index f828419a440..f37a4f2d074 100644 --- a/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll +++ b/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll @@ -316,6 +316,15 @@ private module SsaDefReaches { ) } + /** + * Holds if the reference to `def` at index `i` in basic block `bb` is the + * last reference to `v` inside `bb`. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) + } + predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) { exists(ssaDefRank(def, v, bb, _, _)) } @@ -351,8 +360,7 @@ private module SsaDefReaches { */ predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { varBlockReaches(def, bb1, bb2) and - ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and - variableRead(bb2, i2, _, _) + ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 } } @@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2 bb2 = bb1 ) or - exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and + lastSsaRef(def, _, bb1, i1) and defAdjacentRead(def, bb1, bb2, i2) } +pragma[noinline] +private predicate adjacentDefRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v +) { + adjacentDefRead(def, bb1, i1, bb2, i2) and + v = def.getSourceVariable() +} + private predicate adjacentDefReachesRead( Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and - exists(SourceVariable v | v = def.getSourceVariable() | + exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) | ssaRef(bb1, i1, v, SsaDef()) or variableRead(bb1, i1, v, true) @@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba */ pragma[nomagic] predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { - exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) | + exists(SourceVariable v | // Next reference to `v` inside `bb` is a write - next.definesAt(v, bb, j) and - rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j) and + rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + ) or // Can reach a write using one or more steps - rnk = maxSsaRefRank(bb, v) and + lastSsaRef(def, v, bb, i) and exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) and - next.definesAt(v, bb2, j) and - 1 = ssaRefRank(bb2, j, v, SsaDef()) + 1 = ssaDefRank(next, v, bb2, _, SsaDef()) ) ) } @@ -539,7 +556,8 @@ pragma[nomagic] predicate lastRef(Definition def, BasicBlock bb, int i) { lastRefRedef(def, bb, i, _) or - exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) | + lastSsaRef(def, _, bb, i) and + ( // Can reach exit directly bb instanceof ExitBasicBlock or diff --git a/csharp/ql/src/semmle/code/csharp/Caching.qll b/csharp/ql/src/semmle/code/csharp/Caching.qll index 95ffbf9ffc9..374ecaaa183 100644 --- a/csharp/ql/src/semmle/code/csharp/Caching.qll +++ b/csharp/ql/src/semmle/code/csharp/Caching.qll @@ -49,6 +49,7 @@ module Stages { cached module DataFlowStage { + private import semmle.code.csharp.dataflow.internal.DataFlowDispatch private import semmle.code.csharp.dataflow.internal.DataFlowPrivate private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate @@ -78,6 +79,8 @@ module Stages { or exists(CallContext cc) or + exists(any(DataFlowCall c).getEnclosingCallable()) + or forceCachingInSameStageRev() } } diff --git a/csharp/ql/src/semmle/code/csharp/Element.qll b/csharp/ql/src/semmle/code/csharp/Element.qll index 521138db1b5..fbd96f6086d 100644 --- a/csharp/ql/src/semmle/code/csharp/Element.qll +++ b/csharp/ql/src/semmle/code/csharp/Element.qll @@ -27,9 +27,6 @@ class Element extends DotNet::Element, @element { /** Gets a location of this element, including sources and assemblies. */ override Location getALocation() { none() } - /** Holds if this element is from an assembly. */ - predicate fromLibrary() { this.getFile().fromLibrary() } - /** Gets the parent of this element, if any. */ Element getParent() { result.getAChild() = this } diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll index f828419a440..f37a4f2d074 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll @@ -316,6 +316,15 @@ private module SsaDefReaches { ) } + /** + * Holds if the reference to `def` at index `i` in basic block `bb` is the + * last reference to `v` inside `bb`. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) + } + predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) { exists(ssaDefRank(def, v, bb, _, _)) } @@ -351,8 +360,7 @@ private module SsaDefReaches { */ predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { varBlockReaches(def, bb1, bb2) and - ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and - variableRead(bb2, i2, _, _) + ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 } } @@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2 bb2 = bb1 ) or - exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and + lastSsaRef(def, _, bb1, i1) and defAdjacentRead(def, bb1, bb2, i2) } +pragma[noinline] +private predicate adjacentDefRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v +) { + adjacentDefRead(def, bb1, i1, bb2, i2) and + v = def.getSourceVariable() +} + private predicate adjacentDefReachesRead( Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and - exists(SourceVariable v | v = def.getSourceVariable() | + exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) | ssaRef(bb1, i1, v, SsaDef()) or variableRead(bb1, i1, v, true) @@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba */ pragma[nomagic] predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { - exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) | + exists(SourceVariable v | // Next reference to `v` inside `bb` is a write - next.definesAt(v, bb, j) and - rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j) and + rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + ) or // Can reach a write using one or more steps - rnk = maxSsaRefRank(bb, v) and + lastSsaRef(def, v, bb, i) and exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) and - next.definesAt(v, bb2, j) and - 1 = ssaRefRank(bb2, j, v, SsaDef()) + 1 = ssaDefRank(next, v, bb2, _, SsaDef()) ) ) } @@ -539,7 +556,8 @@ pragma[nomagic] predicate lastRef(Definition def, BasicBlock bb, int i) { lastRefRedef(def, bb, i, _) or - exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) | + lastSsaRef(def, _, bb, i) and + ( // Can reach exit directly bb instanceof ExitBasicBlock or diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll index 4eac274b04f..0a7cf4acc41 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -4,6 +4,7 @@ private import dotnet private import DataFlowPublic private import DataFlowPrivate private import FlowSummaryImpl as FlowSummaryImpl +private import semmle.code.csharp.Caching private import semmle.code.csharp.dataflow.FlowSummary private import semmle.code.csharp.dispatch.Dispatch private import semmle.code.csharp.frameworks.system.Collections @@ -69,8 +70,6 @@ private predicate transitiveCapturedCallTarget(ControlFlow::Nodes::ElementNode c cached private module Cached { - private import semmle.code.csharp.Caching - cached newtype TReturnKind = TNormalReturnKind() { Stages::DataFlowStage::forceCachingInSameStage() } or @@ -247,6 +246,7 @@ abstract class DataFlowCall extends TDataFlowCall { abstract DataFlow::Node getNode(); /** Gets the enclosing callable of this call. */ + cached abstract DataFlowCallable getEnclosingCallable(); /** Gets the underlying expression, if any. */ @@ -280,7 +280,10 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall { override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn } - override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() } + override DataFlowCallable getEnclosingCallable() { + Stages::DataFlowStage::forceCachingInSameStage() and + result = cfn.getEnclosingCallable() + } override string toString() { result = cfn.toString() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 8b446d28b86..9498e51e7e6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 8b446d28b86..9498e51e7e6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 8b446d28b86..9498e51e7e6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 8b446d28b86..9498e51e7e6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 8b446d28b86..9498e51e7e6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -2133,11 +2133,8 @@ private module Stage4 { bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -3132,7 +3129,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index 3b646973e28..f42410ca3a5 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -21,9 +21,11 @@ private import semmle.code.csharp.frameworks.system.threading.Tasks abstract class NodeImpl extends Node { /** Do not call: use `getEnclosingCallable()` instead. */ + cached abstract DataFlowCallable getEnclosingCallableImpl(); /** Do not call: use `getType()` instead. */ + cached abstract DotNet::Type getTypeImpl(); /** Gets the type of this node used for type pruning. */ @@ -39,27 +41,39 @@ abstract class NodeImpl extends Node { } /** Do not call: use `getControlFlowNode()` instead. */ + cached abstract ControlFlow::Node getControlFlowNodeImpl(); /** Do not call: use `getLocation()` instead. */ + cached abstract Location getLocationImpl(); /** Do not call: use `toString()` instead. */ + cached abstract string toStringImpl(); } private class ExprNodeImpl extends ExprNode, NodeImpl { override DataFlowCallable getEnclosingCallableImpl() { + Stages::DataFlowStage::forceCachingInSameStage() and result = this.getExpr().getEnclosingCallable() } - override DotNet::Type getTypeImpl() { result = this.getExpr().getType() } + override DotNet::Type getTypeImpl() { + Stages::DataFlowStage::forceCachingInSameStage() and + result = this.getExpr().getType() + } - override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { this = TExprNode(result) } + override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { + Stages::DataFlowStage::forceCachingInSameStage() and this = TExprNode(result) + } - override Location getLocationImpl() { result = this.getExpr().getLocation() } + override Location getLocationImpl() { + Stages::DataFlowStage::forceCachingInSameStage() and result = this.getExpr().getLocation() + } override string toStringImpl() { + Stages::DataFlowStage::forceCachingInSameStage() and result = this.getControlFlowNode().toString() or exists(CIL::Expr e | @@ -967,6 +981,16 @@ private module Cached { or n.asExpr() = any(WithExpr we).getInitializer() } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNodeImpl).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNodeImpl).argumentOf(call, pos) + } } import Cached @@ -992,8 +1016,6 @@ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode { } abstract class ParameterNodeImpl extends NodeImpl { - abstract DotNet::Parameter getParameter(); - abstract predicate isParameterOf(DataFlowCallable c, int i); } @@ -1010,11 +1032,9 @@ private module ParameterNodes { /** Gets the SSA definition corresponding to this parameter, if any. */ Ssa::ExplicitDefinition getSsaDefinition() { result.getADefinition().(AssignableDefinitions::ImplicitParameterDefinition).getParameter() = - this.getParameter() + parameter } - override DotNet::Parameter getParameter() { result = parameter } - override predicate isParameterOf(DataFlowCallable c, int i) { c.getParameter(i) = parameter } override DataFlowCallable getEnclosingCallableImpl() { result = parameter.getCallable() } @@ -1037,8 +1057,6 @@ private module ParameterNodes { /** Gets the callable containing this implicit instance parameter. */ Callable getCallable() { result = callable } - override DotNet::Parameter getParameter() { none() } - override predicate isParameterOf(DataFlowCallable c, int pos) { callable = c and pos = -1 } override DataFlowCallable getEnclosingCallableImpl() { result = callable } @@ -1113,8 +1131,6 @@ private module ParameterNodes { /** Gets the captured variable that this implicit parameter models. */ LocalScopeVariable getVariable() { result = def.getVariable() } - override DotNet::Parameter getParameter() { none() } - override predicate isParameterOf(DataFlowCallable c, int i) { i = getParameterPosition(def) and c = this.getEnclosingCallable() @@ -1125,13 +1141,15 @@ private module ParameterNodes { import ParameterNodes /** A data-flow node that represents a call argument. */ -abstract class ArgumentNode extends Node { - /** Holds if this argument occurs at the given position in the given call. */ - cached - abstract predicate argumentOf(DataFlowCall call, int pos); +class ArgumentNode extends Node { + ArgumentNode() { argumentNode(this, _, _) } - /** Gets the call in which this node is an argument. */ - final DataFlowCall getCall() { this.argumentOf(result, _) } + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + +abstract private class ArgumentNodeImpl extends Node { + abstract predicate argumentOf(DataFlowCall call, int pos); } private module ArgumentNodes { @@ -1149,7 +1167,7 @@ private module ArgumentNodes { } /** A data-flow node that represents an explicit call argument. */ - class ExplicitArgumentNode extends ArgumentNode { + class ExplicitArgumentNode extends ArgumentNodeImpl { ExplicitArgumentNode() { this.asExpr() instanceof Argument or @@ -1157,7 +1175,6 @@ private module ArgumentNodes { } override predicate argumentOf(DataFlowCall call, int pos) { - Stages::DataFlowStage::forceCachingInSameStage() and exists(ArgumentConfiguration x, Expr c, Argument arg | arg = this.asExpr() and c = call.getExpr() and @@ -1189,7 +1206,8 @@ private module ArgumentNodes { * } } * ``` */ - class ImplicitCapturedArgumentNode extends ArgumentNode, NodeImpl, TImplicitCapturedArgumentNode { + class ImplicitCapturedArgumentNode extends ArgumentNodeImpl, NodeImpl, + TImplicitCapturedArgumentNode { private LocalScopeVariable v; private ControlFlow::Nodes::ElementNode cfn; @@ -1231,7 +1249,7 @@ private module ArgumentNodes { * A node that corresponds to the value of an object creation (`new C()`) before * the constructor has run. */ - class MallocNode extends ArgumentNode, NodeImpl, TMallocNode { + class MallocNode extends ArgumentNodeImpl, NodeImpl, TMallocNode { private ControlFlow::Nodes::ElementNode cfn; MallocNode() { this = TMallocNode(cfn) } @@ -1266,7 +1284,7 @@ private module ArgumentNodes { * and that argument is itself a compatible array, for example * `Foo(new[] { "a", "b", "c" })`. */ - class ParamsArgumentNode extends ArgumentNode, NodeImpl, TParamsArgumentNode { + class ParamsArgumentNode extends ArgumentNodeImpl, NodeImpl, TParamsArgumentNode { private ControlFlow::Node callCfn; ParamsArgumentNode() { this = TParamsArgumentNode(callCfn) } @@ -1291,7 +1309,7 @@ private module ArgumentNodes { override string toStringImpl() { result = "[implicit array creation] " + callCfn } } - private class SummaryArgumentNode extends SummaryNode, ArgumentNode { + private class SummaryArgumentNode extends SummaryNode, ArgumentNodeImpl { private DataFlowCall c; private int i; @@ -1324,10 +1342,7 @@ private module ReturnNodes { ) } - override NormalReturnKind getKind() { - any(DotNet::Callable c).canReturn(this.getExpr()) and - exists(result) - } + override NormalReturnKind getKind() { exists(result) } } /** @@ -1744,7 +1759,10 @@ class DataFlowType extends Gvn::GvnType { } /** Gets the type of `n` used for type pruning. */ -DataFlowType getNodeType(NodeImpl n) { result = n.getDataFlowType() } +pragma[inline] +Gvn::GvnType getNodeType(NodeImpl n) { + pragma[only_bind_into](result) = pragma[only_bind_out](n).getDataFlowType() +} /** Gets a string representation of a `DataFlowType`. */ string ppReprType(DataFlowType t) { result = t.toString() } @@ -1819,7 +1837,8 @@ private module PostUpdateNodes { * Such a node acts as both a post-update node for the `MallocNode`, as well as * a pre-update node for the `ObjectCreationNode`. */ - class ObjectInitializerNode extends PostUpdateNode, NodeImpl, ArgumentNode, TObjectInitializerNode { + class ObjectInitializerNode extends PostUpdateNode, NodeImpl, ArgumentNodeImpl, + TObjectInitializerNode { private ObjectCreation oc; private ControlFlow::Nodes::ElementNode cfn; diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll index dd6e72d54f6..12a62faf387 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -3,7 +3,6 @@ private import cil private import dotnet private import DataFlowDispatch private import DataFlowPrivate -private import semmle.code.csharp.Caching private import semmle.code.csharp.controlflow.Guards private import semmle.code.csharp.Unification @@ -38,38 +37,21 @@ class Node extends TNode { } /** Gets the type of this node. */ - cached - final DotNet::Type getType() { - Stages::DataFlowStage::forceCachingInSameStage() and result = this.(NodeImpl).getTypeImpl() - } + final DotNet::Type getType() { result = this.(NodeImpl).getTypeImpl() } /** Gets the enclosing callable of this node. */ - cached final DataFlowCallable getEnclosingCallable() { - Stages::DataFlowStage::forceCachingInSameStage() and result = this.(NodeImpl).getEnclosingCallableImpl() } /** Gets the control flow node corresponding to this node, if any. */ - cached - final ControlFlow::Node getControlFlowNode() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = this.(NodeImpl).getControlFlowNodeImpl() - } + final ControlFlow::Node getControlFlowNode() { result = this.(NodeImpl).getControlFlowNodeImpl() } /** Gets a textual representation of this node. */ - cached - final string toString() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = this.(NodeImpl).toStringImpl() - } + final string toString() { result = this.(NodeImpl).toStringImpl() } /** Gets the location of this node. */ - cached - final Location getLocation() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = this.(NodeImpl).getLocationImpl() - } + final Location getLocation() { result = this.(NodeImpl).getLocationImpl() } /** * Holds if this element is at the specified location. @@ -81,7 +63,7 @@ class Node extends TNode { predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } } @@ -117,18 +99,18 @@ class ExprNode extends Node { * flow graph. */ class ParameterNode extends Node { - private ParameterNodeImpl p; - - ParameterNode() { this = p } + ParameterNode() { parameterNode(this, _, _) } /** Gets the parameter corresponding to this node, if any. */ - DotNet::Parameter getParameter() { result = p.getParameter() } + DotNet::Parameter getParameter() { + exists(DataFlowCallable c, int i | this.isParameterOf(c, i) and result = c.getParameter(i)) + } /** * Holds if this node is the parameter of callable `c` at the specified * (zero-based) position. */ - predicate isParameterOf(DataFlowCallable c, int i) { p.isParameterOf(c, i) } + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } } /** A definition, viewed as a node in a data flow graph. */ @@ -166,6 +148,7 @@ predicate localFlowStep = localFlowStepImpl/2; * Holds if data flows from `source` to `sink` in zero or more local * (intra-procedural) steps. */ +pragma[inline] predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll index 41da2b8f0b5..63d6c902fed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -268,56 +268,146 @@ private module CallGraph { ) } + /** + * A simple flow step that does not take flow through fields or flow out + * of callables into account. + */ + pragma[nomagic] private predicate delegateFlowStep(Expr pred, Expr succ) { Steps::stepClosed(pred, succ) or - exists(Call call, Callable callable | - callable.getUnboundDeclaration().canReturn(pred) and - call = succ - | - callable = call.getTarget() or - callable = call.getTarget().(Method).getAnOverrider+() or - callable = call.getTarget().(Method).getAnUltimateImplementor() or - callable = getARuntimeDelegateTarget(call, false) - ) - or pred = succ.(DelegateCreation).getArgument() or - exists(AssignableDefinition def, Assignable a | - a instanceof Field or - a instanceof Property - | - a = def.getTarget() and - succ.(AssignableRead) = a.getAnAccess() and - pred = def.getSource() - ) - or exists(AddEventExpr ae | succ.(EventAccess).getTarget() = ae.getTarget() | pred = ae.getRValue() ) } - private predicate reachesDelegateCall(Expr e) { - delegateCall(_, e, _) + private predicate delegateCreationReaches(Callable c, Expr e) { + delegateCreation(e, c, _) or - exists(Expr mid | reachesDelegateCall(mid) | delegateFlowStep(e, mid)) + exists(Expr mid | + delegateCreationReaches(c, mid) and + delegateFlowStep(mid, e) + ) } - pragma[nomagic] - private predicate delegateFlowStepReaches(Expr pred, Expr succ) { - delegateFlowStep(pred, succ) and - reachesDelegateCall(succ) + private predicate reachesDelegateCall(Expr e, Call c, boolean libraryDelegateCall) { + delegateCall(c, e, libraryDelegateCall) + or + exists(Expr mid | + reachesDelegateCall(mid, c, libraryDelegateCall) and + delegateFlowStep(e, mid) + ) } - private Expr delegateCallSource(Callable c) { - delegateCreation(result, c, _) - or - delegateFlowStepReaches(delegateCallSource(c), result) + /** + * A "busy" flow element, that is, a node in the data-flow graph that typically + * has a large fan-in or a large fan-out (or both). + * + * For such busy elements, we do not track flow directly from all delegate + * creations, but instead we first perform a flow analysis between busy elements, + * and then only in the end join up with delegate creations and delegate calls. + */ + abstract private class BusyFlowElement extends Element { + pragma[nomagic] + abstract Expr getAnInput(); + + pragma[nomagic] + abstract Expr getAnOutput(); + + /** Holds if this element can be reached from expression `e`. */ + pragma[nomagic] + private predicate exprReaches(Expr e) { + this.reachesCall(_) and + e = this.getAnInput() + or + exists(Expr mid | + this.exprReaches(mid) and + delegateFlowStep(e, mid) + ) + } + + /** + * Holds if this element can reach a delegate call `c` directly without + * passing through another busy element. + */ + pragma[nomagic] + predicate delegateCall(Call c, boolean libraryDelegateCall) { + reachesDelegateCall(this.getAnOutput(), c, libraryDelegateCall) + } + + pragma[nomagic] + private BusyFlowElement getASuccessor() { result.exprReaches(this.getAnOutput()) } + + /** + * Holds if this element reaches another busy element `other`, + * which can reach a delegate call directly without passing + * through another busy element. + */ + pragma[nomagic] + private predicate reachesCall(BusyFlowElement other) { + this = other and + other.delegateCall(_, _) + or + this.getASuccessor().reachesCall(other) + } + + /** Holds if this element is reached by a delegate creation for `c`. */ + pragma[nomagic] + predicate isReachedBy(Callable c) { + exists(BusyFlowElement pred | + pred.reachesCall(this) and + delegateCreationReaches(c, pred.getAnInput()) + ) + } + } + + private class TFieldOrProperty = @field or @property; + + private class FieldOrPropertyFlow extends BusyFlowElement, Assignable, TFieldOrProperty { + final override Expr getAnInput() { + exists(Assignable target | + target = this.getUnboundDeclaration() and + result = target.getAnAssignedValue() + ) + } + + final override AssignableRead getAnOutput() { + exists(Assignable target | + target = this.getUnboundDeclaration() and + result = target.getAnAccess() + ) + } + } + + private class CallOutputFlow extends BusyFlowElement, Callable { + final override Expr getAnInput() { this.canReturn(result) } + + final override Call getAnOutput() { + exists(Callable target | this = target.getUnboundDeclaration() | + target = result.getTarget() or + target = result.getTarget().(Method).getAnOverrider+() or + target = result.getTarget().(Method).getAnUltimateImplementor() or + target = getARuntimeDelegateTarget(result, false) + ) + } } /** Gets a run-time target for the delegate call `c`. */ + pragma[nomagic] Callable getARuntimeDelegateTarget(Call c, boolean libraryDelegateCall) { - delegateCall(c, delegateCallSource(result), libraryDelegateCall) + // directly resolvable without going through a "busy" element + exists(Expr e | + delegateCreationReaches(result, e) and + delegateCall(c, e, libraryDelegateCall) + ) + or + // resolvable by going through one or more "busy" elements + exists(BusyFlowElement busy | + busy.isReachedBy(result) and + busy.delegateCall(c, libraryDelegateCall) + ) } } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll index f828419a440..f37a4f2d074 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll @@ -316,6 +316,15 @@ private module SsaDefReaches { ) } + /** + * Holds if the reference to `def` at index `i` in basic block `bb` is the + * last reference to `v` inside `bb`. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) + } + predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) { exists(ssaDefRank(def, v, bb, _, _)) } @@ -351,8 +360,7 @@ private module SsaDefReaches { */ predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { varBlockReaches(def, bb1, bb2) and - ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and - variableRead(bb2, i2, _, _) + ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 } } @@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2 bb2 = bb1 ) or - exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and + lastSsaRef(def, _, bb1, i1) and defAdjacentRead(def, bb1, bb2, i2) } +pragma[noinline] +private predicate adjacentDefRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v +) { + adjacentDefRead(def, bb1, i1, bb2, i2) and + v = def.getSourceVariable() +} + private predicate adjacentDefReachesRead( Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and - exists(SourceVariable v | v = def.getSourceVariable() | + exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) | ssaRef(bb1, i1, v, SsaDef()) or variableRead(bb1, i1, v, true) @@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba */ pragma[nomagic] predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { - exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) | + exists(SourceVariable v | // Next reference to `v` inside `bb` is a write - next.definesAt(v, bb, j) and - rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j) and + rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + ) or // Can reach a write using one or more steps - rnk = maxSsaRefRank(bb, v) and + lastSsaRef(def, v, bb, i) and exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) and - next.definesAt(v, bb2, j) and - 1 = ssaRefRank(bb2, j, v, SsaDef()) + 1 = ssaDefRank(next, v, bb2, _, SsaDef()) ) ) } @@ -539,7 +556,8 @@ pragma[nomagic] predicate lastRef(Definition def, BasicBlock bb, int i) { lastRefRedef(def, bb, i, _) or - exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) | + lastSsaRef(def, _, bb, i) and + ( // Can reach exit directly bb instanceof ExitBasicBlock or diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll index f828419a440..f37a4f2d074 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll @@ -316,6 +316,15 @@ private module SsaDefReaches { ) } + /** + * Holds if the reference to `def` at index `i` in basic block `bb` is the + * last reference to `v` inside `bb`. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) + } + predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) { exists(ssaDefRank(def, v, bb, _, _)) } @@ -351,8 +360,7 @@ private module SsaDefReaches { */ predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { varBlockReaches(def, bb1, bb2) and - ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and - variableRead(bb2, i2, _, _) + ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 } } @@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2 bb2 = bb1 ) or - exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and + lastSsaRef(def, _, bb1, i1) and defAdjacentRead(def, bb1, bb2, i2) } +pragma[noinline] +private predicate adjacentDefRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v +) { + adjacentDefRead(def, bb1, i1, bb2, i2) and + v = def.getSourceVariable() +} + private predicate adjacentDefReachesRead( Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and - exists(SourceVariable v | v = def.getSourceVariable() | + exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) | ssaRef(bb1, i1, v, SsaDef()) or variableRead(bb1, i1, v, true) @@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba */ pragma[nomagic] predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { - exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) | + exists(SourceVariable v | // Next reference to `v` inside `bb` is a write - next.definesAt(v, bb, j) and - rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j) and + rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + ) or // Can reach a write using one or more steps - rnk = maxSsaRefRank(bb, v) and + lastSsaRef(def, v, bb, i) and exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) and - next.definesAt(v, bb2, j) and - 1 = ssaRefRank(bb2, j, v, SsaDef()) + 1 = ssaDefRank(next, v, bb2, _, SsaDef()) ) ) } @@ -539,7 +556,8 @@ pragma[nomagic] predicate lastRef(Definition def, BasicBlock bb, int i) { lastRefRedef(def, bb, i, _) or - exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) | + lastSsaRef(def, _, bb, i) and + ( // Can reach exit directly bb instanceof ExitBasicBlock or diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll index 1e0eaaf8017..cca858d69ba 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll @@ -233,71 +233,61 @@ private module Internal { } pragma[noinline] - private predicate hasOverrider(OverridableCallable oc, ValueOrRefType t) { - exists(oc.getAnOverrider(t)) + private predicate hasOverrider(OverridableCallable oc, Gvn::GvnType t) { + exists(oc.getAnOverrider(any(ValueOrRefType t0 | Gvn::getGlobalValueNumber(t0) = t))) } pragma[noinline] - private predicate hasCallable(OverridableCallable source, ValueOrRefType t, OverridableCallable c) { + private predicate hasCallable(OverridableCallable source, Gvn::GvnType t, OverridableCallable c) { c.getUnboundDeclaration() = source and - t.hasCallable(c) and + any(ValueOrRefType t0 | Gvn::getGlobalValueNumber(t0) = t).hasCallable(c) and hasOverrider(c, t) and - hasQualifierTypeOverridden0(t, _) and - hasQualifierTypeOverridden1(source, _) - } - - pragma[noinline] - private Unification::ConstrainedTypeParameter getAConstrainedTypeParameterQualifierType( - DispatchMethodOrAccessorCall call - ) { - result = getAPossibleType(call.getQualifier(), false) - } - - pragma[noinline] - private predicate constrainedTypeParameterQualifierTypeSubsumes( - ValueOrRefType t, Unification::ConstrainedTypeParameter tp - ) { - tp = getAConstrainedTypeParameterQualifierType(_) and - tp.subsumes(t) - } - - pragma[noinline] - private predicate hasQualifierTypeOverridden0(ValueOrRefType t, DispatchMethodOrAccessorCall call) { - hasOverrider(_, t) and - ( - exists(Type t0, Type t1 | - t0 = getAPossibleType(call.getQualifier(), false) and - t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()] - | - t = t1 - or - Unification::subsumes(t1, t) - ) - or - constrainedTypeParameterQualifierTypeSubsumes(t, - getAConstrainedTypeParameterQualifierType(call)) - ) - } - - pragma[noinline] - private predicate hasQualifierTypeOverridden1( - OverridableCallable c, DispatchMethodOrAccessorCall call - ) { - exists(OverridableCallable target | call.getAStaticTarget() = target | - c = target.getUnboundDeclaration() - or - c = target.getAnUltimateImplementor().getUnboundDeclaration() - ) + source = any(DispatchMethodOrAccessorCall call).getAStaticTargetExt() } abstract private class DispatchMethodOrAccessorCall extends DispatchCallImpl { + pragma[noinline] + OverridableCallable getAStaticTargetExt() { + exists(OverridableCallable target | this.getAStaticTarget() = target | + result = target.getUnboundDeclaration() + or + result = target.getAnUltimateImplementor().getUnboundDeclaration() + ) + } + pragma[nomagic] predicate hasQualifierTypeInherited(Type t) { t = getAPossibleType(this.getQualifier(), _) } + pragma[noinline] + private predicate hasSubsumedQualifierType(Gvn::GvnType t) { + hasOverrider(_, t) and + exists(Type t0 | + t0 = getAPossibleType(this.getQualifier(), false) and + not t0 instanceof TypeParameter + | + t = Gvn::getGlobalValueNumber(t0) + or + Gvn::subsumes(Gvn::getGlobalValueNumber(t0), t) + ) + } + + pragma[noinline] + private predicate hasConstrainedTypeParameterQualifierType( + Unification::ConstrainedTypeParameter tp + ) { + tp = getAPossibleType(this.getQualifier(), false) + } + + pragma[noinline] + private predicate hasUnconstrainedTypeParameterQualifierType() { + getAPossibleType(this.getQualifier(), false) instanceof + Unification::UnconstrainedTypeParameter + } + pragma[nomagic] - predicate hasQualifierTypeOverridden(ValueOrRefType t, OverridableCallable c) { - hasQualifierTypeOverridden0(t, this) and - hasCallable(any(OverridableCallable oc | hasQualifierTypeOverridden1(oc, this)), t, c) + predicate hasSubsumedQualifierTypeOverridden(Gvn::GvnType t, OverridableCallable c) { + this.hasSubsumedQualifierType(t) and + hasCallable(any(OverridableCallable oc | oc = this.getAStaticTargetExt()), t, c) } /** @@ -357,12 +347,33 @@ private module Internal { } pragma[nomagic] - private Callable getASubsumedStaticTarget0(Type t) { + private predicate contextArgHasConstrainedTypeParameterType( + DispatchCall ctx, Unification::ConstrainedTypeParameter tp + ) { + this.contextArgHasType(ctx, tp, false) + } + + pragma[nomagic] + private predicate contextArgHasUnconstrainedTypeParameterType(DispatchCall ctx) { + this.contextArgHasType(ctx, any(Unification::UnconstrainedTypeParameter t), false) + } + + pragma[nomagic] + private predicate contextArgHasNonTypeParameterType(DispatchCall ctx, Gvn::GvnType t) { + exists(Type t0 | + this.contextArgHasType(ctx, t0, false) and + not t0 instanceof TypeParameter and + t = Gvn::getGlobalValueNumber(t0) + ) + } + + pragma[nomagic] + private Callable getASubsumedStaticTarget0(Gvn::GvnType t) { exists(Callable staticTarget, Type declType | staticTarget = this.getAStaticTarget() and declType = staticTarget.getDeclaringType() and result = staticTarget.getUnboundDeclaration() and - Unification::subsumes(declType, t) + Gvn::subsumes(Gvn::getGlobalValueNumber(declType), t) ) } @@ -375,7 +386,8 @@ private module Internal { private Callable getASubsumedStaticTarget() { result = this.getAStaticTarget() or - result.getUnboundDeclaration() = this.getASubsumedStaticTarget0(result.getDeclaringType()) + result.getUnboundDeclaration() = + this.getASubsumedStaticTarget0(Gvn::getGlobalValueNumber(result.getDeclaringType())) } /** @@ -426,6 +438,12 @@ private module Internal { ) } + pragma[noinline] + NonConstructedOverridableCallable getAViableOverrider0() { + getAPossibleType(this.getQualifier(), false) instanceof TypeParameter and + result.getAConstructingCallableOrSelf() = this.getAStaticTargetExt() + } + /** * Gets a callable that is defined in a subtype of the qualifier type of this * call, and which overrides a static target of this call. @@ -468,9 +486,22 @@ private module Internal { */ private RuntimeCallable getAViableOverrider() { exists(ValueOrRefType t, NonConstructedOverridableCallable c | - this.hasQualifierTypeOverridden(t, c.getAConstructingCallableOrSelf()) and + this.hasSubsumedQualifierTypeOverridden(Gvn::getGlobalValueNumber(t), + c.getAConstructingCallableOrSelf()) and result = c.getAnOverrider(t) ) + or + exists(NonConstructedOverridableCallable c | + c = this.getAViableOverrider0() and + result = c.getAnOverrider(_) + | + this.hasUnconstrainedTypeParameterQualifierType() + or + exists(Unification::ConstrainedTypeParameter tp | + this.hasConstrainedTypeParameterQualifierType(tp) and + tp.subsumes(result.getDeclaringType()) + ) + ) } override RuntimeCallable getADynamicTarget() { @@ -510,36 +541,40 @@ private module Internal { } pragma[nomagic] - private RuntimeCallable getAViableOverriderInCallContext0( - NonConstructedOverridableCallable c, ValueOrRefType t - ) { - result = this.getAViableOverrider() and - this.contextArgHasType(_, _, false) and - result = c.getAnOverrider(t) + private RuntimeCallable getAViableOverriderInCallContext0(Gvn::GvnType t) { + exists(NonConstructedOverridableCallable c | + result = this.getAViableOverrider() and + this.contextArgHasType(_, _, false) and + result = c.getAnOverrider(any(Type t0 | t = Gvn::getGlobalValueNumber(t0))) and + this.getAStaticTarget() = c.getAConstructingCallableOrSelf() + ) } pragma[nomagic] - private RuntimeCallable getAViableOverriderInCallContext1( - NonConstructedOverridableCallable c, DispatchCall ctx - ) { - exists(ValueOrRefType t | - result = this.getAViableOverriderInCallContext0(c, t) and - exists(Type t0, Type t1 | - this.contextArgHasType(ctx, t0, false) and - t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()] - | - t = t1 - or - Unification::subsumes(t1, t) - ) + private predicate contextArgHasSubsumedType(DispatchCall ctx, Gvn::GvnType t) { + hasOverrider(_, t) and + exists(Gvn::GvnType t0 | this.contextArgHasNonTypeParameterType(ctx, t0) | + t = t0 + or + Gvn::subsumes(t0, t) ) } pragma[nomagic] private RuntimeCallable getAViableOverriderInCallContext(DispatchCall ctx) { - exists(NonConstructedOverridableCallable c | - result = this.getAViableOverriderInCallContext1(c, ctx) and - this.getAStaticTarget() = c.getAConstructingCallableOrSelf() + exists(Gvn::GvnType t | + result = this.getAViableOverriderInCallContext0(t) and + this.contextArgHasSubsumedType(ctx, t) + ) + or + result = this.getAViableOverrider() and + ( + this.contextArgHasUnconstrainedTypeParameterType(ctx) + or + exists(Unification::ConstrainedTypeParameter tp | + this.contextArgHasConstrainedTypeParameterType(ctx, tp) and + tp.subsumes(result.getDeclaringType()) + ) ) } @@ -636,31 +671,41 @@ private module Internal { ) } - private predicate stepExpr0(Expr succ, Expr pred) { - Steps::stepOpen(pred, succ) - or - exists(Assignable a | - a instanceof Field or - a instanceof Property - | - succ.(AssignableRead) = a.getAnAccess() and - pred = a.getAnAssignedValue() and - a = any(Modifiable m | not m.isEffectivelyPublic()) - ) - } - - private predicate stepExpr(Expr succ, Expr pred) { - stepExpr0(succ, pred) and + private predicate stepExpr(Expr pred, Expr succ) { + Steps::stepOpen(pred, succ) and // Do not step through down casts not downCast(succ) and // Only step when we may learn more about the actual type typeMayBeImprecise(succ.getType()) } - private predicate stepTC(Expr succ, Expr pred) = fastTC(stepExpr/2)(succ, pred) + private class AnalyzableFieldOrProperty extends Assignable, Modifiable { + AnalyzableFieldOrProperty() { + ( + this instanceof Field or + this instanceof Property + ) and + not this.isEffectivelyPublic() and + exists(this.getAnAssignedValue()) + } + + AssignableRead getARead() { result = this.getAnAccess() } + } private class Source extends Expr { - Source() { not stepExpr(this, _) } + Source() { + not stepExpr(_, this) and + not this = any(AnalyzableFieldOrProperty a).getARead() + } + + Type getType(boolean isExact) { + result = this.getType() and + if + this instanceof ObjectCreation or + this instanceof BaseAccess + then isExact = true + else isExact = false + } } private class Sink extends Expr { @@ -680,24 +725,38 @@ private module Internal { this = any(DispatchCallImpl c).getArgument(_) } - Source getASource() { stepTC(this, result) } + pragma[nomagic] + Expr getAPred() { stepExpr*(result, this) } + + pragma[nomagic] + AnalyzableFieldOrProperty getAPredRead() { this.getAPred() = result.getARead() } } - /** Holds if the expression `e` has an exact type. */ - private predicate hasExactType(Expr e) { - e instanceof ObjectCreation or - e instanceof BaseAccess + /** Gets a source type for sink expression `e`, using simple data flow. */ + Type getASourceType(Sink sink, boolean isExact) { + result = sink.getAPred().(Source).getType(isExact) + or + result = sink.getAPredRead().(RelevantFieldOrProperty).getASourceType(isExact) } - /** Gets a source type for expression `e`, using simple data flow. */ - Type getASourceType(Sink e, boolean isExact) { - exists(Source s | - s = e.getASource() or - s = e - | - result = s.getType() and - if hasExactType(s) then isExact = true else isExact = false - ) + private class RelevantFieldOrProperty extends AnalyzableFieldOrProperty { + RelevantFieldOrProperty() { + this = any(Sink s).getAPredRead() + or + this = any(RelevantFieldOrProperty a).getAPredRead() + } + + pragma[nomagic] + Expr getAPred() { stepExpr*(result, this.getAnAssignedValue()) } + + pragma[nomagic] + AnalyzableFieldOrProperty getAPredRead() { this.getAPred() = result.getARead() } + + Type getASourceType(boolean isExact) { + result = this.getAPred().(Source).getType(isExact) + or + result = this.getAPredRead().(RelevantFieldOrProperty).getASourceType(isExact) + } } } diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/Dapper.qll b/csharp/ql/src/semmle/code/csharp/frameworks/Dapper.qll new file mode 100644 index 00000000000..3d25f4389fd --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/frameworks/Dapper.qll @@ -0,0 +1,42 @@ +/** + * Classes for modeling Dapper. + */ + +import csharp +private import semmle.code.csharp.frameworks.system.Data + +/** Definitions relating to the `Dapper` package. */ +module Dapper { + /** The namespace `Dapper`. */ + class DapperNamespace extends Namespace { + DapperNamespace() { this.hasQualifiedName("Dapper") } + } + + /** A class in `Dapper`. */ + class DapperClass extends Class { + DapperClass() { this.getParent() instanceof DapperNamespace } + } + + /** A struct in `Dapper`. */ + class DapperStruct extends Struct { + DapperStruct() { this.getParent() instanceof DapperNamespace } + } + + /** The `Dapper.SqlMapper` class. */ + class SqlMapperClass extends DapperClass { + SqlMapperClass() { this.hasName("SqlMapper") } + + /** Gets a DB query method. */ + ExtensionMethod getAQueryMethod() { + result = this.getAMethod() and + result.getName().regexpMatch("Query.*|Execute.*") and + result.getExtendedType() instanceof SystemDataIDbConnectionInterface and + result.isPublic() + } + } + + /** The `Dapper.CommandDefinition` struct. */ + class CommandDefinitionStruct extends DapperStruct { + CommandDefinitionStruct() { this.hasName("CommandDefinition") } + } +} diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/Sql.qll b/csharp/ql/src/semmle/code/csharp/frameworks/Sql.qll index 5774b22a631..1a727030bea 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/Sql.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/Sql.qll @@ -5,6 +5,8 @@ private import semmle.code.csharp.frameworks.system.Data private import semmle.code.csharp.frameworks.system.data.SqlClient private import semmle.code.csharp.frameworks.EntityFramework private import semmle.code.csharp.frameworks.NHibernate +private import semmle.code.csharp.frameworks.Dapper +private import semmle.code.csharp.dataflow.DataFlow4 /** An expression containing a SQL command. */ abstract class SqlExpr extends Expr { @@ -83,3 +85,37 @@ class MicrosoftSqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall { ) } } + +/** A `Dapper.SqlMapper` method that is taking a SQL string argument. */ +class DapperSqlMethodCallSqlExpr extends SqlExpr, MethodCall { + DapperSqlMethodCallSqlExpr() { + this.getTarget() = any(Dapper::SqlMapperClass c).getAQueryMethod() + } + + override Expr getSql() { result = this.getArgumentForName("sql") } +} + +/** A `Dapper.CommandDefinition` creation that is taking a SQL string argument and is passed to a `Dapper.SqlMapper` method. */ +class DapperCommandDefinitionMethodCallSqlExpr extends SqlExpr, ObjectCreation { + DapperCommandDefinitionMethodCallSqlExpr() { + this.getObjectType() instanceof Dapper::CommandDefinitionStruct and + exists(Conf c | c.hasFlow(DataFlow::exprNode(this), _)) + } + + override Expr getSql() { result = this.getArgumentForName("commandText") } +} + +private class Conf extends DataFlow4::Configuration { + Conf() { this = "DapperCommandDefinitionFlowConfig" } + + override predicate isSource(DataFlow::Node node) { + node.asExpr().(ObjectCreation).getObjectType() instanceof Dapper::CommandDefinitionStruct + } + + override predicate isSink(DataFlow::Node node) { + exists(MethodCall mc | + mc.getTarget() = any(Dapper::SqlMapperClass c).getAQueryMethod() and + node.asExpr() = mc.getArgumentForName("command") + ) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/security/dataflow/HardcodedCredentials.qll b/csharp/ql/src/semmle/code/csharp/security/dataflow/HardcodedCredentials.qll index 4538f09e2c0..0ffabe60588 100644 --- a/csharp/ql/src/semmle/code/csharp/security/dataflow/HardcodedCredentials.qll +++ b/csharp/ql/src/semmle/code/csharp/security/dataflow/HardcodedCredentials.qll @@ -111,13 +111,24 @@ module HardcodedCredentials { } /** - * Gets a regular expression for matching names of locations (variables, parameters, keys) that - * indicate the value being held is a credential. + * An assignable whose name indicates that the value being held is a credential. */ - private string getACredentialRegex() { - result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or - result = "(?i).*(puid|username|userid).*" or - result = "(?i).*(cert)(?!.*(format|name)).*" + private class CredentialVar extends Assignable { + pragma[noinline] + CredentialVar() { + exists(string name | name = this.getName() | + name.regexpMatch("(?i).*pass(wd|word|code|phrase)(?!.*question).*") + or + name.regexpMatch("(?i).*(puid|username|userid).*") + or + name.regexpMatch("(?i).*(cert)(?!.*(format|name)).*") + ) + } + } + + private class CredentialVariableAccess extends VariableAccess { + pragma[noinline] + CredentialVariableAccess() { this.getTarget() instanceof CredentialVar } } /** @@ -128,11 +139,11 @@ module HardcodedCredentials { ) { // An argument to a library call that looks like a credential // "...flows to the [Username] parameter in [call to method CreateUser]" - exists(Call call | + exists(Call call, CredentialVar param | supplementaryElement = call and description = "the $@ parameter in $@" and - sink = call.getArgumentForName(sinkName) and - sinkName.regexpMatch(getACredentialRegex()) and + sink = call.getArgumentForParameter(param) and + sinkName = param.getName() and call.getTarget().fromLibrary() ) or @@ -144,22 +155,20 @@ module HardcodedCredentials { description = "the $@ in $@" and sink = call.getArgument(0) and sinkName = "setter call argument" and - p.getName().regexpMatch(getACredentialRegex()) and + p instanceof CredentialVar and p.fromLibrary() ) or // Sink compared to password variable // "...flows to [] which is compared against [access of UserName]" - exists(ComparisonTest ct, VariableAccess credentialAccess, string varName | + exists(ComparisonTest ct, CredentialVariableAccess credentialAccess | sinkName = sink.toString() and supplementaryElement = credentialAccess and description = "$@ which is compared against $@" and ct.getAnArgument() = credentialAccess and ct.getAnArgument() = sink and ct.getComparisonKind().isEquality() and - not sink = credentialAccess and - varName = credentialAccess.getTarget().getName() and - varName.regexpMatch(getACredentialRegex()) + not sink = credentialAccess ) } diff --git a/csharp/ql/src/semmle/code/dotnet/Element.qll b/csharp/ql/src/semmle/code/dotnet/Element.qll index 956a5f2c052..c38b09ce270 100644 --- a/csharp/ql/src/semmle/code/dotnet/Element.qll +++ b/csharp/ql/src/semmle/code/dotnet/Element.qll @@ -28,6 +28,9 @@ class Element extends @dotnet_element { /** Holds if this element is from source code. */ predicate fromSource() { this.getFile().fromSource() } + /** Holds if this element is from an assembly. */ + predicate fromLibrary() { this.getFile().fromLibrary() } + /** * Gets the "language" of this program element, as defined by the extension of the filename. * For example, C# has language "cs", and Visual Basic has language "vb". diff --git a/csharp/ql/test/library-tests/controlflow/graph/Common.qll b/csharp/ql/test/library-tests/controlflow/graph/Common.qll index 5945d2003b2..f6f9b26f1cd 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Common.qll +++ b/csharp/ql/test/library-tests/controlflow/graph/Common.qll @@ -11,9 +11,15 @@ class SourceControlFlowElement extends ControlFlowElement { } class SourceControlFlowNode extends ControlFlow::Node { - SourceControlFlowNode() { not this.getLocation().getFile() instanceof StubFile } + SourceControlFlowNode() { + not this.getLocation().getFile() instanceof StubFile and + not this.getLocation().getFile().fromLibrary() + } } class SourceBasicBlock extends ControlFlow::BasicBlock { - SourceBasicBlock() { not this.getLocation().getFile() instanceof StubFile } + SourceBasicBlock() { + not this.getLocation().getFile() instanceof StubFile and + not this.getLocation().getFile().fromLibrary() + } } diff --git a/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql b/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql index 4a07618c373..2c637ff0db1 100644 --- a/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql +++ b/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql @@ -1,6 +1,7 @@ import csharp query predicate countSplits(ControlFlowElement cfe, int i) { + not cfe.fromLibrary() and i = strictcount(ControlFlow::Nodes::ElementNode n | n.getElement() = cfe) } diff --git a/csharp/ql/test/library-tests/csharp7.1/DefaultLiterals.ql b/csharp/ql/test/library-tests/csharp7.1/DefaultLiterals.ql index 63e5a69b6e5..578b7e7fef4 100644 --- a/csharp/ql/test/library-tests/csharp7.1/DefaultLiterals.ql +++ b/csharp/ql/test/library-tests/csharp7.1/DefaultLiterals.ql @@ -1,4 +1,5 @@ import csharp from DefaultValueExpr l +where l.fromSource() select l, l.getValue() diff --git a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.ql b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.ql index 22f61aacdd8..ed293efc2ba 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.ql +++ b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.ql @@ -2,5 +2,7 @@ import csharp import semmle.code.csharp.dataflow.TaintTracking from DataFlow::Node pred, DataFlow::Node succ -where TaintTracking::localTaintStep(pred, succ) +where + TaintTracking::localTaintStep(pred, succ) and + not pred.asExpr().fromLibrary() select pred, succ diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.ql b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.ql index f1fc98c459b..8f820cfea48 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.ql +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.ql @@ -1,5 +1,5 @@ import csharp from DataFlow::Node pred, DataFlow::Node succ -where DataFlow::localFlowStep(pred, succ) +where not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ) select pred, succ diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.ql b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.ql index 1866acfcda8..ffbdcd4dddf 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.ql +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.ql @@ -1,5 +1,7 @@ import csharp from DataFlow::Node pred, DataFlow::Node succ -where TaintTracking::localTaintStep(pred, succ) +where + not pred.asExpr().fromLibrary() and + TaintTracking::localTaintStep(pred, succ) select pred, succ diff --git a/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql index b07608ea009..02ffbc535ce 100644 --- a/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql +++ b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql @@ -4,5 +4,7 @@ import semmle.code.csharp.dataflow.ModulusAnalysis import semmle.code.csharp.dataflow.Bound from ControlFlow::Nodes::ExprNode e, Bound b, int delta, int mod -where exprModulus(e, b, delta, mod) +where + not e.getExpr().fromLibrary() and + exprModulus(e, b, delta, mod) select e, b.toString(), delta, mod diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql index 5427cdc631d..650d0f6bed4 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql @@ -18,4 +18,5 @@ string getASignString(ControlFlow::Nodes::ExprNode e) { } from ControlFlow::Nodes::ExprNode e +where not e.getExpr().fromLibrary() select e, strictconcat(string s | s = getASignString(e) | s, " ") diff --git a/csharp/ql/test/library-tests/dataflow/tuples/DataFlowStep.ql b/csharp/ql/test/library-tests/dataflow/tuples/DataFlowStep.ql index f1fc98c459b..8f820cfea48 100644 --- a/csharp/ql/test/library-tests/dataflow/tuples/DataFlowStep.ql +++ b/csharp/ql/test/library-tests/dataflow/tuples/DataFlowStep.ql @@ -1,5 +1,5 @@ import csharp from DataFlow::Node pred, DataFlow::Node succ -where DataFlow::localFlowStep(pred, succ) +where not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ) select pred, succ diff --git a/csharp/ql/test/library-tests/exprorstmtparent/Parameter.ql b/csharp/ql/test/library-tests/exprorstmtparent/Parameter.ql index 3c12d5b870f..33f6eeb2b9d 100644 --- a/csharp/ql/test/library-tests/exprorstmtparent/Parameter.ql +++ b/csharp/ql/test/library-tests/exprorstmtparent/Parameter.ql @@ -1,4 +1,5 @@ import csharp from Parameter p +where p.fromSource() select p, p.getDefaultValue() diff --git a/csharp/ql/test/library-tests/goto/Goto1.ql b/csharp/ql/test/library-tests/goto/Goto1.ql index e7ee0b3ff8a..97dd1fa2af0 100644 --- a/csharp/ql/test/library-tests/goto/Goto1.ql +++ b/csharp/ql/test/library-tests/goto/Goto1.ql @@ -1,6 +1,7 @@ import csharp query predicate edges(ControlFlow::Node node, ControlFlow::Node successor, string attr, string val) { + not node.getElement().fromLibrary() and exists(ControlFlow::SuccessorType t | successor = node.getASuccessorByType(t) | attr = "semmle.label" and val = t.toString() diff --git a/csharp/ql/test/library-tests/parameters/Parameters.cs b/csharp/ql/test/library-tests/parameters/Parameters.cs new file mode 100644 index 00000000000..b7cc3b001a9 --- /dev/null +++ b/csharp/ql/test/library-tests/parameters/Parameters.cs @@ -0,0 +1,17 @@ +public class Parameters +{ + public void M1(int a, object b, string c) => throw null; + public void M2(int a, object b = null, string c = "default string") => throw null; + public void M3(int a = 1, object b = null, string c = "null") => throw null; + public void M4(int a = default, object b = default) => throw null; + public void M5(int a = new int(), object b = default) => throw null; + public void M6(MyStruct s1, MyStruct s2 = default(MyStruct), MyStruct s3 = new MyStruct()) => throw null; + public void M7(MyEnum e1, MyEnum e2 = default(MyEnum), MyEnum e3 = new MyEnum(), MyEnum e4 = MyEnum.A, MyEnum e5 = (MyEnum)5) => throw null; + + public void M8(T t = default) => throw null; + public void M9(T t = default) where T : struct => throw null; + public void M10(T t = default) where T : class => throw null; + + public struct MyStruct { } + public enum MyEnum { A = 1, B = 2 } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/parameters/Parameters.cs_ b/csharp/ql/test/library-tests/parameters/Parameters.cs_ new file mode 100644 index 00000000000..062c4b98b18 --- /dev/null +++ b/csharp/ql/test/library-tests/parameters/Parameters.cs_ @@ -0,0 +1,17 @@ +public class ParametersDll +{ + public void M1(int a, object b, string c) => throw null; + public void M2(int a, object b = null, string c = "default string") => throw null; + public void M3(int a = 1, object b = null, string c = "null") => throw null; + public void M4(int a = default, object b = default) => throw null; + public void M5(int a = new int(), object b = default) => throw null; + public void M6(MyStruct s1, MyStruct s2 = default(MyStruct), MyStruct s3 = new MyStruct()) => throw null; + public void M7(MyEnum e1, MyEnum e2 = default(MyEnum), MyEnum e3 = new MyEnum(), MyEnum e4 = MyEnum.A, MyEnum e5 = (MyEnum)5) => throw null; + + public void M8(T t = default) => throw null; + public void M9(T t = default) where T : struct => throw null; + public void M10(T t = default) where T : class => throw null; + + public struct MyStruct { } + public enum MyEnum { A = 1, B = 2 } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/parameters/Parameters.dll b/csharp/ql/test/library-tests/parameters/Parameters.dll new file mode 100644 index 00000000000..cd48ebef015 Binary files /dev/null and b/csharp/ql/test/library-tests/parameters/Parameters.dll differ diff --git a/csharp/ql/test/library-tests/parameters/Parameters.expected b/csharp/ql/test/library-tests/parameters/Parameters.expected new file mode 100644 index 00000000000..864464167b9 --- /dev/null +++ b/csharp/ql/test/library-tests/parameters/Parameters.expected @@ -0,0 +1,50 @@ +noDefaultValue +| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:24:3:24 | a | 0 | +| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:34:3:34 | b | 1 | +| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:44:3:44 | c | 2 | +| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:24:4:24 | a | 0 | +| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:29:8:30 | s1 | 0 | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:27:9:28 | e1 | 0 | +| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | a | 0 | +| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | b | 1 | +| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | c | 2 | +| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | a | 0 | +| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s1 | 0 | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e1 | 0 | +withDefaultValue +| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:34:4:34 | b | 1 | Parameters.cs:4:38:4:41 | null | null | +| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:51:4:51 | c | 2 | Parameters.cs:4:55:4:70 | "default string" | default string | +| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:24:5:24 | a | 0 | Parameters.cs:5:28:5:28 | 1 | 1 | +| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:38:5:38 | b | 1 | Parameters.cs:5:42:5:45 | null | null | +| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:55:5:55 | c | 2 | Parameters.cs:5:59:5:64 | "null" | null | +| Parameters.cs:6:17:6:18 | M4 | Parameters.cs:6:24:6:24 | a | 0 | Parameters.cs:6:28:6:34 | (...) ... | 0 | +| Parameters.cs:6:17:6:18 | M4 | Parameters.cs:6:44:6:44 | b | 1 | Parameters.cs:6:48:6:54 | default | null | +| Parameters.cs:7:17:7:18 | M5 | Parameters.cs:7:24:7:24 | a | 0 | Parameters.cs:7:28:7:36 | object creation of type Int32 | 0 | +| Parameters.cs:7:17:7:18 | M5 | Parameters.cs:7:46:7:46 | b | 1 | Parameters.cs:7:50:7:56 | default | null | +| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:42:8:43 | s2 | 1 | Parameters.cs:8:47:8:63 | default(...) | - | +| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:75:8:76 | s3 | 2 | Parameters.cs:8:80:8:93 | object creation of type MyStruct | - | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:38:9:39 | e2 | 1 | Parameters.cs:9:43:9:57 | default(...) | 0 | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:67:9:68 | e3 | 2 | Parameters.cs:9:72:9:83 | object creation of type MyEnum | 0 | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:93:9:94 | e4 | 3 | Parameters.cs:9:98:9:105 | access to constant A | 1 | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:115:9:116 | e5 | 4 | Parameters.cs:9:120:9:128 | (...) ... | 5 | +| Parameters.cs:11:17:11:21 | M8 | Parameters.cs:11:25:11:25 | t | 0 | Parameters.cs:11:29:11:35 | (...) ... | - | +| Parameters.cs:12:17:12:21 | M9 | Parameters.cs:12:25:12:25 | t | 0 | Parameters.cs:12:29:12:35 | (...) ... | - | +| Parameters.cs:13:17:13:22 | M10 | Parameters.cs:13:26:13:26 | t | 0 | Parameters.cs:13:30:13:36 | (...) ... | null | +| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null | +| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "default string" | default string | +| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 1 | 1 | +| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null | +| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "null" | null | +| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 | +| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null | +| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 | +| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null | +| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s2 | 1 | Parameters.dll:0:0:0:0 | default | - | +| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s3 | 2 | Parameters.dll:0:0:0:0 | default | - | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e2 | 1 | Parameters.dll:0:0:0:0 | (...) ... | 0 | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e3 | 2 | Parameters.dll:0:0:0:0 | (...) ... | 0 | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e4 | 3 | Parameters.dll:0:0:0:0 | (...) ... | 1 | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e5 | 4 | Parameters.dll:0:0:0:0 | (...) ... | 5 | +| Parameters.dll:0:0:0:0 | M8 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | - | +| Parameters.dll:0:0:0:0 | M9 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | - | +| Parameters.dll:0:0:0:0 | M10 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | null | diff --git a/csharp/ql/test/library-tests/parameters/Parameters.ql b/csharp/ql/test/library-tests/parameters/Parameters.ql new file mode 100644 index 00000000000..dca5c2d9006 --- /dev/null +++ b/csharp/ql/test/library-tests/parameters/Parameters.ql @@ -0,0 +1,19 @@ +import csharp + +private predicate fromTestLocation(Element e) { + e.fromSource() or e.getFile().getStem() = "Parameters" +} + +query predicate noDefaultValue(Parameterizable container, Parameter p, int i) { + fromTestLocation(container) and + not p.hasDefaultValue() and + container.getParameter(i) = p +} + +query predicate withDefaultValue(Parameterizable container, Parameter p, int i, Expr e, string value) { + fromTestLocation(container) and + p.hasDefaultValue() and + container.getParameter(i) = p and + p.getDefaultValue() = e and + if exists(e.getValue()) then value = e.getValue() else value = "-" +} diff --git a/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql b/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql index 88a0a00c632..4936dc9b4cd 100644 --- a/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql +++ b/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql @@ -17,4 +17,6 @@ class UnknownLocalVariableDeclExpr extends LocalVariableDeclAndInitExpr { override string toString() { result = "(unknown type) " + this.getName() } } -query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) { n2 = n1.getASuccessor() } +query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) { + not n1.getElement().fromLibrary() and n2 = n1.getASuccessor() +} diff --git a/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected b/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected index ff6d7e236a4..13bdd30b1c4 100644 --- a/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected +++ b/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected @@ -1,2 +1,3 @@ compilationMessages extractorMessages +| file://:0:0:0:0 | Extracting default argument value 'object RecordNumber = default' instead of 'object RecordNumber = -1'. The latter is not supported in C#. | diff --git a/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.expected b/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.expected new file mode 100644 index 00000000000..b5a514b9ffa --- /dev/null +++ b/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.expected @@ -0,0 +1 @@ +| 4 | diff --git a/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.qlref b/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.qlref new file mode 100644 index 00000000000..8c18065043f --- /dev/null +++ b/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.qlref @@ -0,0 +1 @@ +Metrics/Summaries/LinesOfCode.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Metrics/Summaries/file1.cs b/csharp/ql/test/query-tests/Metrics/Summaries/file1.cs new file mode 100644 index 00000000000..0c12535ee09 --- /dev/null +++ b/csharp/ql/test/query-tests/Metrics/Summaries/file1.cs @@ -0,0 +1,14 @@ + +class C1 +{ + /* + int M() + { + return 0; + } + */ + + // int M() => 0; + + int M() => 0; // Comment +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.cs b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.cs index 09a01281f79..f3593096e1e 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.cs @@ -1,4 +1,4 @@ -// semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs ${testdir}/../../../resources/stubs/System.Windows.cs +// semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs ${testdir}/../../../resources/stubs/System.Windows.cs ${testdir}/../../../resources/stubs/Dapper.cs /r:System.Linq.Expressions.dll using System; diff --git a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected index 61dffee741f..2ad651f8c0e 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected @@ -5,6 +5,13 @@ edges | SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:74:56:74:61 | access to local variable query1 | | SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:75:55:75:60 | access to local variable query1 | | SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 | +| SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | +| SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | +| SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | +| SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | +| SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | +| SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | +| SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | nodes | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | semmle.label | access to field categoryTextBox : TextBox | | SqlInjection.cs:38:21:38:40 | access to property Text : String | semmle.label | access to property Text : String | @@ -15,8 +22,29 @@ nodes | SqlInjection.cs:75:55:75:60 | access to local variable query1 | semmle.label | access to local variable query1 | | SqlInjection.cs:87:21:87:29 | access to property Text : String | semmle.label | access to property Text : String | | SqlInjection.cs:88:50:88:55 | access to local variable query1 | semmle.label | access to local variable query1 | +| SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | semmle.label | access to local variable query | #select | SqlInjection.cs:39:50:39:55 | access to local variable query1 | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | SqlInjection.cs:39:50:39:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | this ASP.NET user input | | SqlInjection.cs:74:56:74:61 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:74:56:74:61 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input | | SqlInjection.cs:75:55:75:60 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:75:55:75:60 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input | | SqlInjection.cs:88:50:88:55 | access to local variable query1 | SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:87:21:87:29 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | this TextBox text | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjectionDapper.cs b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjectionDapper.cs new file mode 100644 index 00000000000..ec54c70ddeb --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjectionDapper.cs @@ -0,0 +1,95 @@ +using System; + +namespace Test +{ + using System.Data; + using System.Data.Entity; + using System.Data.SqlClient; + using System.Web.UI.WebControls; + using System.Threading.Tasks; + using Dapper; + + class SqlInjectionDapper + { + string connectionString; + + public void Bad01() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + var result = connection.Query(query); + } + } + + public async Task Bad02() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + var result = await connection.QueryAsync(query); + } + } + + public async Task Bad03() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + var result = await connection.QueryFirstAsync(query); + } + } + + public async Task Bad04() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + + await connection.ExecuteAsync(query); + } + } + + public void Bad05() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + connection.ExecuteScalar(query); + } + } + + public void Bad06() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + connection.ExecuteReader(query); + } + } + + public async Task Bad07() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + + var comDef = new CommandDefinition(query); + var result = await connection.QueryFirstAsync(comDef); + } + } + + public async Task Ok07() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + + var comDef = new CommandDefinition(query); + // no call to any query method + } + } + + System.Windows.Forms.TextBox box1; + } +} diff --git a/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected b/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected index d2dd3037e3c..d4a68c97906 100644 --- a/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected +++ b/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected @@ -1 +1 @@ -| // This file contains auto-generated code.\n// original-extractor-options: /r:System.Text.RegularExpressions.dll /r:System.Collections.Specialized.dll /r:System.Net.dll /r:System.Web.dll /r:System.Net.HttpListener.dll /r:System.Collections.Specialized.dll /r:System.Private.Uri.dll /r:System.Runtime.Extensions.dll /r:System.Linq.Parallel.dll /r:System.Collections.Concurrent.dll /r:System.Linq.Expressions.dll /r:System.Collections.dll /r:System.Linq.Queryable.dll /r:System.Linq.dll /r:System.Collections.NonGeneric.dll /r:System.ObjectModel.dll /r:System.ComponentModel.TypeConverter.dll /r:System.IO.Compression.dll /r:System.IO.Pipes.dll /r:System.Net.Primitives.dll /r:System.Net.Security.dll /r:System.Security.Cryptography.Primitives.dll /r:System.Text.RegularExpressions.dll ${testdir}/../../resources/stubs/System.Web.cs /r:System.Runtime.Serialization.Primitives.dll\n\nnamespace System\n{\n// Generated from `System.Uri` in `System.Private.Uri, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Uri : System.Runtime.Serialization.ISerializable\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) => throw null;\n}\n\nnamespace Collections\n{\n// Generated from `System.Collections.Queue` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Queue : System.ICloneable, System.Collections.IEnumerable, System.Collections.ICollection\n{\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual void CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.SortedList` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedList : System.ICloneable, System.Collections.IEnumerable, System.Collections.IDictionary, System.Collections.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public virtual System.Collections.ICollection Keys { get => throw null; }\n public virtual System.Collections.ICollection Values { get => throw null; }\n public virtual System.Collections.IDictionaryEnumerator GetEnumerator() => throw null;\n public virtual bool Contains(object key) => throw null;\n public virtual bool IsFixedSize { get => throw null; }\n public virtual bool IsReadOnly { get => throw null; }\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object GetByIndex(int index) => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual object this[object key] { get => throw null; set => throw null; }\n public virtual void Add(object key, object value) => throw null;\n public virtual void Clear() => throw null;\n public virtual void CopyTo(System.Array array, int arrayIndex) => throw null;\n public virtual void Remove(object key) => throw null;\n}\n\n// Generated from `System.Collections.Stack` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.ICloneable, System.Collections.IEnumerable, System.Collections.ICollection\n{\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual void CopyTo(System.Array array, int index) => throw null;\n}\n\nnamespace Concurrent\n{\n// Generated from `System.Collections.Concurrent.BlockingCollection<>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class BlockingCollection : System.IDisposable, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public int Count { get => throw null; }\n public void Dispose() => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Concurrent.BlockingCollectionDebugView<>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass BlockingCollectionDebugView\n{\n}\n\n// Generated from `System.Collections.Concurrent.IDictionaryDebugView<,>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass IDictionaryDebugView\n{\n}\n\n}\nnamespace Generic\n{\n// Generated from `System.Collections.Generic.CollectionDebugView<>` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass CollectionDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.DictionaryDebugView<,>` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass DictionaryDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.QueueDebugView<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass QueueDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.SortedSet<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedSet : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.ISet, System.Collections.Generic.IReadOnlySet, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public bool Add(T item) => throw null;\n public bool IsProperSubsetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsProperSupersetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsSubsetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsSupersetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool Overlaps(System.Collections.Generic.IEnumerable other) => throw null;\n public bool Remove(T item) => throw null;\n public bool SetEquals(System.Collections.Generic.IEnumerable other) => throw null;\n public int Count { get => throw null; }\n public virtual bool Contains(T item) => throw null;\n public virtual void Clear() => throw null;\n public virtual void IntersectWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void CopyTo(T[] array, int index) => throw null;\n public void ExceptWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void SymmetricExceptWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void UnionWith(System.Collections.Generic.IEnumerable other) => throw null;\n void System.Collections.Generic.ICollection.Add(T item) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object sender) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n// Generated from `System.Collections.Generic.Stack<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public T Peek() => throw null;\n public int Count { get => throw null; }\n void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) => throw null;\n}\n\n// Generated from `System.Collections.Generic.StackDebugView<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass StackDebugView\n{\n}\n\n}\nnamespace Specialized\n{\n// Generated from `System.Collections.Specialized.NameObjectCollectionBase` in `System.Collections.Specialized, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class NameObjectCollectionBase : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection\n{\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual int Count { get => throw null; }\n public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n public virtual void OnDeserialization(object sender) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Specialized.NameValueCollection` in `System.Collections.Specialized, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NameValueCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n public string this[string name] { get => throw null; set => throw null; }\n}\n\n}\n}\nnamespace ComponentModel\n{\n// Generated from `System.ComponentModel.ComponentConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ComponentConverter : System.ComponentModel.ReferenceConverter\n{\n}\n\n// Generated from `System.ComponentModel.DefaultEventAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DefaultEventAttribute : System.Attribute\n{\n public DefaultEventAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.DefaultPropertyAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DefaultPropertyAttribute : System.Attribute\n{\n public DefaultPropertyAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.INotifyPropertyChanged` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface INotifyPropertyChanged\n{\n}\n\n// Generated from `System.ComponentModel.ReferenceConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ReferenceConverter : System.ComponentModel.TypeConverter\n{\n}\n\n// Generated from `System.ComponentModel.TypeConverterAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeConverterAttribute : System.Attribute\n{\n public TypeConverterAttribute() => throw null;\n public TypeConverterAttribute(System.Type type) => throw null;\n public TypeConverterAttribute(string typeName) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.TypeConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeConverter\n{\n}\n\n// Generated from `System.ComponentModel.TypeDescriptionProviderAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeDescriptionProviderAttribute : System.Attribute\n{\n public TypeDescriptionProviderAttribute(System.Type type) => throw null;\n public TypeDescriptionProviderAttribute(string typeName) => throw null;\n}\n\n// Generated from `System.ComponentModel.TypeDescriptionProvider` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class TypeDescriptionProvider\n{\n}\n\n// Generated from `System.ComponentModel.TypeDescriptor` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeDescriptor\n{\n}\n\nnamespace Design\n{\n// Generated from `System.ComponentModel.Design.DesignerOptionService` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class DesignerOptionService : System.ComponentModel.Design.IDesignerOptionService\n{\n}\n\n// Generated from `System.ComponentModel.Design.IDesignerOptionService` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IDesignerOptionService\n{\n}\n\n}\n}\nnamespace Dynamic\n{\n// Generated from `System.Dynamic.DynamicMetaObject` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DynamicMetaObject\n{\n}\n\n// Generated from `System.Dynamic.ExpandoObject` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ExpandoObject : System.Dynamic.IDynamicMetaObjectProvider, System.ComponentModel.INotifyPropertyChanged, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>, System.Collections.Generic.IDictionary, System.Collections.Generic.ICollection>\n{\n System.Collections.Generic.ICollection System.Collections.Generic.IDictionary.Values { get => throw null; }\n System.Collections.Generic.ICollection System.Collections.Generic.IDictionary.Keys { get => throw null; }\n System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection>.Contains(System.Collections.Generic.KeyValuePair item) => throw null;\n bool System.Collections.Generic.ICollection>.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection>.Remove(System.Collections.Generic.KeyValuePair item) => throw null;\n bool System.Collections.Generic.IDictionary.ContainsKey(string key) => throw null;\n bool System.Collections.Generic.IDictionary.Remove(string key) => throw null;\n bool System.Collections.Generic.IDictionary.TryGetValue(string key, out object value) => throw null;\n int System.Collections.Generic.ICollection>.Count { get => throw null; }\n object this[string key] { get => throw null; set => throw null; }\n void System.Collections.Generic.ICollection>.Add(System.Collections.Generic.KeyValuePair item) => throw null;\n void System.Collections.Generic.ICollection>.Clear() => throw null;\n void System.Collections.Generic.ICollection>.CopyTo(System.Collections.Generic.KeyValuePair[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IDictionary.Add(string key, object value) => throw null;\n}\n\n// Generated from `System.Dynamic.IDynamicMetaObjectProvider` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IDynamicMetaObjectProvider\n{\n}\n\nnamespace Utils\n{\n// Generated from `System.Dynamic.Utils.ListProvider<>` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class ListProvider : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection where T: class\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public T this[int index] { get => throw null; set => throw null; }\n public bool Contains(T item) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool Remove(T item) => throw null;\n public int Count { get => throw null; }\n public int IndexOf(T item) => throw null;\n public void Add(T item) => throw null;\n public void Clear() => throw null;\n public void CopyTo(T[] array, int index) => throw null;\n public void Insert(int index, T item) => throw null;\n public void RemoveAt(int index) => throw null;\n}\n\n}\n}\nnamespace IO\n{\nnamespace Compression\n{\n// Generated from `System.IO.Compression.DeflateStream` in `System.IO.Compression, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089`\npublic class DeflateStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] array, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task CopyToAsync(System.IO.Stream destination, int bufferSize, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] array, int offset, int count) => throw null;\n public override int Read(System.Span buffer) => throw null;\n public override int ReadByte() => throw null;\n public override void CopyTo(System.IO.Stream destination, int bufferSize) => throw null;\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] array, int offset, int count) => throw null;\n public override void Write(System.ReadOnlySpan buffer) => throw null;\n}\n\n}\n}\nnamespace Linq\n{\n// Generated from `System.Linq.Enumerable` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Enumerable\n{\n public static System.Collections.Generic.IEnumerable Select(this System.Collections.Generic.IEnumerable source, System.Func selector) => throw null;\n}\n\n// Generated from `System.Linq.Grouping<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Grouping : System.Linq.IGrouping, System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n TElement this[int index] { get => throw null; set => throw null; }\n bool System.Collections.Generic.ICollection.Contains(TElement item) => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection.Remove(TElement item) => throw null;\n int System.Collections.Generic.ICollection.Count { get => throw null; }\n int System.Collections.Generic.IList.IndexOf(TElement item) => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n void System.Collections.Generic.ICollection.Add(TElement item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.ICollection.CopyTo(TElement[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IList.Insert(int index, TElement item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.IGrouping<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IGrouping : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IIListProvider<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\ninterface IIListProvider : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.ILookup<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface ILookup : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n}\n\n// Generated from `System.Linq.IOrderedEnumerable<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IOrderedEnumerable : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IPartition<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\ninterface IPartition : System.Linq.IIListProvider, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IQueryable` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IQueryable : System.Collections.IEnumerable\n{\n}\n\n// Generated from `System.Linq.Lookup<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Lookup : System.Linq.ILookup, System.Linq.IIListProvider>, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator> GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.OrderedEnumerable<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class OrderedEnumerable : System.Linq.IPartition, System.Linq.IOrderedEnumerable, System.Linq.IIListProvider, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.ParallelEnumerable` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class ParallelEnumerable\n{\n public static System.Linq.ParallelQuery AsParallel(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Linq.ParallelQuery, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n public virtual System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Collections.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Queryable` in `System.Linq.Queryable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Queryable\n{\n public static System.Linq.IQueryable AsQueryable(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.SystemLinq_GroupingDebugView<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass SystemLinq_GroupingDebugView\n{\n}\n\n// Generated from `System.Linq.SystemLinq_LookupDebugView<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass SystemLinq_LookupDebugView\n{\n}\n\nnamespace Expressions\n{\n// Generated from `System.Linq.Expressions.BlockExpressionList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass BlockExpressionList : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public System.Linq.Expressions.Expression this[int index] { get => throw null; set => throw null; }\n public bool Contains(System.Linq.Expressions.Expression item) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool Remove(System.Linq.Expressions.Expression item) => throw null;\n public int Count { get => throw null; }\n public int IndexOf(System.Linq.Expressions.Expression item) => throw null;\n public void Add(System.Linq.Expressions.Expression item) => throw null;\n public void Clear() => throw null;\n public void CopyTo(System.Linq.Expressions.Expression[] array, int index) => throw null;\n public void Insert(int index, System.Linq.Expressions.Expression item) => throw null;\n public void RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.Expressions.Expression` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class Expression\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Linq.Expressions.ParameterExpression` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParameterExpression : System.Linq.Expressions.Expression\n{\n}\n\nnamespace Compiler\n{\n// Generated from `System.Linq.Expressions.Compiler.ParameterList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ParameterList : System.Collections.IEnumerable, System.Collections.Generic.IReadOnlyList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public System.Linq.Expressions.ParameterExpression this[int index] { get => throw null; }\n public int Count { get => throw null; }\n}\n\n}\nnamespace Interpreter\n{\n// Generated from `System.Linq.Expressions.Interpreter.InstructionArray` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstruct InstructionArray\n{\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InstructionList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass InstructionList\n{\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InterpretedFrameInfo` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstruct InterpretedFrameInfo\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InterpretedFrame` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass InterpretedFrame\n{\n}\n\n}\n}\nnamespace Parallel\n{\n// Generated from `System.Linq.Parallel.ListChunk<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ListChunk : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.Lookup<,>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass Lookup : System.Linq.ILookup, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator> GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.PartitionerQueryOperator<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass PartitionerQueryOperator : System.Linq.Parallel.QueryOperator\n{\n}\n\n// Generated from `System.Linq.Parallel.QueryOperator<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class QueryOperator : System.Linq.ParallelQuery\n{\n public override System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.QueryResults<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class QueryResults : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection.Contains(T item) => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection.Remove(T item) => throw null;\n int System.Collections.Generic.IList.IndexOf(T item) => throw null;\n public T this[int index] { get => throw null; set => throw null; }\n public int Count { get => throw null; }\n void System.Collections.Generic.ICollection.Add(T item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.ICollection.CopyTo(T[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IList.Insert(int index, T item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.Parallel.ZipQueryOperator<,,>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ZipQueryOperator : System.Linq.Parallel.QueryOperator\n{\n}\n\n}\n}\nnamespace Net\n{\n// Generated from `System.Net.CookieCollection` in `System.Net.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class CookieCollection : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.IEnumerator GetEnumerator() => throw null;\n public bool Contains(System.Net.Cookie cookie) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool IsSynchronized { get => throw null; }\n public bool Remove(System.Net.Cookie cookie) => throw null;\n public int Count { get => throw null; }\n public object SyncRoot { get => throw null; }\n public void Add(System.Net.Cookie cookie) => throw null;\n public void Clear() => throw null;\n public void CopyTo(System.Array array, int index) => throw null;\n public void CopyTo(System.Net.Cookie[] array, int index) => throw null;\n}\n\n// Generated from `System.Net.Cookie` in `System.Net.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Cookie\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Net.StreamFramer` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass StreamFramer\n{\n}\n\nnamespace Security\n{\n// Generated from `System.Net.Security.AuthenticatedStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class AuthenticatedStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n}\n\n// Generated from `System.Net.Security.CipherSuitesPolicy` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class CipherSuitesPolicy\n{\n}\n\n// Generated from `System.Net.Security.NegotiateStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NegotiateStream : System.Net.Security.AuthenticatedStream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanTimeout { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int ReadTimeout { get => throw null; set => throw null; }\n public override int WriteTimeout { get => throw null; set => throw null; }\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n}\n\n// Generated from `System.Net.Security.SslStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SslStream : System.Net.Security.AuthenticatedStream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanTimeout { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int ReadByte() => throw null;\n public override int ReadTimeout { get => throw null; set => throw null; }\n public override int WriteTimeout { get => throw null; set => throw null; }\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n}\n\n// Generated from `System.Net.Security.TlsCipherSuite` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic enum TlsCipherSuite\n{\n}\n\n}\n}\nnamespace Runtime\n{\nnamespace Serialization\n{\n// Generated from `System.Runtime.Serialization.DataContractAttribute` in `System.Runtime.Serialization.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataContractAttribute : System.Attribute\n{\n public DataContractAttribute() => throw null;\n}\n\n// Generated from `System.Runtime.Serialization.DataMemberAttribute` in `System.Runtime.Serialization.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataMemberAttribute : System.Attribute\n{\n public DataMemberAttribute() => throw null;\n}\n\n}\n}\nnamespace Text\n{\nnamespace RegularExpressions\n{\n// Generated from `System.Text.RegularExpressions.Capture` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Capture\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.CollectionDebuggerProxy<>` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass CollectionDebuggerProxy\n{\n}\n\n// Generated from `System.Text.RegularExpressions.GroupCollection` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class GroupCollection : System.Collections.IList, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyList, System.Collections.Generic.IReadOnlyDictionary, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyCollection>, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable>, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() => throw null;\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Text.RegularExpressions.Group this[int index] { get => throw null; set => throw null; }\n bool System.Collections.Generic.ICollection.Contains(System.Text.RegularExpressions.Group item) => throw null;\n bool System.Collections.Generic.ICollection.Remove(System.Text.RegularExpressions.Group item) => throw null;\n bool System.Collections.IList.Contains(object value) => throw null;\n bool System.Collections.IList.IsFixedSize { get => throw null; }\n int System.Collections.Generic.IList.IndexOf(System.Text.RegularExpressions.Group item) => throw null;\n int System.Collections.IList.Add(object value) => throw null;\n int System.Collections.IList.IndexOf(object value) => throw null;\n object this[int index] { get => throw null; set => throw null; }\n public System.Collections.Generic.IEnumerable Values { get => throw null; }\n public System.Collections.Generic.IEnumerable Keys { get => throw null; }\n public System.Collections.IEnumerator GetEnumerator() => throw null;\n public System.Text.RegularExpressions.Group this[string groupname] { get => throw null; }\n public bool ContainsKey(string key) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool IsSynchronized { get => throw null; }\n public bool TryGetValue(string key, out System.Text.RegularExpressions.Group value) => throw null;\n public int Count { get => throw null; }\n public object SyncRoot { get => throw null; }\n public void CopyTo(System.Array array, int arrayIndex) => throw null;\n public void CopyTo(System.Text.RegularExpressions.Group[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.ICollection.Add(System.Text.RegularExpressions.Group item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.IList.Insert(int index, System.Text.RegularExpressions.Group item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n void System.Collections.IList.Clear() => throw null;\n void System.Collections.IList.Insert(int index, object value) => throw null;\n void System.Collections.IList.Remove(object value) => throw null;\n void System.Collections.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.Group` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Group : System.Text.RegularExpressions.Capture\n{\n}\n\n// Generated from `System.Text.RegularExpressions.Match` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Match : System.Text.RegularExpressions.Group\n{\n}\n\n// Generated from `System.Text.RegularExpressions.RegexOptions` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\n[System.Flags]\npublic enum RegexOptions\n{\n IgnoreCase,\n}\n\n// Generated from `System.Text.RegularExpressions.Regex` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Regex : System.Runtime.Serialization.ISerializable\n{\n public Regex(string pattern) => throw null;\n public Regex(string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public System.Text.RegularExpressions.Match Match(string input) => throw null;\n public override string ToString() => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public string Replace(string input, string replacement) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n}\n}\nnamespace Timers\n{\n// Generated from `System.Timers.TimersDescriptionAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TimersDescriptionAttribute\n{\n public TimersDescriptionAttribute(string description) => throw null;\n}\n\n}\nnamespace Windows\n{\nnamespace Markup\n{\n// Generated from `System.Windows.Markup.ValueSerializerAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ValueSerializerAttribute : System.Attribute\n{\n public ValueSerializerAttribute(System.Type valueSerializerType) => throw null;\n public ValueSerializerAttribute(string valueSerializerTypeName) => throw null;\n}\n\n}\n}\n}\n | +| // This file contains auto-generated code.\n// original-extractor-options: /r:System.Text.RegularExpressions.dll /r:System.Collections.Specialized.dll /r:System.Net.dll /r:System.Web.dll /r:System.Net.HttpListener.dll /r:System.Collections.Specialized.dll /r:System.Private.Uri.dll /r:System.Runtime.Extensions.dll /r:System.Linq.Parallel.dll /r:System.Collections.Concurrent.dll /r:System.Linq.Expressions.dll /r:System.Collections.dll /r:System.Linq.Queryable.dll /r:System.Linq.dll /r:System.Collections.NonGeneric.dll /r:System.ObjectModel.dll /r:System.ComponentModel.TypeConverter.dll /r:System.IO.Compression.dll /r:System.IO.Pipes.dll /r:System.Net.Primitives.dll /r:System.Net.Security.dll /r:System.Security.Cryptography.Primitives.dll /r:System.Text.RegularExpressions.dll ${testdir}/../../resources/stubs/System.Web.cs /r:System.Runtime.Serialization.Primitives.dll\n\nnamespace System\n{\n// Generated from `System.Uri` in `System.Private.Uri, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Uri : System.Runtime.Serialization.ISerializable\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) => throw null;\n}\n\nnamespace Collections\n{\n// Generated from `System.Collections.Queue` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Queue : System.ICloneable, System.Collections.IEnumerable, System.Collections.ICollection\n{\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual void CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.SortedList` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedList : System.ICloneable, System.Collections.IEnumerable, System.Collections.IDictionary, System.Collections.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public virtual System.Collections.ICollection Keys { get => throw null; }\n public virtual System.Collections.ICollection Values { get => throw null; }\n public virtual System.Collections.IDictionaryEnumerator GetEnumerator() => throw null;\n public virtual bool Contains(object key) => throw null;\n public virtual bool IsFixedSize { get => throw null; }\n public virtual bool IsReadOnly { get => throw null; }\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object GetByIndex(int index) => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual object this[object key] { get => throw null; set => throw null; }\n public virtual void Add(object key, object value) => throw null;\n public virtual void Clear() => throw null;\n public virtual void CopyTo(System.Array array, int arrayIndex) => throw null;\n public virtual void Remove(object key) => throw null;\n}\n\n// Generated from `System.Collections.Stack` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.ICloneable, System.Collections.IEnumerable, System.Collections.ICollection\n{\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual void CopyTo(System.Array array, int index) => throw null;\n}\n\nnamespace Concurrent\n{\n// Generated from `System.Collections.Concurrent.BlockingCollection<>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class BlockingCollection : System.IDisposable, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public int Count { get => throw null; }\n public void Dispose() => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Concurrent.BlockingCollectionDebugView<>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass BlockingCollectionDebugView\n{\n}\n\n// Generated from `System.Collections.Concurrent.IDictionaryDebugView<,>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass IDictionaryDebugView\n{\n}\n\n}\nnamespace Generic\n{\n// Generated from `System.Collections.Generic.CollectionDebugView<>` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass CollectionDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.DictionaryDebugView<,>` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass DictionaryDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.QueueDebugView<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass QueueDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.SortedSet<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedSet : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.ISet, System.Collections.Generic.IReadOnlySet, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public bool Add(T item) => throw null;\n public bool IsProperSubsetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsProperSupersetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsSubsetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsSupersetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool Overlaps(System.Collections.Generic.IEnumerable other) => throw null;\n public bool Remove(T item) => throw null;\n public bool SetEquals(System.Collections.Generic.IEnumerable other) => throw null;\n public int Count { get => throw null; }\n public virtual bool Contains(T item) => throw null;\n public virtual void Clear() => throw null;\n public virtual void IntersectWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void CopyTo(T[] array, int index) => throw null;\n public void ExceptWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void SymmetricExceptWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void UnionWith(System.Collections.Generic.IEnumerable other) => throw null;\n void System.Collections.Generic.ICollection.Add(T item) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object sender) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n// Generated from `System.Collections.Generic.Stack<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public T Peek() => throw null;\n public int Count { get => throw null; }\n void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) => throw null;\n}\n\n// Generated from `System.Collections.Generic.StackDebugView<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass StackDebugView\n{\n}\n\n}\nnamespace Specialized\n{\n// Generated from `System.Collections.Specialized.NameObjectCollectionBase` in `System.Collections.Specialized, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class NameObjectCollectionBase : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection\n{\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual int Count { get => throw null; }\n public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n public virtual void OnDeserialization(object sender) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Specialized.NameValueCollection` in `System.Collections.Specialized, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NameValueCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n public string this[string name] { get => throw null; set => throw null; }\n}\n\n}\n}\nnamespace ComponentModel\n{\n// Generated from `System.ComponentModel.ComponentConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ComponentConverter : System.ComponentModel.ReferenceConverter\n{\n}\n\n// Generated from `System.ComponentModel.DefaultEventAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DefaultEventAttribute : System.Attribute\n{\n public DefaultEventAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.DefaultPropertyAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DefaultPropertyAttribute : System.Attribute\n{\n public DefaultPropertyAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.INotifyPropertyChanged` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface INotifyPropertyChanged\n{\n}\n\n// Generated from `System.ComponentModel.ReferenceConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ReferenceConverter : System.ComponentModel.TypeConverter\n{\n}\n\n// Generated from `System.ComponentModel.TypeConverterAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeConverterAttribute : System.Attribute\n{\n public TypeConverterAttribute() => throw null;\n public TypeConverterAttribute(System.Type type) => throw null;\n public TypeConverterAttribute(string typeName) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.TypeConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeConverter\n{\n}\n\n// Generated from `System.ComponentModel.TypeDescriptionProviderAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeDescriptionProviderAttribute : System.Attribute\n{\n public TypeDescriptionProviderAttribute(System.Type type) => throw null;\n public TypeDescriptionProviderAttribute(string typeName) => throw null;\n}\n\n// Generated from `System.ComponentModel.TypeDescriptionProvider` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class TypeDescriptionProvider\n{\n}\n\n// Generated from `System.ComponentModel.TypeDescriptor` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeDescriptor\n{\n}\n\nnamespace Design\n{\n// Generated from `System.ComponentModel.Design.DesignerOptionService` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class DesignerOptionService : System.ComponentModel.Design.IDesignerOptionService\n{\n}\n\n// Generated from `System.ComponentModel.Design.IDesignerOptionService` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IDesignerOptionService\n{\n}\n\n}\n}\nnamespace Dynamic\n{\n// Generated from `System.Dynamic.DynamicMetaObject` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DynamicMetaObject\n{\n}\n\n// Generated from `System.Dynamic.ExpandoObject` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ExpandoObject : System.Dynamic.IDynamicMetaObjectProvider, System.ComponentModel.INotifyPropertyChanged, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>, System.Collections.Generic.IDictionary, System.Collections.Generic.ICollection>\n{\n System.Collections.Generic.ICollection System.Collections.Generic.IDictionary.Values { get => throw null; }\n System.Collections.Generic.ICollection System.Collections.Generic.IDictionary.Keys { get => throw null; }\n System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection>.Contains(System.Collections.Generic.KeyValuePair item) => throw null;\n bool System.Collections.Generic.ICollection>.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection>.Remove(System.Collections.Generic.KeyValuePair item) => throw null;\n bool System.Collections.Generic.IDictionary.ContainsKey(string key) => throw null;\n bool System.Collections.Generic.IDictionary.Remove(string key) => throw null;\n bool System.Collections.Generic.IDictionary.TryGetValue(string key, out object value) => throw null;\n int System.Collections.Generic.ICollection>.Count { get => throw null; }\n object this[string key] { get => throw null; set => throw null; }\n void System.Collections.Generic.ICollection>.Add(System.Collections.Generic.KeyValuePair item) => throw null;\n void System.Collections.Generic.ICollection>.Clear() => throw null;\n void System.Collections.Generic.ICollection>.CopyTo(System.Collections.Generic.KeyValuePair[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IDictionary.Add(string key, object value) => throw null;\n}\n\n// Generated from `System.Dynamic.IDynamicMetaObjectProvider` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IDynamicMetaObjectProvider\n{\n}\n\nnamespace Utils\n{\n// Generated from `System.Dynamic.Utils.ListProvider<>` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class ListProvider : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection where T: class\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public T this[int index] { get => throw null; set => throw null; }\n public bool Contains(T item) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool Remove(T item) => throw null;\n public int Count { get => throw null; }\n public int IndexOf(T item) => throw null;\n public void Add(T item) => throw null;\n public void Clear() => throw null;\n public void CopyTo(T[] array, int index) => throw null;\n public void Insert(int index, T item) => throw null;\n public void RemoveAt(int index) => throw null;\n}\n\n}\n}\nnamespace IO\n{\nnamespace Compression\n{\n// Generated from `System.IO.Compression.DeflateStream` in `System.IO.Compression, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089`\npublic class DeflateStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] array, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task CopyToAsync(System.IO.Stream destination, int bufferSize, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] array, int offset, int count) => throw null;\n public override int Read(System.Span buffer) => throw null;\n public override int ReadByte() => throw null;\n public override void CopyTo(System.IO.Stream destination, int bufferSize) => throw null;\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] array, int offset, int count) => throw null;\n public override void Write(System.ReadOnlySpan buffer) => throw null;\n}\n\n}\n}\nnamespace Linq\n{\n// Generated from `System.Linq.Enumerable` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Enumerable\n{\n public static System.Collections.Generic.IEnumerable Select(this System.Collections.Generic.IEnumerable source, System.Func selector) => throw null;\n}\n\n// Generated from `System.Linq.Grouping<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Grouping : System.Linq.IGrouping, System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n TElement this[int index] { get => throw null; set => throw null; }\n bool System.Collections.Generic.ICollection.Contains(TElement item) => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection.Remove(TElement item) => throw null;\n int System.Collections.Generic.ICollection.Count { get => throw null; }\n int System.Collections.Generic.IList.IndexOf(TElement item) => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n void System.Collections.Generic.ICollection.Add(TElement item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.ICollection.CopyTo(TElement[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IList.Insert(int index, TElement item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.IGrouping<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IGrouping : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IIListProvider<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\ninterface IIListProvider : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.ILookup<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface ILookup : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n}\n\n// Generated from `System.Linq.IOrderedEnumerable<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IOrderedEnumerable : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IPartition<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\ninterface IPartition : System.Linq.IIListProvider, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IQueryable` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IQueryable : System.Collections.IEnumerable\n{\n}\n\n// Generated from `System.Linq.Lookup<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Lookup : System.Linq.ILookup, System.Linq.IIListProvider>, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator> GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.OrderedEnumerable<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class OrderedEnumerable : System.Linq.IPartition, System.Linq.IOrderedEnumerable, System.Linq.IIListProvider, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.ParallelEnumerable` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class ParallelEnumerable\n{\n public static System.Linq.ParallelQuery AsParallel(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Linq.ParallelQuery, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n public virtual System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Collections.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Queryable` in `System.Linq.Queryable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Queryable\n{\n public static System.Linq.IQueryable AsQueryable(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.SystemLinq_GroupingDebugView<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass SystemLinq_GroupingDebugView\n{\n}\n\n// Generated from `System.Linq.SystemLinq_LookupDebugView<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass SystemLinq_LookupDebugView\n{\n}\n\nnamespace Expressions\n{\n// Generated from `System.Linq.Expressions.BlockExpressionList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass BlockExpressionList : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public System.Linq.Expressions.Expression this[int index] { get => throw null; set => throw null; }\n public bool Contains(System.Linq.Expressions.Expression item) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool Remove(System.Linq.Expressions.Expression item) => throw null;\n public int Count { get => throw null; }\n public int IndexOf(System.Linq.Expressions.Expression item) => throw null;\n public void Add(System.Linq.Expressions.Expression item) => throw null;\n public void Clear() => throw null;\n public void CopyTo(System.Linq.Expressions.Expression[] array, int index) => throw null;\n public void Insert(int index, System.Linq.Expressions.Expression item) => throw null;\n public void RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.Expressions.Expression` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class Expression\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Linq.Expressions.ParameterExpression` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParameterExpression : System.Linq.Expressions.Expression\n{\n}\n\nnamespace Compiler\n{\n// Generated from `System.Linq.Expressions.Compiler.ParameterList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ParameterList : System.Collections.IEnumerable, System.Collections.Generic.IReadOnlyList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public System.Linq.Expressions.ParameterExpression this[int index] { get => throw null; }\n public int Count { get => throw null; }\n}\n\n}\nnamespace Interpreter\n{\n// Generated from `System.Linq.Expressions.Interpreter.InstructionArray` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstruct InstructionArray\n{\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InstructionList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass InstructionList\n{\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InterpretedFrameInfo` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstruct InterpretedFrameInfo\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InterpretedFrame` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass InterpretedFrame\n{\n}\n\n}\n}\nnamespace Parallel\n{\n// Generated from `System.Linq.Parallel.ListChunk<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ListChunk : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.Lookup<,>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass Lookup : System.Linq.ILookup, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator> GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.PartitionerQueryOperator<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass PartitionerQueryOperator : System.Linq.Parallel.QueryOperator\n{\n}\n\n// Generated from `System.Linq.Parallel.QueryOperator<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class QueryOperator : System.Linq.ParallelQuery\n{\n public override System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.QueryResults<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class QueryResults : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection.Contains(T item) => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection.Remove(T item) => throw null;\n int System.Collections.Generic.IList.IndexOf(T item) => throw null;\n public T this[int index] { get => throw null; set => throw null; }\n public int Count { get => throw null; }\n void System.Collections.Generic.ICollection.Add(T item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.ICollection.CopyTo(T[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IList.Insert(int index, T item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.Parallel.ZipQueryOperator<,,>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ZipQueryOperator : System.Linq.Parallel.QueryOperator\n{\n}\n\n}\n}\nnamespace Net\n{\n// Generated from `System.Net.CookieCollection` in `System.Net.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class CookieCollection : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.IEnumerator GetEnumerator() => throw null;\n public bool Contains(System.Net.Cookie cookie) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool IsSynchronized { get => throw null; }\n public bool Remove(System.Net.Cookie cookie) => throw null;\n public int Count { get => throw null; }\n public object SyncRoot { get => throw null; }\n public void Add(System.Net.Cookie cookie) => throw null;\n public void Clear() => throw null;\n public void CopyTo(System.Array array, int index) => throw null;\n public void CopyTo(System.Net.Cookie[] array, int index) => throw null;\n}\n\n// Generated from `System.Net.Cookie` in `System.Net.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Cookie\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Net.StreamFramer` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass StreamFramer\n{\n}\n\nnamespace Security\n{\n// Generated from `System.Net.Security.AuthenticatedStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class AuthenticatedStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n}\n\n// Generated from `System.Net.Security.CipherSuitesPolicy` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class CipherSuitesPolicy\n{\n}\n\n// Generated from `System.Net.Security.NegotiateStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NegotiateStream : System.Net.Security.AuthenticatedStream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanTimeout { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int ReadTimeout { get => throw null; set => throw null; }\n public override int WriteTimeout { get => throw null; set => throw null; }\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n}\n\n// Generated from `System.Net.Security.SslStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SslStream : System.Net.Security.AuthenticatedStream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanTimeout { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int ReadByte() => throw null;\n public override int ReadTimeout { get => throw null; set => throw null; }\n public override int WriteTimeout { get => throw null; set => throw null; }\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n}\n\n// Generated from `System.Net.Security.TlsCipherSuite` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic enum TlsCipherSuite\n{\n}\n\n// Generated from `System.Net.Security.TlsFrameHelper` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass TlsFrameHelper\n{\n}\n\n}\n}\nnamespace Runtime\n{\nnamespace Serialization\n{\n// Generated from `System.Runtime.Serialization.DataContractAttribute` in `System.Runtime.Serialization.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataContractAttribute : System.Attribute\n{\n public DataContractAttribute() => throw null;\n}\n\n// Generated from `System.Runtime.Serialization.DataMemberAttribute` in `System.Runtime.Serialization.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataMemberAttribute : System.Attribute\n{\n public DataMemberAttribute() => throw null;\n}\n\n}\n}\nnamespace Text\n{\nnamespace RegularExpressions\n{\n// Generated from `System.Text.RegularExpressions.Capture` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Capture\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.CollectionDebuggerProxy<>` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass CollectionDebuggerProxy\n{\n}\n\n// Generated from `System.Text.RegularExpressions.GroupCollection` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class GroupCollection : System.Collections.IList, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyList, System.Collections.Generic.IReadOnlyDictionary, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyCollection>, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable>, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() => throw null;\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Text.RegularExpressions.Group this[int index] { get => throw null; set => throw null; }\n bool System.Collections.Generic.ICollection.Contains(System.Text.RegularExpressions.Group item) => throw null;\n bool System.Collections.Generic.ICollection.Remove(System.Text.RegularExpressions.Group item) => throw null;\n bool System.Collections.IList.Contains(object value) => throw null;\n bool System.Collections.IList.IsFixedSize { get => throw null; }\n int System.Collections.Generic.IList.IndexOf(System.Text.RegularExpressions.Group item) => throw null;\n int System.Collections.IList.Add(object value) => throw null;\n int System.Collections.IList.IndexOf(object value) => throw null;\n object this[int index] { get => throw null; set => throw null; }\n public System.Collections.Generic.IEnumerable Values { get => throw null; }\n public System.Collections.Generic.IEnumerable Keys { get => throw null; }\n public System.Collections.IEnumerator GetEnumerator() => throw null;\n public System.Text.RegularExpressions.Group this[string groupname] { get => throw null; }\n public bool ContainsKey(string key) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool IsSynchronized { get => throw null; }\n public bool TryGetValue(string key, out System.Text.RegularExpressions.Group value) => throw null;\n public int Count { get => throw null; }\n public object SyncRoot { get => throw null; }\n public void CopyTo(System.Array array, int arrayIndex) => throw null;\n public void CopyTo(System.Text.RegularExpressions.Group[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.ICollection.Add(System.Text.RegularExpressions.Group item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.IList.Insert(int index, System.Text.RegularExpressions.Group item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n void System.Collections.IList.Clear() => throw null;\n void System.Collections.IList.Insert(int index, object value) => throw null;\n void System.Collections.IList.Remove(object value) => throw null;\n void System.Collections.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.Group` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Group : System.Text.RegularExpressions.Capture\n{\n}\n\n// Generated from `System.Text.RegularExpressions.Match` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Match : System.Text.RegularExpressions.Group\n{\n}\n\n// Generated from `System.Text.RegularExpressions.RegexOptions` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\n[System.Flags]\npublic enum RegexOptions\n{\n IgnoreCase,\n}\n\n// Generated from `System.Text.RegularExpressions.Regex` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Regex : System.Runtime.Serialization.ISerializable\n{\n public Regex(string pattern) => throw null;\n public Regex(string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public System.Text.RegularExpressions.Match Match(string input) => throw null;\n public override string ToString() => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public string Replace(string input, string replacement) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n}\n}\nnamespace Timers\n{\n// Generated from `System.Timers.TimersDescriptionAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TimersDescriptionAttribute\n{\n public TimersDescriptionAttribute(string description) => throw null;\n}\n\n}\nnamespace Windows\n{\nnamespace Markup\n{\n// Generated from `System.Windows.Markup.ValueSerializerAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ValueSerializerAttribute : System.Attribute\n{\n public ValueSerializerAttribute(System.Type valueSerializerType) => throw null;\n public ValueSerializerAttribute(string valueSerializerTypeName) => throw null;\n}\n\n}\n}\n}\n | diff --git a/csharp/ql/test/resources/stubs/Dapper.cs b/csharp/ql/test/resources/stubs/Dapper.cs new file mode 100644 index 00000000000..d96f1cd4ee6 --- /dev/null +++ b/csharp/ql/test/resources/stubs/Dapper.cs @@ -0,0 +1,34 @@ +// This file contains auto-generated code. +// original-extractor-options: /r:Dapper.dll /r:System.Data.SqlClient.dll ... + +namespace Dapper +{ + // Generated from `Dapper.CommandDefinition` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null` + public struct CommandDefinition + { + public CommandDefinition(string commandText, object parameters = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null, Dapper.CommandFlags flags = CommandFlags.Buffered, System.Threading.CancellationToken cancellationToken = default) => throw null; + } + + // Generated from `Dapper.CommandFlags` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null` + [System.Flags] + public enum CommandFlags + { + None = 0x0, + Buffered = 0x1, + Pipelined = 0x2, + NoCache = 0x4 + } + + // Generated from `Dapper.SqlMapper` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null` + static public class SqlMapper + { + public static System.Collections.Generic.IEnumerable Query(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static System.Data.IDataReader ExecuteReader(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static System.Threading.Tasks.Task> QueryAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static System.Threading.Tasks.Task QueryFirstAsync(this System.Data.IDbConnection cnn, Dapper.CommandDefinition command) => throw null; + public static System.Threading.Tasks.Task QueryFirstAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static System.Threading.Tasks.Task ExecuteAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static object ExecuteScalar(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + } +} + diff --git a/docs/codeql/codeql-cli/codeql-cli-reference.rst b/docs/codeql/codeql-cli/codeql-cli-reference.rst index 75513a06b54..0c53121d379 100644 --- a/docs/codeql/codeql-cli/codeql-cli-reference.rst +++ b/docs/codeql/codeql-cli/codeql-cli-reference.rst @@ -21,11 +21,3 @@ Learn more about the files you can use when running CodeQL processes and the res - :doc:`SARIF output `: CodeQL supports SARIF as an output format for sharing static analysis results. - :doc:`Exit codes `: The CodeQL CLI reports the status of each command it runs as an exit code. This exit code provides information for subsequent commands or for other tools that rely on the CodeQL CLI. - -.. _cli-commands: - -CodeQL CLI manual ------------------ - -To view detailed information about each CodeQL CLI command, -including its usage and options, add the ``--help`` flag or visit the "`CodeQL CLI manual <../manual>`__." diff --git a/docs/codeql/codeql-cli/index.rst b/docs/codeql/codeql-cli/index.rst index 07ec8431235..d7a8a407364 100644 --- a/docs/codeql/codeql-cli/index.rst +++ b/docs/codeql/codeql-cli/index.rst @@ -18,4 +18,4 @@ CodeQL CLI using-the-codeql-cli codeql-cli-reference - + CodeQL CLI manual diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst index a338791953b..ff49b01a441 100644 --- a/docs/codeql/support/reusables/versions-compilers.rst +++ b/docs/codeql/support/reusables/versions-compilers.rst @@ -11,9 +11,11 @@ Microsoft extensions (up to VS 2019), Arm Compiler 5 [2]_","``.cpp``, ``.c++``, ``.cxx``, ``.hpp``, ``.hh``, ``.h++``, ``.hxx``, ``.c``, ``.cc``, ``.h``" - C#,C# up to 8.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8, + C#,C# up to 9.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8, - .NET Core up to 3.1","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``" + .NET Core up to 3.1 + + .NET 5","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``" Go (aka Golang), "Go up to 1.16", "Go 1.11 or more recent", ``.go`` Java,"Java 7 to 15 [3]_","javac (OpenJDK and Oracle JDK), @@ -28,5 +30,5 @@ .. [2] Support for the Arm Compiler (armcc) is preliminary. .. [3] Builds that execute on Java 7 to 15 can be analyzed. The analysis understands Java 15 standard language features. .. [4] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin. - .. [5] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files. - .. [6] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM. + .. [5] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files. + .. [6] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM. diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md index 3c46436a87a..ea3379f3967 100644 --- a/docs/ql-libraries/dataflow/dataflow.md +++ b/docs/ql-libraries/dataflow/dataflow.md @@ -98,10 +98,6 @@ Recommendations: also work, but the upside of `use-use` steps is that sources defined in terms of variable reads just work out of the box. It also makes certain barrier-implementations simpler. -* A predicate `DataFlowCallable Node::getEnclosingCallable()` is required, and in - order to ensure appropriate join-orders, it is important that the QL compiler knows - that this predicate is functional. It can therefore be necessary to enclose the body - of this predicate in a `unique` aggregate. The shared library does not use `localFlowStep` nor `localFlow` but users of `DataFlow.qll` may expect the existence of `DataFlow::localFlowStep` and diff --git a/java/change-notes/2021-04-14-membertype.md b/java/change-notes/2021-04-14-membertype.md new file mode 100644 index 00000000000..910a5c91423 --- /dev/null +++ b/java/change-notes/2021-04-14-membertype.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* A CodeQL class `MemberType` is introduced to describe nested classes. Its `getQualifiedName` method returns `$`-delimited nested type names (for example, `mypackage.Outer$Middle$Inner`), where previously the same type would be named differently depending on whether it was addressed as a `NestedType` or a `Member`. diff --git a/java/ql/src/Diagnostics/DiagnosticsReporting.qll b/java/ql/src/Diagnostics/DiagnosticsReporting.qll new file mode 100644 index 00000000000..76ce4cdfd50 --- /dev/null +++ b/java/ql/src/Diagnostics/DiagnosticsReporting.qll @@ -0,0 +1,70 @@ +/** + * Provides classes and predicates for reporting extractor diagnostics to end users. + */ + +import java + +/** Gets the SARIF severity level that indicates an error. */ +private int getErrorSeverity() { result = 2 } + +/** Gets the SARIF severity level that indicates a warning. */ +private int getWarnSeverity() { result = 1 } + +private predicate knownWarnings(@diagnostic d, string msg, int sev) { + exists(string filename | + diagnostics(d, 2, _, "Skipping Lombok-ed source file: " + filename, _, _) and + msg = "Use of Lombok detected. Skipping file: " + filename and + sev = getWarnSeverity() + ) +} + +private predicate knownErrors(@diagnostic d, string msg, int sev) { + exists(string numErr, Location l | + diagnostics(d, 6, _, numErr, _, l) and + msg = "Frontend errors in file: " + l.getFile().getAbsolutePath() + " (" + numErr + ")" and + sev = getErrorSeverity() + ) + or + exists(string filename, Location l | + diagnostics(d, 7, _, "Exception compiling file " + filename, _, l) and + msg = "Extraction incomplete in file: " + filename and + sev = getErrorSeverity() + ) + or + exists(string errMsg, Location l | + diagnostics(d, 8, _, errMsg, _, l) and + msg = "Severe error: " + errMsg and + sev = getErrorSeverity() + ) +} + +private predicate unknownErrors(@diagnostic d, string msg, int sev) { + not knownErrors(d, _, _) and + exists(Location l, File f, int diagSev | + diagnostics(d, diagSev, _, _, _, l) and l.getFile() = f and diagSev > 3 + | + exists(f.getRelativePath()) and + msg = "Unknown errors in file: " + f.getAbsolutePath() + " (" + diagSev + ")" and + sev = getErrorSeverity() + ) +} + +/** + * Holds if an extraction error or warning occurred that should be reported to end users, + * with the error message `msg` and SARIF severity `sev`. + */ +predicate reportableDiagnostics(@diagnostic d, string msg, int sev) { + knownWarnings(d, msg, sev) or knownErrors(d, msg, sev) or unknownErrors(d, msg, sev) +} + +/** + * Holds if compilation unit `f` is a source file that has + * no relevant extraction diagnostics associated with it. + */ +predicate successfullyExtracted(CompilationUnit f) { + not exists(@diagnostic d, Location l | + reportableDiagnostics(d, _, _) and diagnostics(d, _, _, _, _, l) and l.getFile() = f + ) and + exists(f.getRelativePath()) and + f.fromSource() +} diff --git a/java/ql/src/Diagnostics/ExtractionErrors.ql b/java/ql/src/Diagnostics/ExtractionErrors.ql new file mode 100644 index 00000000000..8b045df256e --- /dev/null +++ b/java/ql/src/Diagnostics/ExtractionErrors.ql @@ -0,0 +1,13 @@ +/** + * @name Extraction errors + * @description A list of extraction errors for files in the source code directory. + * @kind diagnostic + * @id java/diagnostics/extraction-errors + */ + +import java +import DiagnosticsReporting + +from string msg, int sev +where reportableDiagnostics(_, msg, sev) +select msg, sev diff --git a/java/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql b/java/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql new file mode 100644 index 00000000000..161c9f17e42 --- /dev/null +++ b/java/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql @@ -0,0 +1,14 @@ +/** + * @name Successfully extracted files + * @description A list of all files in the source code directory that + * were extracted without encountering an error in the file. + * @kind diagnostic + * @id java/diagnostics/successfully-extracted-files + */ + +import java +import DiagnosticsReporting + +from CompilationUnit f +where successfullyExtracted(f) +select f, "" diff --git a/java/ql/src/Performance/InnerClassCouldBeStatic.ql b/java/ql/src/Performance/InnerClassCouldBeStatic.ql index a18d801549d..5dba77761c6 100644 --- a/java/ql/src/Performance/InnerClassCouldBeStatic.ql +++ b/java/ql/src/Performance/InnerClassCouldBeStatic.ql @@ -100,8 +100,7 @@ predicate potentiallyStatic(InnerClass c) { m = a.getEnclosingCallable() and m.getDeclaringType() = c ) and - not c instanceof AnonymousClass and - not c instanceof LocalClass and + c instanceof MemberType and forall( InnerClass other // If nested and non-static, ... | diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql index c9c1e2917c0..3426d9f6f62 100644 --- a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql +++ b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql @@ -79,8 +79,7 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) { printStackCall.getAnArgument() = printWriter and printStackCall.getQualifier() = exception and stackTraceString.getQualifier() = stringWriterVar.getAnAccess() and - stackTraceString.getMethod().getName() = "toString" and - stackTraceString.getMethod().getNumberOfParameters() = 0 + stackTraceString.getMethod() instanceof ToStringMethod ) } diff --git a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql index c84abe1ff75..7b026efb7ae 100644 --- a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql +++ b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql @@ -33,9 +33,8 @@ class InsecureAlgoLiteral extends ShortStringLiteral { } predicate objectToString(MethodAccess ma) { - exists(Method m | + exists(ToStringMethod m | m = ma.getMethod() and - m.hasName("toString") and m.getDeclaringType() instanceof TypeObject and variableTrack(ma.getQualifier()).getType().getErasure() instanceof TypeObject ) diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql index 4fb3f14c993..c76527bc538 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql @@ -19,14 +19,14 @@ class HardcodedCredentialApiCallConfiguration extends DataFlow::Configuration { override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof HardcodedExpr and - not n.asExpr().getEnclosingCallable().getName() = "toString" + not n.asExpr().getEnclosingCallable() instanceof ToStringMethod } override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsApiSink } override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { node1.asExpr().getType() instanceof TypeString and - exists(MethodAccess ma | ma.getMethod().getName().regexpMatch("getBytes|toCharArray") | + exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) | node2.asExpr() = ma and ma.getQualifier() = node1.asExpr() ) diff --git a/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql b/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql index 40a4e36d70c..c1395669405 100644 --- a/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql +++ b/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql @@ -10,9 +10,8 @@ import java -from MethodAccess ma, Method tostring +from MethodAccess ma, ToStringMethod tostring where - tostring.hasName("toString") and tostring.getDeclaringType() instanceof TypeString and ma.getMethod() = tostring select ma, "Redundant call to 'toString' on a String object." diff --git a/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql b/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql index 7d9ef5b85f9..c6eaf5af2cb 100644 --- a/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql +++ b/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql @@ -14,20 +14,13 @@ import java import semmle.code.java.StringFormat predicate explicitToStringCall(Expr e) { - exists(MethodAccess ma, Method toString | toString = ma.getMethod() | - e = ma.getQualifier() and - toString.getName() = "toString" and - toString.getNumberOfParameters() = 0 and - not toString.isStatic() + exists(MethodAccess ma | + ma.getMethod() instanceof ToStringMethod and + e = ma.getQualifier() ) } -predicate directlyDeclaresToString(Class c) { - exists(Method m | m.getDeclaringType() = c | - m.getName() = "toString" and - m.getNumberOfParameters() = 0 - ) -} +predicate directlyDeclaresToString(Class c) { any(ToStringMethod m).getDeclaringType() = c } predicate inheritsObjectToString(Class t) { not directlyDeclaresToString(t.getSourceDeclaration()) and diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp new file mode 100644 index 00000000000..e201156728a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp @@ -0,0 +1,47 @@ + + + +

    Spring Boot is a popular framework that facilitates the development of stand-alone applications +and micro services. Spring Boot Actuator helps to expose production-ready support features against +Spring Boot applications.

    + +

    Endpoints of Spring Boot Actuator allow to monitor and interact with a Spring Boot application. +Exposing unprotected actuator endpoints through configuration files can lead to information disclosure +or even remote code execution vulnerability.

    + +

    Rather than programmatically permitting endpoint requests or enforcing access control, frequently +developers simply leave management endpoints publicly accessible in the application configuration file +application.properties without enforcing access control through Spring Security.

    +
    + + +

    Declare the Spring Boot Starter Security module in XML configuration or programmatically enforce +security checks on management endpoints using Spring Security. Otherwise accessing management endpoints +on a different HTTP port other than the port that the web application is listening on also helps to +improve the security.

    +
    + + +

    The following examples show both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, +no security module is declared and sensitive management endpoints are exposed. In the 'GOOD' configuration, +security is enforced and only endpoints requiring exposure are exposed.

    + + + +
    + + +
  • + Spring Boot documentation: + Spring Boot Actuator: Production-ready Features +
  • +
  • + VERACODE Blog: + Exploiting Spring Boot Actuators +
  • +
  • + HackerOne Report: + Spring Actuator endpoints publicly available, leading to account takeover +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql new file mode 100644 index 00000000000..e6965959d13 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql @@ -0,0 +1,116 @@ +/** + * @name Insecure Spring Boot Actuator Configuration + * @description Exposed Spring Boot Actuator through configuration files without declarative or procedural + * security enforcement leads to information leak or even remote code execution. + * @kind problem + * @id java/insecure-spring-actuator-config + * @tags security + * external/cwe-016 + */ + +/* + * Note this query requires properties files to be indexed before it can produce results. + * If creating your own database with the CodeQL CLI, you should run + * `codeql database index-files --language=properties ...` + * If using lgtm.com, you should add `properties_files: true` to the index block of your + * lgtm.yml file (see https://lgtm.com/help/lgtm/java-extraction) + */ + +import java +import semmle.code.configfiles.ConfigFiles +import semmle.code.xml.MavenPom + +/** The parent node of the `org.springframework.boot` group. */ +class SpringBootParent extends Parent { + SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" } +} + +/** Class of Spring Boot dependencies. */ +class SpringBootPom extends Pom { + SpringBootPom() { this.getParentElement() instanceof SpringBootParent } + + /** Holds if the Spring Boot Actuator module `spring-boot-starter-actuator` is used in the project. */ + predicate isSpringBootActuatorUsed() { + this.getADependency().getArtifact().getValue() = "spring-boot-starter-actuator" + } + + /** + * Holds if the Spring Boot Security module is used in the project, which brings in other security + * related libraries. + */ + predicate isSpringBootSecurityUsed() { + this.getADependency().getArtifact().getValue() = "spring-boot-starter-security" + } +} + +/** The properties file `application.properties`. */ +class ApplicationProperties extends ConfigPair { + ApplicationProperties() { this.getFile().getBaseName() = "application.properties" } +} + +/** The configuration property `management.security.enabled`. */ +class ManagementSecurityConfig extends ApplicationProperties { + ManagementSecurityConfig() { this.getNameElement().getName() = "management.security.enabled" } + + /** Gets the whitespace-trimmed value of this property. */ + string getValue() { result = this.getValueElement().getValue().trim() } + + /** Holds if `management.security.enabled` is set to `false`. */ + predicate hasSecurityDisabled() { getValue() = "false" } + + /** Holds if `management.security.enabled` is set to `true`. */ + predicate hasSecurityEnabled() { getValue() = "true" } +} + +/** The configuration property `management.endpoints.web.exposure.include`. */ +class ManagementEndPointInclude extends ApplicationProperties { + ManagementEndPointInclude() { + this.getNameElement().getName() = "management.endpoints.web.exposure.include" + } + + /** Gets the whitespace-trimmed value of this property. */ + string getValue() { result = this.getValueElement().getValue().trim() } +} + +/** + * Holds if `ApplicationProperties` ap of a repository managed by `SpringBootPom` pom + * has a vulnerable configuration of Spring Boot Actuator management endpoints. + */ +predicate hasConfidentialEndPointExposed(SpringBootPom pom, ApplicationProperties ap) { + pom.isSpringBootActuatorUsed() and + not pom.isSpringBootSecurityUsed() and + ap.getFile() + .getParentContainer() + .getAbsolutePath() + .matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory + exists(string springBootVersion | springBootVersion = pom.getParentElement().getVersionString() | + springBootVersion.regexpMatch("1\\.[0-4].*") and // version 1.0, 1.1, ..., 1.4 + not exists(ManagementSecurityConfig me | + me.hasSecurityEnabled() and me.getFile() = ap.getFile() + ) + or + springBootVersion.matches("1.5%") and // version 1.5 + exists(ManagementSecurityConfig me | me.hasSecurityDisabled() and me.getFile() = ap.getFile()) + or + springBootVersion.matches("2.%") and //version 2.x + exists(ManagementEndPointInclude mi | + mi.getFile() = ap.getFile() and + ( + mi.getValue() = "*" // all endpoints are enabled + or + mi.getValue() + .matches([ + "%dump%", "%trace%", "%logfile%", "%shutdown%", "%startup%", "%mappings%", "%env%", + "%beans%", "%sessions%" + ]) // confidential endpoints to check although all endpoints apart from '/health' and '/info' are considered sensitive by Spring + ) + ) + ) +} + +from SpringBootPom pom, ApplicationProperties ap, Dependency d +where + hasConfidentialEndPointExposed(pom, ap) and + d = pom.getADependency() and + d.getArtifact().getValue() = "spring-boot-starter-actuator" +select d, "Insecure configuration of Spring Boot Actuator exposes sensitive endpoints." diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/application.properties b/java/ql/src/experimental/Security/CWE/CWE-016/application.properties new file mode 100644 index 00000000000..4f5defdd948 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/application.properties @@ -0,0 +1,22 @@ +#management.endpoints.web.base-path=/admin + + +#### BAD: All management endpoints are accessible #### +# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default + +# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators +management.security.enabled=false + +# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to expose everything +management.endpoints.web.exposure.include=* + + +#### GOOD: All management endpoints have access control #### +# safe configuration (spring boot 1.0 - 1.4): exposes actuators by default +management.security.enabled=true + +# safe configuration (spring boot 1.5+): requires value false to expose sensitive actuators +management.security.enabled=true + +# safe configuration (spring boot 2+): exposes health and info only by default, here overridden to expose one additional endpoint which we assume is intentional and safe. +management.endpoints.web.exposure.include=beans,info,health diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml b/java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml new file mode 100644 index 00000000000..9dd5c9c188b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + spring-boot-actuator-app + spring-boot-actuator-app + 1.0-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + + + + org.springframework.boot + spring-boot-starter-parent + 2.3.8.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-devtools + + + + + + + org.springframework.boot + spring-boot-test + + + + \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml b/java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml new file mode 100644 index 00000000000..89f577f21e5 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + spring-boot-actuator-app + spring-boot-actuator-app + 1.0-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + + + + org.springframework.boot + spring-boot-starter-parent + 2.3.8.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-devtools + + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-test + + + + \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/FlowUtils.qll b/java/ql/src/experimental/Security/CWE/CWE-094/FlowUtils.qll new file mode 100644 index 00000000000..5371c51aa8f --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/FlowUtils.qll @@ -0,0 +1,14 @@ +import java +import semmle.code.java.dataflow.FlowSources + +/** + * Holds if `fromNode` to `toNode` is a dataflow step that returns data from + * a bean by calling one of its getters. + */ +predicate hasGetterFlow(DataFlow::Node fromNode, DataFlow::Node toNode) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m instanceof GetterMethod and + ma.getQualifier() = fromNode.asExpr() and + ma = toNode.asExpr() + ) +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.qhelp new file mode 100644 index 00000000000..9bf84f710dc --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.qhelp @@ -0,0 +1,81 @@ + + + + +

    +Apache Groovy is a powerful, optionally typed and dynamic language, +with static-typing and static compilation capabilities. + +It integrates smoothly with any Java program, +and immediately delivers to your application powerful features, +including scripting capabilities, Domain-Specific Language authoring, +runtime and compile-time meta-programming and functional programming. + +If a Groovy script is built using attacker-controlled data, +and then evaluated, then it may allow the attacker to achieve RCE. +

    +
    + + +

    +It is generally recommended to avoid using untrusted input in a Groovy evaluation. +If this is not possible, use a sandbox solution. Developers must also take care that Groovy +compile-time metaprogramming can also lead to RCE: it is possible to achieve RCE by compiling +a Groovy script (see the article "Abusing Meta Programming for Unauthenticated RCE!" linked below). + +Groovy's SecureASTCustomizer allows securing source code by controlling what code constructs are permitted. +This is typically done when using Groovy for its scripting or domain specific language (DSL) features. +The fundamental problem is that Groovy is a dynamic language, yet SecureASTCustomizer works by looking at Groovy AST statically. + +This makes it very easy for an attacker to bypass many of the intended checks +(see https://kohsuke.org/2012/04/27/groovy-secureastcustomizer-is-harmful/). +Therefore, besides SecureASTCustomizer, runtime checks are also necessary before calling Groovy methods +(see https://melix.github.io/blog/2015/03/sandboxing.html). + +It is also possible to use a block-list method, excluding unwanted classes from being loaded by the JVM. +This method is not always recommended, because block-lists can be bypassed by unexpected values. + +

    +
    + + +

    +The following example uses untrusted data to evaluate a Groovy script. +

    + + +

    +The following example uses classloader block-list approach to exclude loading dangerous classes. +

    + + +
    + + +
  • + Orange Tsai: + Abusing Meta Programming for Unauthenticated RCE!. +
  • +
  • + Cédric Champeau: + Improved sandboxing of Groovy scripts. +
  • +
  • + Kohsuke Kawaguchi: + Groovy SecureASTCustomizer is harmful. +
  • +
  • + Welk1n: + Groovy Injection payloads. +
  • +
  • + Charles Chan: + Secure Groovy Script Execution in a Sandbox. +
  • +
  • + Eugene: + Scripting and sandboxing in a JVM environment. +
  • +
    + +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.ql new file mode 100644 index 00000000000..0622c9cd7e3 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.ql @@ -0,0 +1,20 @@ +/** + * @name Groovy Language injection + * @description Evaluation of a user-controlled Groovy script + * may lead to arbitrary code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/groovy-injection + * @tags security + * external/cwe/cwe-094 + */ + +import java +import DataFlow::PathGraph +import GroovyInjectionLib + +from DataFlow::PathNode source, DataFlow::PathNode sink, GroovyInjectionConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Groovy Injection from $@.", source.getNode(), + "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBad.java b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBad.java new file mode 100644 index 00000000000..8afe77d2a39 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBad.java @@ -0,0 +1,27 @@ +public class GroovyInjection { + void injectionViaClassLoader(HttpServletRequest request) { + String script = request.getParameter("script"); + final GroovyClassLoader classLoader = new GroovyClassLoader(); + Class groovy = classLoader.parseClass(script); + GroovyObject groovyObj = (GroovyObject) groovy.newInstance(); + } + + void injectionViaEval(HttpServletRequest request) { + String script = request.getParameter("script"); + Eval.me(script); + } + + void injectionViaGroovyShell(HttpServletRequest request) { + GroovyShell shell = new GroovyShell(); + String script = request.getParameter("script"); + shell.evaluate(script); + } + + void injectionViaGroovyShellGroovyCodeSource(HttpServletRequest request) { + GroovyShell shell = new GroovyShell(); + String script = request.getParameter("script"); + GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test"); + shell.evaluate(gcs); + } +} + diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBlocklist.java b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBlocklist.java new file mode 100644 index 00000000000..61159a3137f --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBlocklist.java @@ -0,0 +1,17 @@ +public class SandboxGroovyClassLoader extends ClassLoader { + public SandboxGroovyClassLoader(ClassLoader parent) { + super(parent); + } + + /* override `loadClass` here to prevent loading sensitive classes, such as `java.lang.Runtime`, `java.lang.ProcessBuilder`, `java.lang.System`, etc. */ + /* Note we must also block `groovy.transform.ASTTest`, `groovy.lang.GrabConfig` and `org.buildobjects.process.ProcBuilder` to prevent compile-time RCE. */ + + static void runWithSandboxGroovyClassLoader() throws Exception { + // GOOD: route all class-loading via sand-boxing classloader. + SandboxGroovyClassLoader classLoader = new GroovyClassLoader(new SandboxGroovyClassLoader()); + + Class scriptClass = classLoader.parseClass(untrusted.getQueryString()); + Object scriptInstance = scriptClass.newInstance(); + Object result = scriptClass.getDeclaredMethod("bar", new Class[]{}).invoke(scriptInstance, new Object[]{}); + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionLib.qll new file mode 100644 index 00000000000..61ddb9b9a1a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionLib.qll @@ -0,0 +1,160 @@ +/** + * Provides classes and predicates for Groovy Code Injection + * taint-tracking configuration. + */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking + +/** A data flow sink for Groovy expression injection vulnerabilities. */ +abstract private class GroovyInjectionSink extends DataFlow::ExprNode { } + +/** + * A taint-tracking configuration for unsafe user input + * that is used to evaluate a Groovy expression. + */ +class GroovyInjectionConfig extends TaintTracking::Configuration { + GroovyInjectionConfig() { this = "GroovyInjectionConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof GroovyInjectionSink } + + override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + groovyCodeSourceTaintStep(fromNode, toNode) + } +} + +/** The class `groovy.lang.GroovyShell`. */ +private class TypeGroovyShell extends RefType { + TypeGroovyShell() { this.hasQualifiedName("groovy.lang", "GroovyShell") } +} + +/** The class `groovy.lang.GroovyCodeSource`. */ +private class TypeGroovyCodeSource extends RefType { + TypeGroovyCodeSource() { this.hasQualifiedName("groovy.lang", "GroovyCodeSource") } +} + +/** + * Methods in the `GroovyShell` class that evaluate a Groovy expression. + */ +private class GroovyShellMethod extends Method { + GroovyShellMethod() { + this.getDeclaringType() instanceof TypeGroovyShell and + this.getName() in ["evaluate", "parse", "run"] + } +} + +private class GroovyShellMethodAccess extends MethodAccess { + GroovyShellMethodAccess() { this.getMethod() instanceof GroovyShellMethod } +} + +/** + * Holds if `fromNode` to `toNode` is a dataflow step from a tainted string to + * a `GroovyCodeSource` instance, i.e. `new GroovyCodeSource(tainted, ...)`. + */ +private predicate groovyCodeSourceTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + exists(ConstructorCall gcscc | + gcscc.getConstructedType() instanceof TypeGroovyCodeSource and + gcscc = toNode.asExpr() and + gcscc.getArgument(0) = fromNode.asExpr() + ) +} + +/** + * A sink for Groovy Injection via the `GroovyShell` class. + * + * ``` + * GroovyShell gs = new GroovyShell(); + * gs.evaluate(sink, ....) + * gs.run(sink, ....) + * gs.parse(sink,...) + * ``` + */ +private class GroovyShellSink extends GroovyInjectionSink { + GroovyShellSink() { + exists(GroovyShellMethodAccess ma, Argument firstArg | + ma.getArgument(0) = firstArg and + firstArg = this.asExpr() and + ( + firstArg.getType() instanceof TypeString or + firstArg.getType() instanceof TypeGroovyCodeSource + ) + ) + } +} + +/** The class `groovy.util.Eval`. */ +private class TypeEval extends RefType { + TypeEval() { this.hasQualifiedName("groovy.util", "Eval") } +} + +/** + * Methods in the `Eval` class that evaluate a Groovy expression. + */ +private class EvalMethod extends Method { + EvalMethod() { + this.getDeclaringType() instanceof TypeEval and + this.getName() in ["me", "x", "xy", "xyz"] + } +} + +private class EvalMethodAccess extends MethodAccess { + EvalMethodAccess() { this.getMethod() instanceof EvalMethod } + + Expr getArgumentExpr() { result = this.getArgument(this.getNumArgument() - 1) } +} + +/** + * A sink for Groovy Injection via the `Eval` class. + * + * ``` + * Eval.me(sink) + * Eval.me("p1", "p2", sink) + * Eval.x("p1", sink) + * Eval.xy("p1", "p2" sink) + * Eval.xyz("p1", "p2", "p3", sink) + * ``` + */ +private class EvalSink extends GroovyInjectionSink { + EvalSink() { exists(EvalMethodAccess ma | ma.getArgumentExpr() = this.asExpr()) } +} + +/** The class `groovy.lang.GroovyClassLoader`. */ +private class TypeGroovyClassLoader extends RefType { + TypeGroovyClassLoader() { this.hasQualifiedName("groovy.lang", "GroovyClassLoader") } +} + +/** + * A method in the `GroovyClassLoader` class that evaluates a Groovy expression. + */ +private class GroovyClassLoaderParseClassMethod extends Method { + GroovyClassLoaderParseClassMethod() { + this.getDeclaringType() instanceof TypeGroovyClassLoader and + this.hasName("parseClass") + } +} + +private class GroovyClassLoaderParseClassMethodAccess extends MethodAccess { + GroovyClassLoaderParseClassMethodAccess() { + this.getMethod() instanceof GroovyClassLoaderParseClassMethod + } +} + +/** + * A sink for Groovy Injection via the `GroovyClassLoader` class. + * + * ``` + * GroovyClassLoader classLoader = new GroovyClassLoader(); + * Class groovy = classLoader.parseClass(script); + * ``` + * + * Groovy supports compile-time metaprogramming, so just calling the `parseClass` + * method is enough to achieve RCE. + */ +private class GroovyClassLoadParseClassSink extends GroovyInjectionSink { + GroovyClassLoadParseClassSink() { + exists(GroovyClassLoaderParseClassMethodAccess ma | ma.getArgument(0) = this.asExpr()) + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qhelp new file mode 100644 index 00000000000..216bdeaebf3 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qhelp @@ -0,0 +1,42 @@ + + + +

    +It is dangerous to load Dex libraries from shared world-writable storage spaces. A malicious actor can replace a dex file with a maliciously crafted file +which when loaded by the app can lead to code execution. +

    +
    + + +

    + Loading a file from private storage instead of a world-writable one can prevent this issue, + because the attacker cannot access files stored there. +

    +
    + + +

    + The following example loads a Dex file from a shared world-writable location. in this case, + since the `/sdcard` directory is on external storage, anyone can read/write to the location. + bypassing all Android security policies. Hence, this is insecure. +

    + + +

    + The next example loads a Dex file stored inside the app's private storage. + This is not exploitable as nobody else except the app can access the data stored there. +

    + +
    + + +
  • + Android Documentation: + Data and file storage overview. +
  • +
  • + Android Documentation: + DexClassLoader. +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.ql b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.ql new file mode 100644 index 00000000000..bae3ed63d70 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.ql @@ -0,0 +1,20 @@ +/** + * @name Insecure loading of an Android Dex File + * @description Loading a DEX library located in a world-writable location such as + * an SD card can lead to arbitrary code execution vulnerabilities. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/android-insecure-dex-loading + * @tags security + * external/cwe/cwe-094 + */ + +import java +import InsecureDexLoading +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureDexConfiguration conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Potential arbitrary code execution due to $@.", + source.getNode(), "a value loaded from a world-writable source." diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qll b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qll new file mode 100644 index 00000000000..0ee0954216e --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qll @@ -0,0 +1,100 @@ +import java +import semmle.code.java.dataflow.FlowSources + +/** + * A taint-tracking configuration detecting unsafe use of a + * `DexClassLoader` by an Android app. + */ +class InsecureDexConfiguration extends TaintTracking::Configuration { + InsecureDexConfiguration() { this = "Insecure Dex File Load" } + + override predicate isSource(DataFlow::Node source) { source instanceof InsecureDexSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof InsecureDexSink } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + flowStep(pred, succ) + } +} + +/** A data flow source for insecure Dex class loading vulnerabilities. */ +abstract class InsecureDexSource extends DataFlow::Node { } + +/** A data flow sink for insecure Dex class loading vulnerabilities. */ +abstract class InsecureDexSink extends DataFlow::Node { } + +private predicate flowStep(DataFlow::Node pred, DataFlow::Node succ) { + // propagate from a `java.io.File` via the `File.getAbsolutePath` call. + exists(MethodAccess m | + m.getMethod().getDeclaringType() instanceof TypeFile and + m.getMethod().hasName("getAbsolutePath") and + m.getQualifier() = pred.asExpr() and + m = succ.asExpr() + ) + or + // propagate from a `java.io.File` via the `File.toString` call. + exists(MethodAccess m | + m.getMethod().getDeclaringType() instanceof TypeFile and + m.getMethod().hasName("toString") and + m.getQualifier() = pred.asExpr() and + m = succ.asExpr() + ) + or + // propagate to newly created `File` if the parent directory of the new `File` is tainted + exists(ConstructorCall cc | + cc.getConstructedType() instanceof TypeFile and + cc.getArgument(0) = pred.asExpr() and + cc = succ.asExpr() + ) +} + +/** + * An argument to a `DexClassLoader` call taken as a sink for + * insecure Dex class loading vulnerabilities. + */ +private class DexClassLoader extends InsecureDexSink { + DexClassLoader() { + exists(ConstructorCall cc | + cc.getConstructedType().hasQualifiedName("dalvik.system", "DexClassLoader") + | + this.asExpr() = cc.getArgument(0) + ) + } +} + +/** + * A `File` instance which reads from an SD card + * taken as a source for insecure Dex class loading vulnerabilities. + */ +private class ExternalFile extends InsecureDexSource { + ExternalFile() { + exists(ConstructorCall cc, Argument a | + cc.getConstructedType() instanceof TypeFile and + a = cc.getArgument(0) and + a.(CompileTimeConstantExpr).getStringValue().matches("%sdcard%") + | + this.asExpr() = a + ) + } +} + +/** + * A directory or file which may be stored in an world writable directory + * taken as a source for insecure Dex class loading vulnerabilities. + */ +private class ExternalStorageDirSource extends InsecureDexSource { + ExternalStorageDirSource() { + exists(Method m | + m.getDeclaringType().hasQualifiedName("android.os", "Environment") and + m.hasName("getExternalStorageDirectory") + or + m.getDeclaringType().hasQualifiedName("android.content", "Context") and + m.hasName([ + "getExternalFilesDir", "getExternalFilesDirs", "getExternalMediaDirs", + "getExternalCacheDir", "getExternalCacheDirs" + ]) + | + this.asExpr() = m.getAReference() + ) + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingBad.java b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingBad.java new file mode 100644 index 00000000000..869b6bc571c --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingBad.java @@ -0,0 +1,32 @@ + +import android.app.Application; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.os.Bundle; + +import dalvik.system.DexClassLoader; +import dalvik.system.DexFile; + +public class InsecureDexLoading extends Application { + @Override + public void onCreate() { + super.onCreate(); + updateChecker(); + } + + private void updateChecker() { + try { + File file = new File("/sdcard/updater.apk"); + if (file.exists() && file.isFile() && file.length() <= 1000) { + DexClassLoader cl = new DexClassLoader(file.getAbsolutePath(), getCacheDir().getAbsolutePath(), null, + getClassLoader()); + int version = (int) cl.loadClass("my.package.class").getDeclaredMethod("myMethod").invoke(null); + if (Build.VERSION.SDK_INT < version) { + Toast.makeText(this, "Loaded Dex!", Toast.LENGTH_LONG).show(); + } + } + } catch (Exception e) { + // ignore + } + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingGood.java b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingGood.java new file mode 100644 index 00000000000..e45e3938f7b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingGood.java @@ -0,0 +1,23 @@ +public class SecureDexLoading extends Application { + @Override + public void onCreate() { + super.onCreate(); + updateChecker(); + } + + private void updateChecker() { + try { + File file = new File(getCacheDir() + "/updater.apk"); + if (file.exists() && file.isFile() && file.length() <= 1000) { + DexClassLoader cl = new DexClassLoader(file.getAbsolutePath(), getCacheDir().getAbsolutePath(), null, + getClassLoader()); + int version = (int) cl.loadClass("my.package.class").getDeclaredMethod("myMethod").invoke(null); + if (Build.VERSION.SDK_INT < version) { + Toast.makeText(this, "Securely loaded Dex!", Toast.LENGTH_LONG).show(); + } + } + } catch (Exception e) { + // ignore + } + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp new file mode 100644 index 00000000000..a8d3cd0fe70 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp @@ -0,0 +1,61 @@ + + + + +

    +Jakarta Expression Language (EL) is an expression language for Java applications. +There is a single language specification and multiple implementations +such as Glassfish, Juel, Apache Commons EL, etc. +The language allows invocation of methods available in the JVM. +If an expression is built using attacker-controlled data, +and then evaluated, it may allow the attacker to run arbitrary code. +

    +
    + + +

    +It is generally recommended to avoid using untrusted data in an EL expression. +Before using untrusted data to build an EL expression, the data should be validated +to ensure it is not evaluated as expression language. If the EL implementation offers +configuring a sandbox for EL expressions, they should be run in a restrictive sandbox +that allows accessing only explicitly allowed classes. If the EL implementation +does not support sandboxing, consider using other expression language implementations +with sandboxing capabilities such as Apache Commons JEXL or the Spring Expression Language. +

    +
    + + +

    +The following example shows how untrusted data is used to build and run an expression +using the JUEL interpreter: +

    + + +

    +JUEL does not support running expressions in a sandbox. To prevent running arbitrary code, +incoming data has to be checked before including it in an expression. The next example +uses a Regex pattern to check whether a user tries to run an allowed expression or not: +

    + + +
    + + +
  • + Eclipse Foundation: + Jakarta Expression Language. +
  • +
  • + Jakarta EE documentation: + Jakarta Expression Language API +
  • +
  • + OWASP: + Expression Language Injection. +
  • +
  • + JUEL: + Home page +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql new file mode 100644 index 00000000000..8190ec3d61f --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql @@ -0,0 +1,20 @@ +/** + * @name Jakarta Expression Language injection + * @description Evaluation of a user-controlled expression + * may lead to arbitrary code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/javaee-expression-injection + * @tags security + * external/cwe/cwe-094 + */ + +import java +import JakartaExpressionInjectionLib +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, JakartaExpressionInjectionConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Jakarta Expression Language injection from $@.", + source.getNode(), "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll new file mode 100644 index 00000000000..43090974364 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll @@ -0,0 +1,108 @@ +import java +import FlowUtils +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking + +/** + * A taint-tracking configuration for unsafe user input + * that is used to construct and evaluate an expression. + */ +class JakartaExpressionInjectionConfig extends TaintTracking::Configuration { + JakartaExpressionInjectionConfig() { this = "JakartaExpressionInjectionConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof ExpressionEvaluationSink } + + override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + any(TaintPropagatingCall c).taintFlow(fromNode, toNode) or + hasGetterFlow(fromNode, toNode) + } +} + +/** + * A sink for Expresssion Language injection vulnerabilities, + * i.e. method calls that run evaluation of an expression. + */ +private class ExpressionEvaluationSink extends DataFlow::ExprNode { + ExpressionEvaluationSink() { + exists(MethodAccess ma, Method m, Expr taintFrom | + ma.getMethod() = m and taintFrom = this.asExpr() + | + m.getDeclaringType() instanceof ValueExpression and + m.hasName(["getValue", "setValue"]) and + ma.getQualifier() = taintFrom + or + m.getDeclaringType() instanceof MethodExpression and + m.hasName("invoke") and + ma.getQualifier() = taintFrom + or + m.getDeclaringType() instanceof LambdaExpression and + m.hasName("invoke") and + ma.getQualifier() = taintFrom + or + m.getDeclaringType() instanceof ELProcessor and + m.hasName(["eval", "getValue", "setValue"]) and + ma.getArgument(0) = taintFrom + or + m.getDeclaringType() instanceof ELProcessor and + m.hasName("setVariable") and + ma.getArgument(1) = taintFrom + ) + } +} + +/** + * Defines method calls that propagate tainted expressions. + */ +private class TaintPropagatingCall extends Call { + Expr taintFromExpr; + + TaintPropagatingCall() { + taintFromExpr = this.getArgument(1) and + ( + exists(Method m | this.(MethodAccess).getMethod() = m | + m.getDeclaringType() instanceof ExpressionFactory and + m.hasName(["createValueExpression", "createMethodExpression"]) and + taintFromExpr.getType() instanceof TypeString + ) + or + exists(Constructor c | this.(ConstructorCall).getConstructor() = c | + c.getDeclaringType() instanceof LambdaExpression and + taintFromExpr.getType() instanceof ValueExpression + ) + ) + } + + /** + * Holds if `fromNode` to `toNode` is a dataflow step that propagates + * tainted data. + */ + predicate taintFlow(DataFlow::Node fromNode, DataFlow::Node toNode) { + fromNode.asExpr() = taintFromExpr and toNode.asExpr() = this + } +} + +private class JakartaType extends RefType { + JakartaType() { getPackage().hasName(["javax.el", "jakarta.el"]) } +} + +private class ELProcessor extends JakartaType { + ELProcessor() { hasName("ELProcessor") } +} + +private class ExpressionFactory extends JakartaType { + ExpressionFactory() { hasName("ExpressionFactory") } +} + +private class ValueExpression extends JakartaType { + ValueExpression() { hasName("ValueExpression") } +} + +private class MethodExpression extends JakartaType { + MethodExpression() { hasName("MethodExpression") } +} + +private class LambdaExpression extends JakartaType { + LambdaExpression() { hasName("LambdaExpression") } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll index 561d7e46ae9..89d7cb496a4 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll @@ -1,4 +1,5 @@ import java +import FlowUtils import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.TaintTracking @@ -16,7 +17,7 @@ class JexlInjectionConfig extends TaintTracking::Configuration { override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { any(TaintPropagatingJexlMethodCall c).taintFlow(fromNode, toNode) or - returnsDataFromBean(fromNode, toNode) + hasGetterFlow(fromNode, toNode) } } @@ -152,18 +153,6 @@ private predicate createsJexlEngine(DataFlow::Node fromNode, DataFlow::Node toNo ) } -/** - * Holds if `fromNode` to `toNode` is a dataflow step that returns data from - * a bean by calling one of its getters. - */ -private predicate returnsDataFromBean(DataFlow::Node fromNode, DataFlow::Node toNode) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - m instanceof GetterMethod and - ma.getQualifier() = fromNode.asExpr() and - ma = toNode.asExpr() - ) -} - /** * A methods in the `JexlEngine` class that gets or sets a property with a JEXL expression. */ diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java b/java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java new file mode 100644 index 00000000000..3dfaaead68a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java @@ -0,0 +1,10 @@ +String input = getRemoteUserInput(); +String pattern = "(inside|outside)\\.(temperature|humidity)"; +if (!input.matches(pattern)) { + throw new IllegalArgumentException("Unexpected expression"); +} +String expression = "${" + input + "}"; +ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl(); +ValueExpression e = factory.createValueExpression(context, expression, Object.class); +SimpleContext context = getContext(); +Object result = e.getValue(context); diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java b/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java new file mode 100644 index 00000000000..27afa0fcb49 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java @@ -0,0 +1,5 @@ +String expression = "${" + getRemoteUserInput() + "}"; +ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl(); +ValueExpression e = factory.createValueExpression(context, expression, Object.class); +SimpleContext context = getContext(); +Object result = e.getValue(context); \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java new file mode 100644 index 00000000000..48d80707ff8 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java @@ -0,0 +1,44 @@ +class SensitiveCookieNotHttpOnly { + // GOOD - Create a sensitive cookie with the `HttpOnly` flag set. + public void addCookie(String jwt_token, HttpServletRequest request, HttpServletResponse response) { + Cookie jwtCookie =new Cookie("jwt_token", jwt_token); + jwtCookie.setPath("/"); + jwtCookie.setMaxAge(3600*24*7); + jwtCookie.setHttpOnly(true); + response.addCookie(jwtCookie); + } + + // BAD - Create a sensitive cookie without the `HttpOnly` flag set. + public void addCookie2(String jwt_token, String userId, HttpServletRequest request, HttpServletResponse response) { + Cookie jwtCookie =new Cookie("jwt_token", jwt_token); + jwtCookie.setPath("/"); + jwtCookie.setMaxAge(3600*24*7); + response.addCookie(jwtCookie); + } + + // GOOD - Set a sensitive cookie header with the `HttpOnly` flag set. + public void addCookie3(String authId, HttpServletRequest request, HttpServletResponse response) { + response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure"); + } + + // BAD - Set a sensitive cookie header without the `HttpOnly` flag set. + public void addCookie4(String authId, HttpServletRequest request, HttpServletResponse response) { + response.addHeader("Set-Cookie", "token=" +authId + ";Secure"); + } + + // GOOD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through string concatenation. + public void addCookie5(String accessKey, HttpServletRequest request, HttpServletResponse response) { + response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true) + ";HttpOnly"); + } + + // BAD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` without the `HttpOnly` flag set. + public void addCookie6(String accessKey, HttpServletRequest request, HttpServletResponse response) { + response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true).toString()); + } + + // GOOD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through the constructor. + public void addCookie7(String accessKey, HttpServletRequest request, HttpServletResponse response) { + NewCookie accessKeyCookie = new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true); + response.setHeader("Set-Cookie", accessKeyCookie.toString()); + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp new file mode 100644 index 00000000000..ee3e8a4181a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp @@ -0,0 +1,27 @@ + + + + +

    Cross-Site Scripting (XSS) is categorized as one of the OWASP Top 10 Security Vulnerabilities. The HttpOnly flag directs compatible browsers to prevent client-side script from accessing cookies. Including the HttpOnly flag in the Set-Cookie HTTP response header for a sensitive cookie helps mitigate the risk associated with XSS where an attacker's script code attempts to read the contents of a cookie and exfiltrate information obtained.

    +
    + + +

    Use the HttpOnly flag when generating a cookie containing sensitive information to help mitigate the risk of client side script accessing the protected cookie.

    +
    + + +

    The following example shows two ways of generating sensitive cookies. In the 'BAD' cases, the HttpOnly flag is not set. In the 'GOOD' cases, the HttpOnly flag is set.

    + +
    + + +
  • + PortSwigger: + Cookie without HttpOnly flag set +
  • +
  • + OWASP: + HttpOnly +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql new file mode 100644 index 00000000000..4c0dc624d07 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql @@ -0,0 +1,221 @@ +/** + * @name Sensitive cookies without the HttpOnly response header set + * @description Sensitive cookies without the 'HttpOnly' flag set leaves session cookies vulnerable to + * an XSS attack. + * @kind path-problem + * @id java/sensitive-cookie-not-httponly + * @tags security + * external/cwe/cwe-1004 + */ + +/* + * Sketch of the structure of this query: we track cookie names that appear to be sensitive + * (e.g. `session` or `token`) to a `ServletResponse.addHeader(...)` or `.addCookie(...)` + * method that does not set the `httpOnly` flag. Subsidiary configurations + * `MatchesHttpOnlyConfiguration` and `SetHttpOnlyInCookieConfiguration` are used to establish + * when the `httpOnly` flag is likely to have been set, before configuration + * `MissingHttpOnlyConfiguration` establishes that a non-`httpOnly` cookie has a sensitive-seeming name. + */ + +import java +import semmle.code.java.dataflow.FlowSteps +import semmle.code.java.frameworks.Servlets +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.dataflow.TaintTracking2 +import DataFlow::PathGraph + +/** Gets a regular expression for matching common names of sensitive cookies. */ +string getSensitiveCookieNameRegex() { result = "(?i).*(auth|session|token|key|credential).*" } + +/** Gets a regular expression for matching CSRF cookies. */ +string getCsrfCookieNameRegex() { result = "(?i).*(csrf).*" } + +/** + * Holds if a string is concatenated with the name of a sensitive cookie. Excludes CSRF cookies since + * they are special cookies implementing the Synchronizer Token Pattern that can be used in JavaScript. + */ +predicate isSensitiveCookieNameExpr(Expr expr) { + exists(string s | s = expr.(CompileTimeConstantExpr).getStringValue() | + s.regexpMatch(getSensitiveCookieNameRegex()) and not s.regexpMatch(getCsrfCookieNameRegex()) + ) + or + isSensitiveCookieNameExpr(expr.(AddExpr).getAnOperand()) +} + +/** A sensitive cookie name. */ +class SensitiveCookieNameExpr extends Expr { + SensitiveCookieNameExpr() { isSensitiveCookieNameExpr(this) } +} + +/** A method call that sets a `Set-Cookie` header. */ +class SetCookieMethodAccess extends MethodAccess { + SetCookieMethodAccess() { + ( + this.getMethod() instanceof ResponseAddHeaderMethod or + this.getMethod() instanceof ResponseSetHeaderMethod + ) and + this.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() = "set-cookie" + } +} + +/** + * A taint configuration tracking flow from the text `httponly` to argument 1 of + * `SetCookieMethodAccess`. + */ +class MatchesHttpOnlyConfiguration extends TaintTracking2::Configuration { + MatchesHttpOnlyConfiguration() { this = "MatchesHttpOnlyConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr().(CompileTimeConstantExpr).getStringValue().toLowerCase().matches("%httponly%") + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr() = any(SetCookieMethodAccess ma).getArgument(1) + } +} + +/** A class descended from `javax.servlet.http.Cookie` or `javax/jakarta.ws.rs.core.Cookie`. */ +class CookieClass extends RefType { + CookieClass() { + this.getASupertype*() + .hasQualifiedName(["javax.servlet.http", "javax.ws.rs.core", "jakarta.ws.rs.core"], "Cookie") + } +} + +/** Holds if `expr` is any boolean-typed expression other than literal `false`. */ +// Inlined because this could be a very large result set if computed out of context +pragma[inline] +predicate mayBeBooleanTrue(Expr expr) { + expr.getType() instanceof BooleanType and + not expr.(CompileTimeConstantExpr).getBooleanValue() = false +} + +/** Holds if the method call may set the `HttpOnly` flag. */ +predicate setsCookieHttpOnly(MethodAccess ma) { + ma.getMethod().getName() = "setHttpOnly" and + // any use of setHttpOnly(x) where x isn't false is probably safe + mayBeBooleanTrue(ma.getArgument(0)) +} + +/** Holds if `ma` removes a cookie. */ +predicate removesCookie(MethodAccess ma) { + ma.getMethod().getName() = "setMaxAge" and + ma.getArgument(0).(IntegerLiteral).getIntValue() = 0 +} + +/** + * Holds if the MethodAccess `ma` is a test method call indicated by: + * a) in a test directory such as `src/test/java` + * b) in a test package whose name has the word `test` + * c) in a test class whose name has the word `test` + * d) in a test class implementing a test framework such as JUnit or TestNG + */ +predicate isTestMethod(MethodAccess ma) { + exists(Method m | + m = ma.getEnclosingCallable() and + ( + m.getDeclaringType().getName().toLowerCase().matches("%test%") or // Simple check to exclude test classes to reduce FPs + m.getDeclaringType().getPackage().getName().toLowerCase().matches("%test%") or // Simple check to exclude classes in test packages to reduce FPs + exists(m.getLocation().getFile().getAbsolutePath().indexOf("/src/test/java")) or // Match test directory structure of build tools like maven + m instanceof TestMethod // Test method of a test case implementing a test framework such as JUnit or TestNG + ) + ) +} + +/** + * A taint configuration tracking flow of a method that sets the `HttpOnly` flag, + * or one that removes a cookie, to a `ServletResponse.addCookie` call. + */ +class SetHttpOnlyOrRemovesCookieConfiguration extends TaintTracking2::Configuration { + SetHttpOnlyOrRemovesCookieConfiguration() { this = "SetHttpOnlyOrRemovesCookieConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() = + any(MethodAccess ma | setsCookieHttpOnly(ma) or removesCookie(ma)).getQualifier() + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr() = + any(MethodAccess ma | ma.getMethod() instanceof ResponseAddCookieMethod).getArgument(0) + } +} + +/** + * A cookie that is added to an HTTP response and which doesn't have `httpOnly` set, used as a sink + * in `MissingHttpOnlyConfiguration`. + */ +class CookieResponseSink extends DataFlow::ExprNode { + CookieResponseSink() { + exists(MethodAccess ma | + ( + ma.getMethod() instanceof ResponseAddCookieMethod and + this.getExpr() = ma.getArgument(0) and + not exists(SetHttpOnlyOrRemovesCookieConfiguration cc | cc.hasFlowTo(this)) + or + ma instanceof SetCookieMethodAccess and + this.getExpr() = ma.getArgument(1) and + not exists(MatchesHttpOnlyConfiguration cc | cc.hasFlowTo(this)) // response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure") + ) and + not isTestMethod(ma) // Test class or method + ) + } +} + +/** Holds if `cie` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */ +predicate setsHttpOnlyInNewCookie(ClassInstanceExpr cie) { + cie.getConstructedType().hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "NewCookie") and + ( + cie.getNumArgument() = 6 and + mayBeBooleanTrue(cie.getArgument(5)) // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly) + or + cie.getNumArgument() = 8 and + cie.getArgument(6).getType() instanceof BooleanType and + mayBeBooleanTrue(cie.getArgument(7)) // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) + or + cie.getNumArgument() = 10 and + mayBeBooleanTrue(cie.getArgument(9)) // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly) + ) +} + +/** + * A taint configuration tracking flow from a sensitive cookie without the `HttpOnly` flag + * set to its HTTP response. + */ +class MissingHttpOnlyConfiguration extends TaintTracking::Configuration { + MissingHttpOnlyConfiguration() { this = "MissingHttpOnlyConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof SensitiveCookieNameExpr + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof CookieResponseSink } + + override predicate isSanitizer(DataFlow::Node node) { + // JAX-RS's `new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true)` and similar + setsHttpOnlyInNewCookie(node.asExpr()) + } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists( + ConstructorCall cc // new Cookie(...) + | + cc.getConstructedType() instanceof CookieClass and + pred.asExpr() = cc.getAnArgument() and + succ.asExpr() = cc + ) + or + exists( + MethodAccess ma // cookie.toString() + | + ma.getMethod().getName() = "toString" and + ma.getQualifier().getType() instanceof CookieClass and + pred.asExpr() = ma.getQualifier() and + succ.asExpr() = ma + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, MissingHttpOnlyConfiguration c +where c.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ doesn't have the HttpOnly flag set.", source.getNode(), + "This sensitive cookie" diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql index 467d78ae1c4..9fa2fe596fd 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql @@ -68,7 +68,7 @@ predicate isBooleanTrue(Expr expr) { or exists(MethodAccess ma | expr = ma and - ma.getMethod().hasName("toString") and + ma.getMethod() instanceof ToStringMethod and ma.getQualifier().(FieldAccess).getField().hasName("TRUE") and ma.getQualifier() .(FieldAccess) diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll new file mode 100644 index 00000000000..b8f1a13b119 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll @@ -0,0 +1,57 @@ +import java +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +/** Json string type data. */ +abstract class JsonStringSource extends DataFlow::Node { } + +/** + * Convert to String using Gson library. * + * + * For example, in the method access `Gson.toJson(...)`, + * the `Object` type data is converted to the `String` type data. + */ +private class GsonString extends JsonStringSource { + GsonString() { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m.hasName("toJson") and + m.getDeclaringType().getASupertype*().hasQualifiedName("com.google.gson", "Gson") and + this.asExpr() = ma + ) + } +} + +/** + * Convert to String using Fastjson library. + * + * For example, in the method access `JSON.toJSONString(...)`, + * the `Object` type data is converted to the `String` type data. + */ +private class FastjsonString extends JsonStringSource { + FastjsonString() { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m.hasName("toJSONString") and + m.getDeclaringType().getASupertype*().hasQualifiedName("com.alibaba.fastjson", "JSON") and + this.asExpr() = ma + ) + } +} + +/** + * Convert to String using Jackson library. + * + * For example, in the method access `ObjectMapper.writeValueAsString(...)`, + * the `Object` type data is converted to the `String` type data. + */ +private class JacksonString extends JsonStringSource { + JacksonString() { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m.hasName("writeValueAsString") and + m.getDeclaringType() + .getASupertype*() + .hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") and + this.asExpr() = ma + ) + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java new file mode 100644 index 00000000000..8f39efbc2b6 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java @@ -0,0 +1,161 @@ +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +@Controller +public class JsonpInjection { + + private static HashMap hashMap = new HashMap(); + + static { + hashMap.put("username","admin"); + hashMap.put("password","123456"); + } + + @GetMapping(value = "jsonp1") + @ResponseBody + public String bad1(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + Gson gson = new Gson(); + String result = gson.toJson(hashMap); + resultStr = jsonpCallback + "(" + result + ")"; + return resultStr; + } + + @GetMapping(value = "jsonp2") + @ResponseBody + public String bad2(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")"; + return resultStr; + } + + @GetMapping(value = "jsonp3") + @ResponseBody + public String bad3(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + String jsonStr = getJsonStr(hashMap); + resultStr = jsonpCallback + "(" + jsonStr + ")"; + return resultStr; + } + + @GetMapping(value = "jsonp4") + @ResponseBody + public String bad4(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + String restr = JSONObject.toJSONString(hashMap); + resultStr = jsonpCallback + "(" + restr + ");"; + return resultStr; + } + + @GetMapping(value = "jsonp5") + @ResponseBody + public void bad5(HttpServletRequest request, + HttpServletResponse response) throws Exception { + String jsonpCallback = request.getParameter("jsonpCallback"); + PrintWriter pw = null; + Gson gson = new Gson(); + String result = gson.toJson(hashMap); + String resultStr = null; + pw = response.getWriter(); + resultStr = jsonpCallback + "(" + result + ")"; + pw.println(resultStr); + } + + @GetMapping(value = "jsonp6") + @ResponseBody + public void bad6(HttpServletRequest request, + HttpServletResponse response) throws Exception { + String jsonpCallback = request.getParameter("jsonpCallback"); + PrintWriter pw = null; + ObjectMapper mapper = new ObjectMapper(); + String result = mapper.writeValueAsString(hashMap); + String resultStr = null; + pw = response.getWriter(); + resultStr = jsonpCallback + "(" + result + ")"; + pw.println(resultStr); + } + + @RequestMapping(value = "jsonp7", method = RequestMethod.GET) + @ResponseBody + public String bad7(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + Gson gson = new Gson(); + String result = gson.toJson(hashMap); + resultStr = jsonpCallback + "(" + result + ")"; + return resultStr; + } + + @RequestMapping(value = "jsonp11") + @ResponseBody + public String good1(HttpServletRequest request) { + JSONObject parameterObj = readToJSONObect(request); + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + String restr = JSONObject.toJSONString(hashMap); + resultStr = jsonpCallback + "(" + restr + ");"; + return resultStr; + } + + @RequestMapping(value = "jsonp12") + @ResponseBody + public String good2(@RequestParam("file") MultipartFile file,HttpServletRequest request) { + if(null == file){ + return "upload file error"; + } + String fileName = file.getOriginalFilename(); + System.out.println("file operations"); + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + String restr = JSONObject.toJSONString(hashMap); + resultStr = jsonpCallback + "(" + restr + ");"; + return resultStr; + } + + public static JSONObject readToJSONObect(HttpServletRequest request){ + String jsonText = readPostContent(request); + JSONObject jsonObj = JSONObject.parseObject(jsonText, JSONObject.class); + return jsonObj; + } + + public static String readPostContent(HttpServletRequest request){ + BufferedReader in= null; + String content = null; + String line = null; + try { + in = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8")); + StringBuilder buf = new StringBuilder(); + while ((line = in.readLine()) != null) { + buf.append(line); + } + content = buf.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + String uri = request.getRequestURI(); + return content; + } + + public static String getJsonStr(Object result) { + return JSONObject.toJSONString(result); + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp new file mode 100644 index 00000000000..e8fb89d3989 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp @@ -0,0 +1,37 @@ + + + +

    The software uses external input as the function name to wrap JSON data and returns it to the client as a request response. +When there is a cross-domain problem, this could lead to information leakage.

    + +
    + + +

    Adding Referer/Origin or random token verification processing can effectively prevent the leakage of sensitive information.

    + +
    + + +

    The following examples show the bad case and the good case respectively. Bad cases, such as bad1 to bad7, +will cause information leakage when there are cross-domain problems. In a good case, for example, in the good1 +method and the good2 method, When these two methods process the request, there must be a request body in the request, which does not meet the conditions of Jsonp injection.

    + + + +
    + + +
  • +OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes: +JSON hijacking. +
  • +
  • +Practical JSONP Injection: + + Completely controllable from the URL (GET variable) +. +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql new file mode 100644 index 00000000000..71ee842f162 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql @@ -0,0 +1,45 @@ +/** + * @name JSONP Injection + * @description User-controlled callback function names that are not verified are vulnerable + * to jsonp injection attacks. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/jsonp-injection + * @tags security + * external/cwe/cwe-352 + */ + +import java +import JsonpInjectionLib +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.deadcode.WebEntryPoints +import DataFlow::PathGraph + +/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */ +class RequestResponseFlowConfig extends TaintTracking::Configuration { + RequestResponseFlowConfig() { this = "RequestResponseFlowConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource and + any(RequestGetMethod m).polyCalls*(source.getEnclosingCallable()) + } + + override predicate isSink(DataFlow::Node sink) { + sink instanceof XssSink and + any(RequestGetMethod m).polyCalls*(sink.getEnclosingCallable()) + } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(MethodAccess ma | + isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, RequestResponseFlowConfig conf +where + conf.hasFlowPath(source, sink) and + exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode())) +select sink.getNode(), source, sink, "Jsonp response might include code from $@.", source.getNode(), + "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll new file mode 100644 index 00000000000..6da4f87294a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll @@ -0,0 +1,118 @@ +import java +import DataFlow +import JsonStringLib +import semmle.code.java.security.XSS +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.DataFlow3 +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.frameworks.spring.SpringController + +/** + * A method that is called to handle an HTTP GET request. + */ +abstract class RequestGetMethod extends Method { + RequestGetMethod() { + not exists(MethodAccess ma | + // Exclude apparent GET handlers that read a request entity, because this likely indicates this is not in fact a GET handler. + // This is particularly a problem with Spring handlers, which can sometimes neglect to specify a request method. + // Even if it is in fact a GET handler, such a request method will be unusable in the context `