diff --git a/.github/workflows/close-stale.yml b/.github/workflows/close-stale.yml new file mode 100644 index 000000000000..c63a6be690c2 --- /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/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b86009ef6da9..87d6632d03e7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -19,13 +19,18 @@ jobs: runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + pull-requests: read + steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@main # Override language selection by uncommenting this and choosing your languages with: languages: csharp @@ -34,7 +39,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@main # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -48,4 +53,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@main diff --git a/config/identical-files.json b/config/identical-files.json index 6c1c0c7409d9..8cfca3596c5e 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -5,6 +5,7 @@ "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll", "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll", "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll", + "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll", @@ -56,6 +57,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 000000000000..4d0f8833adc9 --- /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 000000000000..1876f4cf5f7a --- /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/change-notes/2021-26-04-more-sound-expr-might-overflow.md b/cpp/change-notes/2021-26-04-more-sound-expr-might-overflow.md new file mode 100644 index 000000000000..5a7b8414fade --- /dev/null +++ b/cpp/change-notes/2021-26-04-more-sound-expr-might-overflow.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The `exprMightOverflowPositively` and `exprMightOverflowNegatively` predicates from the `SimpleRangeAnalysis` library now recognize more expressions that might overflow. diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp index 16fc75fc7ad8..aa97965996f2 100644 --- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp @@ -39,7 +39,7 @@ then replace all the relevant occurrences in the code.

  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 5: Object Life Cycle, Rec 5.4 (PDF). + Chapter 5: Object Life Cycle, Rec 5.4 (PDF).
  • DCL06-C. Use meaningful symbolic constants to represent literal values diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp index ca01aac69f02..19182fe5b19d 100644 --- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp @@ -38,7 +38,7 @@ constant.

  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 5: Object Life Cycle, Rec 5.4 (PDF). + Chapter 5: Object Life Cycle, Rec 5.4 (PDF).
  • DCL06-C. Use meaningful symbolic constants to represent literal values diff --git a/cpp/ql/src/Best Practices/SloppyGlobal.qhelp b/cpp/ql/src/Best Practices/SloppyGlobal.qhelp index a6255cc6a9e2..48b36e6d4767 100644 --- a/cpp/ql/src/Best Practices/SloppyGlobal.qhelp +++ b/cpp/ql/src/Best Practices/SloppyGlobal.qhelp @@ -21,7 +21,7 @@ Review the purpose of the each global variable flagged by this rule and update e
  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 1: Naming, Rec 1.1 (PDF). + Chapter 1: Naming, Rec 1.1 (PDF).
  • Global variables. diff --git a/cpp/ql/src/Best Practices/UseOfGoto.qhelp b/cpp/ql/src/Best Practices/UseOfGoto.qhelp index dd5702aabe4f..cfceb144605b 100644 --- a/cpp/ql/src/Best Practices/UseOfGoto.qhelp +++ b/cpp/ql/src/Best Practices/UseOfGoto.qhelp @@ -45,7 +45,7 @@ this rule.
  • Mats Henricson and Erik Nyquist, Industrial Strength C++, Rule 4.6. Prentice Hall PTR, 1997. - (PDF). + (PDF).
  • cplusplus.com: Control Structures. diff --git a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql index 353e51daa71a..e3873e487dd2 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/Critical/ReturnValueIgnored.qhelp b/cpp/ql/src/Critical/ReturnValueIgnored.qhelp index 26d0f5c6e848..de0636987a07 100644 --- a/cpp/ql/src/Critical/ReturnValueIgnored.qhelp +++ b/cpp/ql/src/Critical/ReturnValueIgnored.qhelp @@ -32,7 +32,7 @@ Check the return value of functions that return status information.
  • - M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 12: Error handling. Prentice Hall PTR, 1997 (available online). + M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 12: Error handling. Prentice Hall PTR, 1997 (available online).
  • The CERT C Secure Coding Standard: EXP32-PL. Do not ignore function return values. diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp index 88a150a0ba25..3c9a68b97e2d 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp +++ b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp @@ -26,7 +26,7 @@ indication that there may be cases unhandled by the switch statemen MSDN Library: switch statement (C++)
  • - M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 4: Control Flow, Rec 4.5. Prentice Hall PTR, 1997 (available online). + M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 4: Control Flow, Rec 4.5. Prentice Hall PTR, 1997 (available online).
  • diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql index fc06ed0a500e..f5dda53d4844 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/Metrics/Files/FTransitiveIncludes.qhelp b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp index 4e0b989b1213..846e54b33c9f 100644 --- a/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp +++ b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp @@ -29,7 +29,7 @@ build time: the more included files, the longer the compilation time.

    Decoupling C Header Files
  • - C++ Best Practice - + C++ Best Practice - Designing Header Files
  • diff --git a/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp index a3091c68285d..785ad68847da 100644 --- a/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp +++ b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp @@ -35,7 +35,7 @@ they are contributing to unnecessarily long build times and creating artificial Decoupling C Header Files
  • - C++ Best Practice - + C++ Best Practice - Designing Header Files
  • diff --git a/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql index 0adb600dbda6..7a7aa716e8a7 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql @@ -28,6 +28,7 @@ predicate outOfBoundsExpr(Expr expr, string kind) { from Expr use, Expr origin, string kind where + not use.getUnspecifiedType() instanceof PointerType and outOfBoundsExpr(use, kind) and tainted(origin, use) and origin != use and 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 000000000000..1f1f10b89cc5 --- /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 000000000000..4167ce57d65e --- /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 000000000000..163305dd0399 --- /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 000000000000..276318431186 --- /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 000000000000..f49d5a4fe78f --- /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 000000000000..72d7625b5170 --- /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 000000000000..8458d82f7ad2 --- /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 000000000000..8114da831feb --- /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 000000000000..4f30f112eb07 --- /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 012109074e9a..e4577968730e 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 5311ffe2708e..f68395f29bff 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/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp index d4edec95db93..fde62870954f 100644 --- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp @@ -33,7 +33,7 @@ the break statement only exits from one level of the loop.

  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 4: Control Flow, Rule 4.6 (PDF). + Chapter 4: Control Flow, Rule 4.6 (PDF).
  • www.cplusplus.com Control Structures diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp index c165fe59ba8c..19d01d570cd2 100644 --- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp @@ -39,7 +39,7 @@ loop if the loop requires more complicated variable iteration.
  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 4: Control Flow, Rule 4.1 (PDF). + Chapter 4: Control Flow, Rule 4.1 (PDF).
  • diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll index b47618de2e92..31ef5570451b 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll @@ -1307,7 +1307,8 @@ private predicate conditionJumps(Expr test, boolean truth, Node n2, Pos p2) { ) } -// Factored out for performance. See QL-796. +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. private predicate normalGroupMemberBaseCase(Node memberNode, Pos memberPos, Node atNode) { memberNode = atNode and memberPos.isAt() and diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll index 77e0f05ed020..476f626e8749 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll @@ -104,9 +104,43 @@ private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condit ) } +/** + * This relation is the same as the `el instanceof Function`, only obfuscated + * so the optimizer will not understand that any `FunctionCall.getTarget()` + * should be in this relation. + */ +pragma[noinline] +private predicate isFunction(Element el) { + el instanceof Function + or + el.(Expr).getParent() = el +} + +/** + * Holds if `fc` is a `FunctionCall` with no return value for `getTarget`. This + * can happen in case of rare database inconsistencies. + */ +pragma[noopt] +private predicate callHasNoTarget(@funbindexpr fc) { + exists(Function f | + funbind(fc, f) and + not isFunction(f) + ) +} + +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. +private predicate potentiallyReturningFunctionCall_base(FunctionCall fc) { + fc.isVirtual() + or + callHasNoTarget(fc) +} + /** A function call that *may* return; if in doubt, we assume it may. */ private predicate potentiallyReturningFunctionCall(FunctionCall fc) { - potentiallyReturningFunction(fc.getTarget()) or fc.isVirtual() + potentiallyReturningFunctionCall_base(fc) + or + potentiallyReturningFunction(fc.getTarget()) } /** A function that *may* return; if in doubt, we assume it may. */ 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 7024f9d8598c..f6072763e1a4 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 0a6d459ec79b..558b43cb0da7 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()) } @@ -694,7 +694,12 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) { fromExpr = call.getQualifier() ) and call.getTarget() = f and - outModel.isReturnValue() + // AST dataflow treats a reference as if it were the referred-to object, while the dataflow + // models treat references as pointers. If the return type of the call is a reference, then + // look for data flow the the referred-to object, rather than the reference itself. + if call.getType().getUnspecifiedType() instanceof ReferenceType + then outModel.isReturnValueDeref() + else outModel.isReturnValue() ) ) } @@ -715,6 +720,7 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) { } private module FieldFlow { + private import DataFlowImplCommon private import DataFlowImplLocal private import DataFlowPrivate @@ -747,7 +753,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 f3aa94a79920..e3f8b6f68fb5 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 1ef340c4f213..591f461c8eb9 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 349efdcee103..6f6f710ac4b3 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/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll index e02a72fe680a..86f538e5d4e5 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll @@ -1271,7 +1271,8 @@ private predicate convparents(Expr child, int idx, Element parent) { ) } -// Pulled out for performance. See QL-796. +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. private predicate hasNoConversions(Expr e) { not e.hasConversion() } /** 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 76ca7b215dce..f5fb7309cff6 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 337dc71a3caa..16182296e400 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 000000000000..8c3182162170 --- /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 000000000000..5fc15cf986c7 --- /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/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index af6f3d819824..13c384017379 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -1645,6 +1645,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index 19fb0490f808..e26d61b9792c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -4,6 +4,71 @@ private import AliasAnalysisImports private class IntValue = Ints::IntValue; +/** + * If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side + * effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`. + */ +private CallInstruction getPrimaryCall(Instruction instr) { + result = instr + or + result = instr.(SideEffectInstruction).getPrimaryInstruction() +} + +/** + * Holds if `operand` serves as an input argument (or indirection) to `call`, in the position + * specified by `input`. + */ +private predicate isCallInput( + CallInstruction call, Operand operand, AliasModels::FunctionInput input +) { + call = getPrimaryCall(operand.getUse()) and + ( + exists(int index | + input.isParameterOrQualifierAddress(index) and + operand = call.getArgumentOperand(index) + ) + or + exists(int index, ReadSideEffectInstruction read | + input.isParameterDerefOrQualifierObject(index) and + read = call.getAParameterSideEffect(index) and + operand = read.getSideEffectOperand() + ) + ) +} + +/** + * Holds if `instr` serves as a return value or output argument indirection for `call`, in the + * position specified by `output`. + */ +private predicate isCallOutput( + CallInstruction call, Instruction instr, AliasModels::FunctionOutput output +) { + call = getPrimaryCall(instr) and + ( + output.isReturnValue() and instr = call + or + exists(int index, WriteSideEffectInstruction write | + output.isParameterDerefOrQualifierObject(index) and + write = call.getAParameterSideEffect(index) and + instr = write + ) + ) +} + +/** + * Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled + * address flow through a function call. + */ +private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) { + exists( + CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output + | + call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and + isCallInput(call, operand, input) and + isCallOutput(call, resultInstr, output) + ) +} + /** * Holds if the operand `tag` of instruction `instr` is used in a way that does * not result in any address held in that operand from escaping beyond the @@ -34,7 +99,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { private predicate operandEscapesDomain(Operand operand) { not operandIsConsumedWithoutEscaping(operand) and - not operandIsPropagated(operand, _) and + not operandIsPropagated(operand, _, _) and not isArgumentForParameter(_, operand, _) and not isOnlyEscapesViaReturnArgument(operand) and not operand.getUse() instanceof ReturnValueInstruction and @@ -69,67 +134,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) { } /** - * Holds if any address held in operand `tag` of instruction `instr` is - * propagated to the result of `instr`, offset by the number of bits in - * `bitOffset`. If the address is propagated, but the offset is not known to be - * a constant, then `bitOffset` is unknown. + * Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by + * the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to + * be a constant, then `bitOffset` is `unknown()`. */ -private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { - exists(Instruction instr | - instr = operand.getUse() and - ( - // Converting to a non-virtual base class adds the offset of the base class. - exists(ConvertToNonVirtualBaseInstruction convert | - convert = instr and - bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) - ) - or - // Conversion using dynamic_cast results in an unknown offset - instr instanceof CheckedConvertOrNullInstruction and - bitOffset = Ints::unknown() - or - // Converting to a derived class subtracts the offset of the base class. - exists(ConvertToDerivedInstruction convert | - convert = instr and - bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) - ) - or - // Converting to a virtual base class adds an unknown offset. - instr instanceof ConvertToVirtualBaseInstruction and - bitOffset = Ints::unknown() - or - // Conversion to another pointer type propagates the source address. - exists(ConvertInstruction convert, IRType resultType | - convert = instr and - resultType = convert.getResultIRType() and - resultType instanceof IRAddressType and - bitOffset = 0 - ) - or - // Adding an integer to or subtracting an integer from a pointer propagates - // the address with an offset. - exists(PointerOffsetInstruction ptrOffset | - ptrOffset = instr and - operand = ptrOffset.getLeftOperand() and - bitOffset = getPointerBitOffset(ptrOffset) - ) - or - // Computing a field address from a pointer propagates the address plus the - // offset of the field. - bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) - or - // A copy propagates the source value. - operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 - or - // Some functions are known to propagate an argument - isAlwaysReturnedArgument(operand) and bitOffset = 0 +private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) { + // Some functions are known to propagate an argument + hasAddressFlowThroughCall(operand, instr) and + bitOffset = 0 + or + instr = operand.getUse() and + ( + // Converting to a non-virtual base class adds the offset of the base class. + exists(ConvertToNonVirtualBaseInstruction convert | + convert = instr and + bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) + ) + or + // Conversion using dynamic_cast results in an unknown offset + instr instanceof CheckedConvertOrNullInstruction and + bitOffset = Ints::unknown() + or + // Converting to a derived class subtracts the offset of the base class. + exists(ConvertToDerivedInstruction convert | + convert = instr and + bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) + ) + or + // Converting to a virtual base class adds an unknown offset. + instr instanceof ConvertToVirtualBaseInstruction and + bitOffset = Ints::unknown() + or + // Conversion to another pointer type propagates the source address. + exists(ConvertInstruction convert, IRType resultType | + convert = instr and + resultType = convert.getResultIRType() and + resultType instanceof IRAddressType and + bitOffset = 0 ) + or + // Adding an integer to or subtracting an integer from a pointer propagates + // the address with an offset. + exists(PointerOffsetInstruction ptrOffset | + ptrOffset = instr and + operand = ptrOffset.getLeftOperand() and + bitOffset = getPointerBitOffset(ptrOffset) + ) + or + // Computing a field address from a pointer propagates the address plus the + // offset of the field. + bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) + or + // A copy propagates the source value. + operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 ) } private predicate operandEscapesNonReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -151,9 +216,11 @@ private predicate operandEscapesNonReturn(Operand operand) { } private predicate operandMayReachReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and - resultMayReachReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and + resultMayReachReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -173,9 +240,9 @@ private predicate operandMayReachReturn(Operand operand) { private predicate operandReturned(Operand operand, IntValue bitOffset) { // The address is propagated to the result of the instruction, and that result itself is returned - exists(IntValue bitOffset1, IntValue bitOffset2 | - operandIsPropagated(operand, bitOffset1) and - resultReturned(operand.getUse(), bitOffset2) and + exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 | + operandIsPropagated(operand, bitOffset1, instr) and + resultReturned(instr, bitOffset2) and bitOffset = Ints::add(bitOffset1, bitOffset2) ) or @@ -214,13 +281,6 @@ private predicate isArgumentForParameter( ) } -private predicate isAlwaysReturnedArgument(Operand operand) { - exists(AliasModels::AliasFunction f | - f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) - ) -} - private predicate isOnlyEscapesViaReturnArgument(Operand operand) { exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and @@ -270,12 +330,15 @@ predicate allocationEscapes(Configuration::Allocation allocation) { /** * Equivalent to `operandIsPropagated()`, but includes interprocedural propagation. */ -private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) { - operandIsPropagated(operand, bitOffset) +private predicate operandIsPropagatedIncludingByCall( + Operand operand, IntValue bitOffset, Instruction instr +) { + operandIsPropagated(operand, bitOffset, instr) or exists(CallInstruction call, Instruction init | isArgumentForParameter(call, operand, init) and - resultReturned(init, bitOffset) + resultReturned(init, bitOffset) and + instr = call ) } @@ -292,8 +355,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, // We already have an offset from `middle`. hasBaseAndOffset(addrOperand, middle, previousBitOffset) and // `middle` is propagated from `base`. - middleOperand = middle.getAnOperand() and - operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and + operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and base = middleOperand.getDef() and bitOffset = Ints::add(previousBitOffset, additionalBitOffset) ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll index 69cd6e6dc291..866afb64b31a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll @@ -105,7 +105,21 @@ class DynamicAllocation extends Allocation, TDynamicAllocation { DynamicAllocation() { this = TDynamicAllocation(call) } final override string toString() { - result = call.toString() + " at " + call.getLocation() // This isn't performant, but it's only used in test/dump code right now. + // This isn't performant, but it's only used in test/dump code right now. + // Dynamic allocations within a function are numbered in the order by start + // line number. This keeps them stable when the function moves within the + // file, or when non-allocating lines are added and removed within the + // function. + exists(int i | + result = "dynamic{" + i.toString() + "}" and + call = + rank[i](CallInstruction rangeCall | + exists(TDynamicAllocation(rangeCall)) and + rangeCall.getEnclosingIRFunction() = call.getEnclosingIRFunction() + | + rangeCall order by rangeCall.getLocation().getStartLine() + ) + ) } final override CallInstruction getABaseInstruction() { result = call } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index af6f3d819824..13c384017379 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -1645,6 +1645,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** 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 000000000000..350127a58d17 --- /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 7ad1dd2c01e5..56d4c807ac85 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 1de9936ae1fb..81c69cf0ea22 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 @@ -41,7 +42,8 @@ IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) { */ predicate isIRConstant(Expr expr) { exists(expr.getValue()) } -// Pulled out to work around QL-796 +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. private predicate isOrphan(Expr expr) { not exists(getRealParent(expr)) } /** @@ -635,46 +637,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 + TTranslatedArgumentSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) { + not ignoreExpr(expr) and + not ignoreExpr(call) 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 - ) + n >= 0 and expr = call.getArgument(n).getFullyConverted() or - not call.getTarget() instanceof SideEffectFunction and - n = -1 and - ( - isWrite = true and - not call.getTarget() instanceof ConstMemberFunction - or - isWrite = false - ) + n = -1 and expr = call.getQualifier().getFullyConverted() ) and - not ignoreExpr(expr) and - not ignoreExpr(call) + opcode = getASideEffectOpcode(call, n) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index af6f3d819824..13c384017379 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -1645,6 +1645,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 19fb0490f808..e26d61b9792c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -4,6 +4,71 @@ private import AliasAnalysisImports private class IntValue = Ints::IntValue; +/** + * If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side + * effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`. + */ +private CallInstruction getPrimaryCall(Instruction instr) { + result = instr + or + result = instr.(SideEffectInstruction).getPrimaryInstruction() +} + +/** + * Holds if `operand` serves as an input argument (or indirection) to `call`, in the position + * specified by `input`. + */ +private predicate isCallInput( + CallInstruction call, Operand operand, AliasModels::FunctionInput input +) { + call = getPrimaryCall(operand.getUse()) and + ( + exists(int index | + input.isParameterOrQualifierAddress(index) and + operand = call.getArgumentOperand(index) + ) + or + exists(int index, ReadSideEffectInstruction read | + input.isParameterDerefOrQualifierObject(index) and + read = call.getAParameterSideEffect(index) and + operand = read.getSideEffectOperand() + ) + ) +} + +/** + * Holds if `instr` serves as a return value or output argument indirection for `call`, in the + * position specified by `output`. + */ +private predicate isCallOutput( + CallInstruction call, Instruction instr, AliasModels::FunctionOutput output +) { + call = getPrimaryCall(instr) and + ( + output.isReturnValue() and instr = call + or + exists(int index, WriteSideEffectInstruction write | + output.isParameterDerefOrQualifierObject(index) and + write = call.getAParameterSideEffect(index) and + instr = write + ) + ) +} + +/** + * Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled + * address flow through a function call. + */ +private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) { + exists( + CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output + | + call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and + isCallInput(call, operand, input) and + isCallOutput(call, resultInstr, output) + ) +} + /** * Holds if the operand `tag` of instruction `instr` is used in a way that does * not result in any address held in that operand from escaping beyond the @@ -34,7 +99,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { private predicate operandEscapesDomain(Operand operand) { not operandIsConsumedWithoutEscaping(operand) and - not operandIsPropagated(operand, _) and + not operandIsPropagated(operand, _, _) and not isArgumentForParameter(_, operand, _) and not isOnlyEscapesViaReturnArgument(operand) and not operand.getUse() instanceof ReturnValueInstruction and @@ -69,67 +134,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) { } /** - * Holds if any address held in operand `tag` of instruction `instr` is - * propagated to the result of `instr`, offset by the number of bits in - * `bitOffset`. If the address is propagated, but the offset is not known to be - * a constant, then `bitOffset` is unknown. + * Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by + * the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to + * be a constant, then `bitOffset` is `unknown()`. */ -private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { - exists(Instruction instr | - instr = operand.getUse() and - ( - // Converting to a non-virtual base class adds the offset of the base class. - exists(ConvertToNonVirtualBaseInstruction convert | - convert = instr and - bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) - ) - or - // Conversion using dynamic_cast results in an unknown offset - instr instanceof CheckedConvertOrNullInstruction and - bitOffset = Ints::unknown() - or - // Converting to a derived class subtracts the offset of the base class. - exists(ConvertToDerivedInstruction convert | - convert = instr and - bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) - ) - or - // Converting to a virtual base class adds an unknown offset. - instr instanceof ConvertToVirtualBaseInstruction and - bitOffset = Ints::unknown() - or - // Conversion to another pointer type propagates the source address. - exists(ConvertInstruction convert, IRType resultType | - convert = instr and - resultType = convert.getResultIRType() and - resultType instanceof IRAddressType and - bitOffset = 0 - ) - or - // Adding an integer to or subtracting an integer from a pointer propagates - // the address with an offset. - exists(PointerOffsetInstruction ptrOffset | - ptrOffset = instr and - operand = ptrOffset.getLeftOperand() and - bitOffset = getPointerBitOffset(ptrOffset) - ) - or - // Computing a field address from a pointer propagates the address plus the - // offset of the field. - bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) - or - // A copy propagates the source value. - operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 - or - // Some functions are known to propagate an argument - isAlwaysReturnedArgument(operand) and bitOffset = 0 +private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) { + // Some functions are known to propagate an argument + hasAddressFlowThroughCall(operand, instr) and + bitOffset = 0 + or + instr = operand.getUse() and + ( + // Converting to a non-virtual base class adds the offset of the base class. + exists(ConvertToNonVirtualBaseInstruction convert | + convert = instr and + bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) + ) + or + // Conversion using dynamic_cast results in an unknown offset + instr instanceof CheckedConvertOrNullInstruction and + bitOffset = Ints::unknown() + or + // Converting to a derived class subtracts the offset of the base class. + exists(ConvertToDerivedInstruction convert | + convert = instr and + bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) + ) + or + // Converting to a virtual base class adds an unknown offset. + instr instanceof ConvertToVirtualBaseInstruction and + bitOffset = Ints::unknown() + or + // Conversion to another pointer type propagates the source address. + exists(ConvertInstruction convert, IRType resultType | + convert = instr and + resultType = convert.getResultIRType() and + resultType instanceof IRAddressType and + bitOffset = 0 ) + or + // Adding an integer to or subtracting an integer from a pointer propagates + // the address with an offset. + exists(PointerOffsetInstruction ptrOffset | + ptrOffset = instr and + operand = ptrOffset.getLeftOperand() and + bitOffset = getPointerBitOffset(ptrOffset) + ) + or + // Computing a field address from a pointer propagates the address plus the + // offset of the field. + bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) + or + // A copy propagates the source value. + operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 ) } private predicate operandEscapesNonReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -151,9 +216,11 @@ private predicate operandEscapesNonReturn(Operand operand) { } private predicate operandMayReachReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and - resultMayReachReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and + resultMayReachReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -173,9 +240,9 @@ private predicate operandMayReachReturn(Operand operand) { private predicate operandReturned(Operand operand, IntValue bitOffset) { // The address is propagated to the result of the instruction, and that result itself is returned - exists(IntValue bitOffset1, IntValue bitOffset2 | - operandIsPropagated(operand, bitOffset1) and - resultReturned(operand.getUse(), bitOffset2) and + exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 | + operandIsPropagated(operand, bitOffset1, instr) and + resultReturned(instr, bitOffset2) and bitOffset = Ints::add(bitOffset1, bitOffset2) ) or @@ -214,13 +281,6 @@ private predicate isArgumentForParameter( ) } -private predicate isAlwaysReturnedArgument(Operand operand) { - exists(AliasModels::AliasFunction f | - f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) - ) -} - private predicate isOnlyEscapesViaReturnArgument(Operand operand) { exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and @@ -270,12 +330,15 @@ predicate allocationEscapes(Configuration::Allocation allocation) { /** * Equivalent to `operandIsPropagated()`, but includes interprocedural propagation. */ -private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) { - operandIsPropagated(operand, bitOffset) +private predicate operandIsPropagatedIncludingByCall( + Operand operand, IntValue bitOffset, Instruction instr +) { + operandIsPropagated(operand, bitOffset, instr) or exists(CallInstruction call, Instruction init | isArgumentForParameter(call, operand, init) and - resultReturned(init, bitOffset) + resultReturned(init, bitOffset) and + instr = call ) } @@ -292,8 +355,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, // We already have an offset from `middle`. hasBaseAndOffset(addrOperand, middle, previousBitOffset) and // `middle` is propagated from `base`. - middleOperand = middle.getAnOperand() and - operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and + operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and base = middleOperand.getDef() and bitOffset = Ints::add(previousBitOffset, additionalBitOffset) ) 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 4bcf5aa3575f..7f9639f63732 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll @@ -1,35 +1,64 @@ +import semmle.code.cpp.models.interfaces.Alias +import semmle.code.cpp.models.interfaces.SideEffect import semmle.code.cpp.models.interfaces.Taint import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.PointerWrapper /** - * The `std::shared_ptr` and `std::unique_ptr` template classes. + * The `std::shared_ptr`, `std::weak_ptr`, and `std::unique_ptr` template classes. */ -private class UniqueOrSharedPtr extends Class, PointerWrapper { - UniqueOrSharedPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "unique_ptr"]) } +private class SmartPtr extends Class, PointerWrapper { + SmartPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "weak_ptr", "unique_ptr"]) } override MemberFunction getAnUnwrapperFunction() { result.(OverloadedPointerDereferenceFunction).getDeclaringType() = this 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 PointerWrapperDataFlow extends DataFlowFunction { - PointerWrapperDataFlow() { - this = any(PointerWrapper wrapper).getAnUnwrapperFunction() and - not this.getUnspecifiedType() instanceof ReferenceType +/** + * Any function that returns the address wrapped by a `PointerWrapper`, whether as a pointer or a + * reference. + * + * Examples: + * - `std::unique_ptr::get()` + * - `std::shared_ptr::operator->()` + * - `std::weak_ptr::operator*()` + */ +private class PointerUnwrapperFunction extends MemberFunction, TaintFunction, DataFlowFunction, + SideEffectFunction, AliasFunction { + PointerUnwrapperFunction() { + exists(PointerWrapper wrapper | wrapper.getAnUnwrapperFunction() = this) } - override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { - input.isQualifierAddress() and output.isReturnValue() - or - input.isQualifierObject() and output.isReturnValueDeref() - or + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { input.isReturnValueDeref() and output.isQualifierObject() } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and output.isReturnValue() + } + + override predicate hasOnlySpecificReadSideEffects() { any() } + + override predicate hasOnlySpecificWriteSideEffects() { any() } + + override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { + // Only reads from `*this`. + i = -1 and buffer = false + } + + override predicate parameterNeverEscapes(int index) { index = -1 } + + override predicate parameterEscapesOnlyViaReturn(int index) { none() } + + override predicate hasAddressFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and output.isReturnValue() + } } /** @@ -60,31 +89,80 @@ private class MakeUniqueOrShared extends TaintFunction { } /** - * A prefix `operator*` member function for a `shared_ptr` or `unique_ptr` type. + * A function that sets the value of a smart pointer. + * + * This could be a constructor, an assignment operator, or a named member function like `reset()`. */ -private class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunction { - UniqueOrSharedDereferenceMemberOperator() { - this.hasName("operator*") and - this.getDeclaringType() instanceof UniqueOrSharedPtr +private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, SideEffectFunction { + SmartPtrSetterFunction() { + this.getDeclaringType() instanceof SmartPtr and + not this.isStatic() and + ( + this instanceof Constructor + or + this.hasName("operator=") + or + this.hasName("reset") + ) } - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - input.isQualifierObject() and - output.isReturnValueDeref() + override predicate hasOnlySpecificReadSideEffects() { none() } + + override predicate hasOnlySpecificWriteSideEffects() { none() } + + override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) { + // Always write to the destination smart pointer itself. + i = -1 and buffer = false and mustWrite = true + or + // When taking ownership of a smart pointer via an rvalue reference, always overwrite the input + // smart pointer. + getPointerInput().isParameterDeref(i) and + this.getParameter(i).getUnspecifiedType() instanceof RValueReferenceType and + buffer = false and + mustWrite = true } -} -/** - * The `std::shared_ptr` or `std::unique_ptr` function `get`. - */ -private class UniqueOrSharedGet extends TaintFunction { - UniqueOrSharedGet() { - this.hasName("get") and - this.getDeclaringType() instanceof UniqueOrSharedPtr + override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { + getPointerInput().isParameterDeref(i) and + buffer = false + or + not this instanceof Constructor and + i = -1 and + buffer = false } - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - input.isQualifierObject() and + override predicate parameterNeverEscapes(int index) { index = -1 } + + override predicate parameterEscapesOnlyViaReturn(int index) { none() } + + override predicate hasAddressFlow(FunctionInput input, FunctionOutput output) { + input = getPointerInput() and + output.isQualifierObject() + or + // Assignment operator always returns a reference to `*this`. + this.hasName("operator=") and + input.isQualifierAddress() and output.isReturnValue() } + + private FunctionInput getPointerInput() { + exists(Parameter param0 | + param0 = this.getParameter(0) and + ( + param0.getUnspecifiedType().(ReferenceType).getBaseType() instanceof SmartPtr and + if this.getParameter(1).getUnspecifiedType() instanceof PointerType + then + // This is one of the constructors of `std::shared_ptr` that creates a smart pointer that + // wraps a raw pointer with ownership controlled by an unrelated smart pointer. We propagate + // the raw pointer in the second parameter, rather than the smart pointer in the first + // parameter. + result.isParameter(1) + else result.isParameterDeref(0) + ) + or + // One of the functions that takes ownership of a raw pointer. + param0.getUnspecifiedType() instanceof PointerType and + result.isParameter(0) + ) + } } diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll index 083bbf55c388..e947a93fc908 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll @@ -50,5 +50,16 @@ abstract class AliasFunction extends Function { /** * Holds if the function always returns the value of the parameter at the specified index. */ - abstract predicate parameterIsAlwaysReturned(int index); + predicate parameterIsAlwaysReturned(int index) { none() } + + /** + * Holds if the address passed in via `input` is always propagated to `output`. + */ + predicate hasAddressFlow(FunctionInput input, FunctionOutput output) { + exists(int index | + // By default, just use the old `parameterIsAlwaysReturned` predicate to detect flow from the + // parameter to the return value. + input.isParameter(index) and output.isReturnValue() and this.parameterIsAlwaysReturned(index) + ) + } } 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 447e7f7cedea..8948aee424b2 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/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index f3bbbddd97e4..79dbe49611e0 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -1617,6 +1617,20 @@ private module SimpleRangeAnalysisCached { defMightOverflowPositively(def, v) } + /** + * Holds if `e` is an expression where the concept of overflow makes sense. + * This predicate is used to filter out some of the unanalyzable expressions + * from `exprMightOverflowPositively` and `exprMightOverflowNegatively`. + */ + pragma[inline] + private predicate exprThatCanOverflow(Expr e) { + e instanceof UnaryArithmeticOperation or + e instanceof BinaryArithmeticOperation or + e instanceof AssignArithmeticOperation or + e instanceof LShiftExpr or + e instanceof AssignLShiftExpr + } + /** * Holds if the expression might overflow negatively. This predicate * does not consider the possibility that the expression might overflow @@ -1630,6 +1644,11 @@ private module SimpleRangeAnalysisCached { // bound of `x`, so the standard logic (above) does not work for // detecting whether it might overflow. getLowerBoundsImpl(expr.(PostfixDecrExpr)) = exprMinVal(expr) + or + // We can't conclude that any unanalyzable expression might overflow. This + // is because there are many expressions that the range analysis doesn't + // handle, but where the concept of overflow doesn't make sense. + exprThatCanOverflow(expr) and not analyzableExpr(expr) } /** @@ -1657,6 +1676,11 @@ private module SimpleRangeAnalysisCached { // bound of `x`, so the standard logic (above) does not work for // detecting whether it might overflow. getUpperBoundsImpl(expr.(PostfixIncrExpr)) = exprMaxVal(expr) + or + // We can't conclude that any unanalyzable expression might overflow. This + // is because there are many expressions that the range analysis doesn't + // handle, but where the concept of overflow doesn't make sense. + exprThatCanOverflow(expr) and not analyzableExpr(expr) } /** diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll index e7ad1c559e63..6cf82791d526 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,14 @@ 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) { + // Since `e` is guarenteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or + // an `AssignArithmeticOperation` by the other constraints in this predicate, we know that + // `convertedExprMightOverflowPositively` will have a result even when `e` is not analyzable + // by `SimpleRangeAnalysis`. + convertedExprMightOverflowPositively(e) and use = e.getAnOperand() and exists(LocalScopeVariable v | use.getTarget() = v | // overflow possible if large @@ -115,9 +122,14 @@ 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) { + // Since `e` is guarenteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or + // an `AssignArithmeticOperation` by the other constraints in this predicate, we know that + // `convertedExprMightOverflowNegatively` will have a result even when `e` is not analyzable + // by `SimpleRangeAnalysis`. + convertedExprMightOverflowNegatively(e) 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 000000000000..1eca77a526f1 --- /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 000000000000..496d5f1b7be6 --- /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 000000000000..d11bbd446a61 --- /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 000000000000..9bf28db3c8a8 --- /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 000000000000..1f41f499dede --- /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 103afd8ffd94..cd160a70638c 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 af52dac01447..3e9791e10247 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 000000000000..1209c7e78307 --- /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 000000000000..5189abcce5d1 --- /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 d986bb3b13c6..6d3108756307 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 000000000000..f08d2a45757f --- /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/include/memory.h b/cpp/ql/test/include/memory.h new file mode 100644 index 000000000000..fdd09a7d43b5 --- /dev/null +++ b/cpp/ql/test/include/memory.h @@ -0,0 +1,139 @@ +#if !defined(CODEQL_MEMORY_H) +#define CODEQL_MEMORY_H + +namespace std { + namespace detail { + template + class compressed_pair_element { + T element; + + public: + compressed_pair_element() = default; + compressed_pair_element(const T& t) : element(t) {} + + T& get() { return element; } + + const T& get() const { return element; } + }; + + template + struct compressed_pair : private compressed_pair_element, private compressed_pair_element { + compressed_pair() = default; + compressed_pair(T& t) : compressed_pair_element(t), compressed_pair_element() {} + compressed_pair(const compressed_pair&) = delete; + compressed_pair(compressed_pair&&) noexcept = default; + + T& first() { return static_cast&>(*this).get(); } + U& second() { return static_cast&>(*this).get(); } + + const T& first() const { return static_cast&>(*this).get(); } + const U& second() const { return static_cast&>(*this).get(); } + }; + } + + template + struct default_delete { + void operator()(T* ptr) const { delete ptr; } + }; + + template + struct default_delete { + template + void operator()(U* ptr) const { delete[] ptr; } + }; + + template > + class unique_ptr { + private: + detail::compressed_pair data; + public: + constexpr unique_ptr() noexcept {} + explicit unique_ptr(T* ptr) noexcept : data(ptr) {} + unique_ptr(const unique_ptr& ptr) = delete; + unique_ptr(unique_ptr&& ptr) noexcept = default; + + unique_ptr& operator=(unique_ptr&& ptr) noexcept = default; + + T& operator*() const { return *get(); } + T* operator->() const noexcept { return get(); } + + T* get() const noexcept { return data.first(); } + T* release() noexcept { + Deleter& d = data.second(); + d(data.first()); + data.first() = nullptr; + } + + ~unique_ptr() { + Deleter& d = data.second(); + d(data.first()); + } + }; + + template unique_ptr make_unique(Args&&... args) { + return unique_ptr(new T(args...)); // std::forward calls elided for simplicity. + } + + class ctrl_block { + unsigned uses; + + public: + ctrl_block() : uses(1) {} + + void inc() { ++uses; } + bool dec() { return --uses == 0; } + + virtual void destroy() = 0; + virtual ~ctrl_block() {} + }; + + template > + struct ctrl_block_impl: public ctrl_block { + T* ptr; + Deleter d; + + ctrl_block_impl(T* ptr, Deleter d) : ptr(ptr), d(d) {} + virtual void destroy() override { d(ptr); } + }; + + template + class shared_ptr { + private: + ctrl_block* ctrl; + T* ptr; + + void dec() { + if(ctrl->dec()) { + ctrl->destroy(); + delete ctrl; + } + } + + void inc() { + ctrl->inc(); + } + + public: + constexpr shared_ptr() noexcept = default; + shared_ptr(T* ptr) : ctrl(new ctrl_block_impl(ptr, default_delete())) {} + shared_ptr(const shared_ptr& s) noexcept : ptr(s.ptr), ctrl(s.ctrl) { + inc(); + } + shared_ptr(shared_ptr&& s) noexcept = default; + shared_ptr(unique_ptr&& s) : shared_ptr(s.release()) { + } + T* operator->() const { return ptr; } + + T& operator*() const { return *ptr; } + + T* get() const noexcept { return ptr; } + + ~shared_ptr() { dec(); } + }; + + template shared_ptr make_shared(Args&&... args) { + return shared_ptr(new T(args...)); // std::forward calls elided for simplicity. + } +} + +#endif \ No newline at end of file diff --git a/cpp/ql/test/include/type_traits.h b/cpp/ql/test/include/type_traits.h new file mode 100644 index 000000000000..19bdd46906be --- /dev/null +++ b/cpp/ql/test/include/type_traits.h @@ -0,0 +1,21 @@ +#if !defined(CODEQL_TYPE_TRAITS_H) +#define CODEQL_TYPE_TRAITS_H + +namespace std { + template + struct remove_reference { + typedef T type; + }; + + template + struct remove_reference { + typedef T type; + }; + + template + struct remove_reference { + typedef T type; + }; +} + +#endif diff --git a/cpp/ql/test/include/utility.h b/cpp/ql/test/include/utility.h new file mode 100644 index 000000000000..24c8572886db --- /dev/null +++ b/cpp/ql/test/include/utility.h @@ -0,0 +1,13 @@ +#if !defined(CODEQL_UTILITY_H) +#define CODEQL_UTILITY_H + +#include "type_traits.h" + +namespace std { + template + typename remove_reference::type&& move(T&& src) { + return static_cast::type&&>(src); + } +} + +#endif 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 fc6c97aa2a63..db9a86fbb570 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 63d3b2c0f48f..fe7d8360403c 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 37a0dc3832a2..e6234ca17f77 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 d8b6b4e0e69a..fae4b06da5ad 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/smart-pointers-taint/test.cpp b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp index 0dbbd7a23843..bd24f0e72f24 100644 --- a/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp @@ -7,7 +7,7 @@ void test_unique_ptr_int() { std::unique_ptr p1(new int(source())); std::unique_ptr p2 = std::make_unique(source()); - sink(*p1); // $ MISSING: ast,ir + sink(*p1); // $ ir MISSING: ast sink(*p2); // $ ast ir=8:50 } @@ -21,7 +21,7 @@ void test_unique_ptr_struct() { std::unique_ptr p1(new A{source(), 0}); std::unique_ptr p2 = std::make_unique(source(), 0); - sink(p1->x); // $ MISSING: ast,ir + sink(p1->x); // $ ir MISSING: ast sink(p1->y); sink(p2->x); // $ MISSING: ast,ir sink(p2->y); @@ -31,7 +31,7 @@ void test_shared_ptr_int() { std::shared_ptr p1(new int(source())); std::shared_ptr p2 = std::make_shared(source()); - sink(*p1); // $ ast + sink(*p1); // $ ast ir sink(*p2); // $ ast ir=32:50 } @@ -39,7 +39,7 @@ void test_shared_ptr_struct() { std::shared_ptr p1(new A{source(), 0}); std::shared_ptr p2 = std::make_shared(source(), 0); - sink(p1->x); // $ MISSING: ast,ir + sink(p1->x); // $ ir MISSING: ast sink(p1->y); sink(p2->x); // $ MISSING: ast,ir sink(p2->y); 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 5b654ddb9ad8..ce939661c92d 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3223,49 +3223,67 @@ | 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:18:11:18:11 | ref arg p | TAINT | +| 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:30:11:30:11 | ref arg p | TAINT | +| 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:37:6:37:6 | ref arg p | TAINT | +| 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:45:6:45:6 | ref arg p | TAINT | +| 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 | | -| smart_pointer.cpp:52:12:52:14 | ref arg call to get | smart_pointer.cpp:52:10:52:10 | ref arg p | | +| 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 | | -| smart_pointer.cpp:57:12:57:14 | ref arg call to get | smart_pointer.cpp:57:10:57:10 | ref arg p | | +| 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 | @@ -3273,46 +3291,167 @@ | 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:76:45:76:45 | p | smart_pointer.cpp:77:3:77:3 | p | | -| smart_pointer.cpp:76:45:76:45 | p | smart_pointer.cpp:78:8:78:8 | p | | -| smart_pointer.cpp:76:45:76:45 | p | smart_pointer.cpp:79:8:79:8 | p | | -| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:81:3:81:3 | q | | -| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:82:8:82:8 | q | | -| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:83:8:83:8 | q | | -| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:84:8:84:8 | q | | -| smart_pointer.cpp:77:3:77:3 | p | smart_pointer.cpp:77:4:77:4 | call to operator-> | | -| smart_pointer.cpp:77:3:77:3 | ref arg p | smart_pointer.cpp:78:8:78:8 | p | | -| smart_pointer.cpp:77:3:77:3 | ref arg p | smart_pointer.cpp:79:8:79:8 | p | | -| smart_pointer.cpp:77:3:77:17 | ... = ... | smart_pointer.cpp:77:6:77:6 | x [post update] | | -| smart_pointer.cpp:77:3:77:17 | ... = ... | smart_pointer.cpp:78:11:78:11 | x | | -| smart_pointer.cpp:77:4:77:4 | call to operator-> [post update] | smart_pointer.cpp:77:3:77:3 | ref arg p | | -| smart_pointer.cpp:77:10:77:15 | call to source | smart_pointer.cpp:77:3:77:17 | ... = ... | | -| smart_pointer.cpp:78:8:78:8 | p | smart_pointer.cpp:78:9:78:9 | call to operator-> | | -| smart_pointer.cpp:78:8:78:8 | ref arg p | smart_pointer.cpp:79:8:79:8 | p | | -| smart_pointer.cpp:79:8:79:8 | p | smart_pointer.cpp:79:9:79:9 | call to operator-> | | -| smart_pointer.cpp:81:3:81:3 | q | smart_pointer.cpp:81:4:81:4 | call to operator-> | | -| smart_pointer.cpp:81:3:81:3 | ref arg q | smart_pointer.cpp:82:8:82:8 | q | | -| smart_pointer.cpp:81:3:81:3 | ref arg q | smart_pointer.cpp:83:8:83:8 | q | | -| smart_pointer.cpp:81:3:81:3 | ref arg q | smart_pointer.cpp:84:8:84:8 | q | | -| smart_pointer.cpp:81:3:81:20 | ... = ... | smart_pointer.cpp:81:9:81:9 | x [post update] | | -| smart_pointer.cpp:81:3:81:20 | ... = ... | smart_pointer.cpp:82:14:82:14 | x | | -| smart_pointer.cpp:81:4:81:4 | call to operator-> [post update] | smart_pointer.cpp:81:3:81:3 | ref arg q | | -| smart_pointer.cpp:81:13:81:18 | call to source | smart_pointer.cpp:81:3:81:20 | ... = ... | | -| smart_pointer.cpp:82:8:82:8 | q | smart_pointer.cpp:82:9:82:9 | call to operator-> | | -| smart_pointer.cpp:82:8:82:8 | ref arg q | smart_pointer.cpp:83:8:83:8 | q | | -| smart_pointer.cpp:82:8:82:8 | ref arg q | smart_pointer.cpp:84:8:84:8 | q | | -| smart_pointer.cpp:83:8:83:8 | q | smart_pointer.cpp:83:9:83:9 | call to operator-> | | -| smart_pointer.cpp:83:8:83:8 | ref arg q | smart_pointer.cpp:84:8:84:8 | q | | -| smart_pointer.cpp:84:8:84:8 | q | smart_pointer.cpp:84:9:84:9 | call to operator-> | | -| smart_pointer.cpp:87:17:87:18 | pa | smart_pointer.cpp:88:5:88:6 | pa | | -| smart_pointer.cpp:88:5:88:20 | ... = ... | smart_pointer.cpp:88:9:88:9 | x [post update] | | -| smart_pointer.cpp:88:13:88:18 | call to source | smart_pointer.cpp:88:5:88:20 | ... = ... | | -| smart_pointer.cpp:92:25:92:50 | call to unique_ptr | smart_pointer.cpp:93:11:93:11 | p | | -| smart_pointer.cpp:92:25:92:50 | call to unique_ptr | smart_pointer.cpp:94:8:94:8 | p | | -| smart_pointer.cpp:93:11:93:11 | p | smart_pointer.cpp:93:13:93:15 | call to get | | -| smart_pointer.cpp:93:11:93:11 | ref arg p | smart_pointer.cpp:94:8:94:8 | p | | -| smart_pointer.cpp:93:13:93:15 | ref arg call to get | smart_pointer.cpp:93:11:93:11 | ref arg p | | -| smart_pointer.cpp:94:8:94:8 | p | smart_pointer.cpp:94:9:94:9 | 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:3 | call to operator* [post update] | smart_pointer.cpp:71:4:71:6 | ref arg ptr | TAINT | +| 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:3 | call to operator* [post update] | smart_pointer.cpp:121:4:121:6 | ref arg ptr | TAINT | +| 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 | call to operator* [inner post update] | smart_pointer.cpp:128:14:128:15 | ref arg p2 | 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:128:14:128:15 | ref arg p2 | TAINT | +| 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:9:129:9 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | ref arg p2 | TAINT | +| 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:136:18:136:19 | ref arg p2 | TAINT | +| 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:9:137:9 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | ref arg p2 | TAINT | +| 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 | | @@ -3404,13 +3543,13 @@ | standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:120:2:120:3 | it | | | standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:121:7:121:8 | it | | | standalone_iterators.cpp:117:7:117:8 | it [post update] | standalone_iterators.cpp:122:7:122:8 | c1 | | -| standalone_iterators.cpp:118:2:118:3 | it | standalone_iterators.cpp:118:5:118:5 | call to operator+= | | +| standalone_iterators.cpp:118:2:118:3 | it | standalone_iterators.cpp:118:5:118:5 | call to operator+= | TAINT | | standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:119:7:119:8 | it | | | standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:120:2:120:3 | it | | | standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | | | standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:122:7:122:8 | c1 | | | standalone_iterators.cpp:118:8:118:8 | 1 | standalone_iterators.cpp:118:2:118:3 | ref arg it | TAINT | -| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | | +| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | TAINT | | standalone_iterators.cpp:120:2:120:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | | | standalone_iterators.cpp:120:8:120:13 | call to source | standalone_iterators.cpp:120:2:120:3 | ref arg it | TAINT | | stl.h:75:8:75:8 | Unknown literal | stl.h:75:8:75:8 | constructor init of field container | TAINT | @@ -3432,125 +3571,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 | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | 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: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 | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | | 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 | | @@ -4103,13 +4242,13 @@ | string.cpp:407:9:407:10 | i6 | string.cpp:407:8:407:8 | call to operator* | TAINT | | string.cpp:408:8:408:9 | i2 | string.cpp:408:3:408:9 | ... = ... | | | string.cpp:408:8:408:9 | i2 | string.cpp:409:10:409:11 | i7 | | -| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | | +| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | TAINT | | string.cpp:409:12:409:12 | call to operator+= | string.cpp:409:8:409:8 | call to operator* | TAINT | | string.cpp:409:12:409:12 | ref arg call to operator+= | string.cpp:409:10:409:11 | ref arg i7 | TAINT | | string.cpp:409:14:409:14 | 1 | string.cpp:409:10:409:11 | ref arg i7 | TAINT | | string.cpp:410:8:410:9 | i2 | string.cpp:410:3:410:9 | ... = ... | | | string.cpp:410:8:410:9 | i2 | string.cpp:411:10:411:11 | i8 | | -| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | | +| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | TAINT | | string.cpp:411:12:411:12 | call to operator-= | string.cpp:411:8:411:8 | call to operator* | TAINT | | string.cpp:411:12:411:12 | ref arg call to operator-= | string.cpp:411:10:411:11 | ref arg i8 | TAINT | | string.cpp:411:14:411:14 | 1 | string.cpp:411:10:411:11 | ref arg i8 | TAINT | @@ -4524,7 +4663,7 @@ | stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:39:7:39:9 | ss3 | | | stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:44:7:44:9 | ss3 | | | stringstream.cpp:33:7:33:9 | ss3 | stringstream.cpp:33:11:33:11 | call to operator<< | | -| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | | +| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | TAINT | | stringstream.cpp:33:11:33:11 | ref arg call to operator<< | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT | | stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT | | stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:11:33:11 | call to operator<< | TAINT | @@ -4533,7 +4672,7 @@ | stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:40:7:40:9 | ss4 | | | stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:45:7:45:9 | ss4 | | | stringstream.cpp:34:7:34:9 | ss4 | stringstream.cpp:34:11:34:11 | call to operator<< | | -| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | | +| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | TAINT | | stringstream.cpp:34:11:34:11 | ref arg call to operator<< | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT | | stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT | | stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:11:34:11 | call to operator<< | TAINT | @@ -4814,7 +4953,7 @@ | stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | | stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:11:147:11 | call to operator>> | | | stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:14:147:15 | ref arg s3 | TAINT | -| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | | +| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | TAINT | | stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:20:147:21 | ref arg s4 | TAINT | | stringstream.cpp:147:11:147:11 | ref arg call to operator>> | stringstream.cpp:147:7:147:9 | ref arg ss2 | TAINT | | stringstream.cpp:147:14:147:15 | ref arg s3 | stringstream.cpp:150:7:150:8 | s3 | | @@ -4846,7 +4985,7 @@ | stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | | stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:11:155:11 | call to operator>> | | | stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:14:155:15 | ref arg b3 | TAINT | -| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | | +| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | TAINT | | stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:20:155:21 | ref arg b4 | TAINT | | stringstream.cpp:155:11:155:11 | ref arg call to operator>> | stringstream.cpp:155:7:155:9 | ref arg ss2 | TAINT | | stringstream.cpp:155:14:155:15 | ref arg b3 | stringstream.cpp:158:7:158:8 | b3 | | @@ -5168,7 +5307,7 @@ | stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:7:245:13 | call to getline | | | stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:20:245:21 | ref arg s6 | TAINT | | stringstream.cpp:245:20:245:21 | ref arg s6 | stringstream.cpp:248:7:248:8 | s6 | | -| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | | +| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | TAINT | | stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:33:250:34 | ref arg s8 | TAINT | | stringstream.cpp:250:15:250:21 | ref arg call to getline | stringstream.cpp:250:23:250:25 | ref arg ss2 | TAINT | | stringstream.cpp:250:23:250:25 | ss2 | stringstream.cpp:250:15:250:21 | call to getline | | @@ -5372,7 +5511,7 @@ | swap1.cpp:100:9:100:17 | ref arg call to move | swap1.cpp:103:10:103:10 | x | | | swap1.cpp:100:19:100:19 | x | swap1.cpp:100:5:100:5 | ref arg y | TAINT | | swap1.cpp:100:19:100:19 | x | swap1.cpp:100:7:100:7 | call to operator= | TAINT | -| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | | +| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | TAINT | | swap1.cpp:108:23:108:31 | move_from | swap1.cpp:109:5:109:13 | move_from | | | swap1.cpp:108:23:108:31 | move_from | swap1.cpp:111:10:111:18 | move_from | | | swap1.cpp:108:23:108:31 | move_from | swap1.cpp:113:41:113:49 | move_from | | @@ -5385,7 +5524,7 @@ | swap1.cpp:113:31:113:39 | call to move | swap1.cpp:113:31:113:51 | call to Class | TAINT | | swap1.cpp:113:31:113:39 | ref arg call to move | swap1.cpp:113:41:113:49 | move_from [inner post update] | | | swap1.cpp:113:31:113:51 | call to Class | swap1.cpp:115:10:115:16 | move_to | | -| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | | +| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | TAINT | | swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:51 | call to Class | | | swap1.cpp:120:23:120:23 | x | swap1.cpp:122:5:122:5 | x | | | swap1.cpp:120:23:120:23 | x | swap1.cpp:124:10:124:10 | x | | @@ -5419,7 +5558,7 @@ | swap1.cpp:142:5:142:5 | ref arg y | swap1.cpp:144:10:144:10 | y | | | swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:142:29:142:29 | x [inner post update] | | | swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:145:10:145:10 | x | | -| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | | +| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | TAINT | | swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | | | swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | | | swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | | @@ -5551,7 +5690,7 @@ | swap2.cpp:100:9:100:17 | ref arg call to move | swap2.cpp:103:10:103:10 | x | | | swap2.cpp:100:19:100:19 | x | swap2.cpp:100:5:100:5 | ref arg y | TAINT | | swap2.cpp:100:19:100:19 | x | swap2.cpp:100:7:100:7 | call to operator= | TAINT | -| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | | +| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | TAINT | | swap2.cpp:108:23:108:31 | move_from | swap2.cpp:109:5:109:13 | move_from | | | swap2.cpp:108:23:108:31 | move_from | swap2.cpp:111:10:111:18 | move_from | | | swap2.cpp:108:23:108:31 | move_from | swap2.cpp:113:41:113:49 | move_from | | @@ -5564,7 +5703,7 @@ | swap2.cpp:113:31:113:39 | call to move | swap2.cpp:113:31:113:51 | call to Class | TAINT | | swap2.cpp:113:31:113:39 | ref arg call to move | swap2.cpp:113:41:113:49 | move_from [inner post update] | | | swap2.cpp:113:31:113:51 | call to Class | swap2.cpp:115:10:115:16 | move_to | | -| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | | +| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | TAINT | | swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:51 | call to Class | | | swap2.cpp:120:23:120:23 | x | swap2.cpp:122:5:122:5 | x | | | swap2.cpp:120:23:120:23 | x | swap2.cpp:124:10:124:10 | x | | @@ -5598,7 +5737,7 @@ | swap2.cpp:142:5:142:5 | ref arg y | swap2.cpp:144:10:144:10 | y | | | swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:142:29:142:29 | x [inner post update] | | | swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:145:10:145:10 | x | | -| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | | +| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | TAINT | | taint.cpp:4:27:4:33 | source1 | taint.cpp:6:13:6:19 | source1 | | | taint.cpp:4:40:4:45 | clean1 | taint.cpp:5:8:5:13 | clean1 | | | taint.cpp:4:40:4:45 | clean1 | taint.cpp:6:3:6:8 | clean1 | | @@ -7822,7 +7961,7 @@ | vector.cpp:527:9:527:10 | ref arg it | vector.cpp:529:9:529:10 | it | | | vector.cpp:527:9:527:10 | ref arg it | vector.cpp:530:3:530:4 | it | | | vector.cpp:527:9:527:10 | ref arg it | vector.cpp:531:9:531:10 | it | | -| vector.cpp:528:3:528:4 | it | vector.cpp:528:6:528:6 | call to operator+= | | +| vector.cpp:528:3:528:4 | it | vector.cpp:528:6:528:6 | call to operator+= | TAINT | | vector.cpp:528:3:528:4 | ref arg it | vector.cpp:529:9:529:10 | it | | | vector.cpp:528:3:528:4 | ref arg it | vector.cpp:530:3:530:4 | it | | | vector.cpp:528:3:528:4 | ref arg it | vector.cpp:531:9:531:10 | it | | @@ -7830,7 +7969,7 @@ | vector.cpp:529:9:529:10 | it | vector.cpp:529:8:529:8 | call to operator* | TAINT | | vector.cpp:529:9:529:10 | ref arg it | vector.cpp:530:3:530:4 | it | | | vector.cpp:529:9:529:10 | ref arg it | vector.cpp:531:9:531:10 | it | | -| vector.cpp:530:3:530:4 | it | vector.cpp:530:6:530:6 | call to operator+= | | +| vector.cpp:530:3:530:4 | it | vector.cpp:530:6:530:6 | call to operator+= | TAINT | | vector.cpp:530:3:530:4 | ref arg it | vector.cpp:531:9:531:10 | it | | | vector.cpp:530:9:530:14 | call to source | vector.cpp:530:3:530:4 | ref arg it | TAINT | | vector.cpp:531:9:531:10 | it | vector.cpp:531:8:531:8 | call to operator* | TAINT | 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 cf43c3a6c1ce..28e44779009b 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 @@ -9,7 +9,7 @@ template void sink(std::unique_ptr&); void test_make_shared() { std::shared_ptr p = std::make_shared(source()); - sink(*p); // $ ast MISSING: ir + sink(*p); // $ ast,ir sink(p); // $ ast,ir } @@ -21,7 +21,7 @@ void test_make_shared_array() { void test_make_unique() { std::unique_ptr p = std::make_unique(source()); - sink(*p); // $ ast MISSING: ir + sink(*p); // $ ast,ir sink(p); // $ ast,ir } @@ -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() { @@ -67,6 +67,16 @@ void test_shared_field_member() { 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; @@ -91,5 +101,38 @@ void taint_x(A* pa) { void reverse_taint_smart_pointer() { std::unique_ptr p = std::unique_ptr(new A); taint_x(p.get()); - sink(p->x); // $ ast MISSING: ir + sink(p->x); // $ ast,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 09e77c5a3b67..44550c3df768 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 a7dac56cc96a..e008e2de6596 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/points_to/points_to.ql b/cpp/ql/test/library-tests/ir/points_to/points_to.ql index 89d1e31e119b..da090c5552ee 100644 --- a/cpp/ql/test/library-tests/ir/points_to/points_to.ql +++ b/cpp/ql/test/library-tests/ir/points_to/points_to.ql @@ -5,7 +5,16 @@ private import semmle.code.cpp.ir.internal.IntegerConstant as Ints private predicate ignoreAllocation(string name) { name = "i" or name = "p" or - name = "q" + name = "q" or + name = "s" or + name = "t" or + name = "?{AllAliased}" +} + +private predicate ignoreFile(File file) { + // Ignore standard headers. + file.getBaseName() = ["memory.h", "type_traits.h", "utility.h"] or + not file.fromSource() } module Raw { @@ -29,7 +38,8 @@ module Raw { not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and value = memLocation.toString() and element = instr.toString() and - location = instr.getLocation() + location = instr.getLocation() and + not ignoreFile(location.getFile()) ) } } @@ -52,13 +62,14 @@ module UnaliasedSSA { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(Instruction instr, MemoryLocation memLocation | memLocation = getAMemoryAccess(instr) and - not memLocation instanceof AliasedVirtualVariable and + not memLocation.getVirtualVariable() instanceof AliasedVirtualVariable and not memLocation instanceof AllNonLocalMemory and tag = "ussa" and not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and value = memLocation.toString() and element = instr.toString() and - location = instr.getLocation() + location = instr.getLocation() and + not ignoreFile(location.getFile()) ) } } diff --git a/cpp/ql/test/library-tests/ir/points_to/smart_pointer.cpp b/cpp/ql/test/library-tests/ir/points_to/smart_pointer.cpp new file mode 100644 index 000000000000..de4cbc96e5f1 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/points_to/smart_pointer.cpp @@ -0,0 +1,33 @@ +#include "../../../include/memory.h" +#include "../../../include/utility.h" + +using std::shared_ptr; +using std::unique_ptr; + +struct S { + int x; +}; + +void unique_ptr_init(S s) { + unique_ptr p(new S); //$ussa=dynamic{1} + int i = (*p).x; //$ussa=dynamic{1}[0..4) + *p = s; //$ussa=dynamic{1}[0..4) + unique_ptr q = std::move(p); + *(q.get()) = s; //$ussa=dynamic{1}[0..4) + shared_ptr t(std::move(q)); + t->x = 5; //$ussa=dynamic{1}[0..4) + *t = s; //$ussa=dynamic{1}[0..4) + *(t.get()) = s; //$ussa=dynamic{1}[0..4) +} + +void shared_ptr_init(S s) { + shared_ptr p(new S); //$ussa=dynamic{1} + int i = (*p).x; //$ussa=dynamic{1}[0..4) + *p = s; //$ussa=dynamic{1}[0..4) + shared_ptr q = std::move(p); + *(q.get()) = s; //$ussa=dynamic{1}[0..4) + shared_ptr t(q); + t->x = 5; //$ussa=dynamic{1}[0..4) + *t = s; //$ussa=dynamic{1}[0..4) + *(t.get()) = s; //$ussa=dynamic{1}[0..4) +} 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 c4019a38251e..76de50dd792b 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 a101e638cd08..ab79d20214a8 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 8b6cf17dbbbb..0b257db98a43 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 8b6cf17dbbbb..0b257db98a43 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 03b202817cae..1ce2558a34f6 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 a46371f36b6c..cbaf5d630795 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 @@ -3,6 +3,4 @@ | test.c:50:3:50:5 | sc3 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:49:9:49:16 | 127 | Extreme value | | 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 8c40d984ee0a..8760641c8e2d 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 @@ -72,8 +72,8 @@ void test_negatives() { signed char sc1, sc2, sc3, sc4, sc5, sc6, sc7, sc8; sc1 = CHAR_MAX; - sc1 += 0; // GOOD [FALSE POSITIVE] - sc1 += -1; // GOOD [FALSE POSITIVE] + sc1 += 0; // GOOD + 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 8bb25025b868..bdf00e0a5df5 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 7b9f2e788811..99ad4c8f963f 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.3 [/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 @@ public void TestDotnetVersionWindows() 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 8787406c104e..a456c9407db6 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -179,53 +179,20 @@ private static BuildScript DownloadDotNetVersion(Autobuilder builder, string pat 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' - -$arguments = @{ - Channel = 'release' - Version = $Version - InstallDir = $InstallDir -} - -$ScriptBlock = [scriptblock]::create("".{$($Script)} $(&{$args} @arguments)"") + var psCommand = $"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version {version} -InstallDir {path}"; -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); - - var removeScript = new CommandBuilder(builder.Actions). - RunCommand("del"). - Argument(psScriptFile); + Argument("-Command"). + Argument("\"" + psCommand + "\""). + Script; - return install.Script & BuildScript.Try(removeScript.Script); + return GetInstall("pwsh") | GetInstall("powershell"); } else { 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 000000000000..a2f957ca9230 --- /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/change-notes/2021-04-22-console-read-local-source.md b/csharp/change-notes/2021-04-22-console-read-local-source.md new file mode 100644 index 000000000000..9fbb5f2ff32a --- /dev/null +++ b/csharp/change-notes/2021-04-22-console-read-local-source.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* `System.Console.Read` methods have been added as data flow sources of local user input. \ No newline at end of file diff --git a/csharp/change-notes/2021-04-23-model-error-extraction.md b/csharp/change-notes/2021-04-23-model-error-extraction.md new file mode 100644 index 000000000000..7ac878442984 --- /dev/null +++ b/csharp/change-notes/2021-04-23-model-error-extraction.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Program model errors are now extracted in standalone extraction. \ No newline at end of file diff --git a/csharp/change-notes/2021-04-26-string-builder-summaries.md b/csharp/change-notes/2021-04-26-string-builder-summaries.md new file mode 100644 index 000000000000..285f40faf398 --- /dev/null +++ b/csharp/change-notes/2021-04-26-string-builder-summaries.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Data-flow modelling of `StringBuilder` objects has been improved. \ 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 6d36dded8019..103ea8cacdab 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -193,6 +193,16 @@ private static bool ContainsPattern(SyntaxNode node) => 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); } diff --git a/csharp/extractor/Semmle.Extraction/Context.cs b/csharp/extractor/Semmle.Extraction/Context.cs index c0580db96a7d..a60d82c6952a 100644 --- a/csharp/extractor/Semmle.Extraction/Context.cs +++ b/csharp/extractor/Semmle.Extraction/Context.cs @@ -376,6 +376,19 @@ private void ExtractionError(Message msg) Extractor.Message(msg); } + private void ExtractionError(InternalError error) + { + ExtractionError(new Message(error.Message, error.EntityText, CreateLocation(error.Location), error.StackTrace, Severity.Error)); + } + + private void ReportError(InternalError error) + { + if (!Extractor.Standalone) + throw error; + + ExtractionError(error); + } + /// /// Signal an error in the program model. /// @@ -383,8 +396,7 @@ private void ExtractionError(Message msg) /// The error message. public void ModelError(SyntaxNode node, string msg) { - if (!Extractor.Standalone) - throw new InternalError(node, msg); + ReportError(new InternalError(node, msg)); } /// @@ -394,8 +406,7 @@ public void ModelError(SyntaxNode node, string msg) /// The error message. public void ModelError(ISymbol symbol, string msg) { - if (!Extractor.Standalone) - throw new InternalError(symbol, msg); + ReportError(new InternalError(symbol, msg)); } /// @@ -404,8 +415,7 @@ public void ModelError(ISymbol symbol, string msg) /// The error message. public void ModelError(string msg) { - if (!Extractor.Standalone) - throw new InternalError(msg); + ReportError(new InternalError(msg)); } /// diff --git a/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql b/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql index 7469cbc59e72..abb962449b9f 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 000000000000..d5378a44f0de --- /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/Diagnostics/DiagnosticExtractionErrors.ql b/csharp/ql/src/Diagnostics/DiagnosticExtractionErrors.ql new file mode 100644 index 000000000000..23943d8491fb --- /dev/null +++ b/csharp/ql/src/Diagnostics/DiagnosticExtractionErrors.ql @@ -0,0 +1,63 @@ +/** + * @name Extraction errors + * @description List all errors reported by the extractor or the compiler. Extractor errors are + * limited to those files where there are no compilation errors. + * @kind diagnostic + * @id cs/diagnostics/extraction-errors + */ + +import csharp +import semmle.code.csharp.commons.Diagnostics + +private newtype TDiagnosticError = + TCompilerError(CompilerError c) or + TExtractorError(ExtractorError e) + +abstract private class DiagnosticError extends TDiagnosticError { + abstract string getMessage(); + + abstract string toString(); + + abstract Location getLocation(); + + string getLocationMessage() { + if getLocation().getFile().fromSource() + then result = " in " + getLocation().getFile() + else result = "" + } +} + +private class DiagnosticCompilerError extends DiagnosticError { + CompilerError c; + + DiagnosticCompilerError() { this = TCompilerError(c) } + + override string getMessage() { + result = "Compiler error" + this.getLocationMessage() + ": " + c.getMessage() + } + + override string toString() { result = c.toString() } + + override Location getLocation() { result = c.getLocation() } +} + +private class DiagnosticExtractorError extends DiagnosticError { + ExtractorError e; + + DiagnosticExtractorError() { + this = TExtractorError(e) and + not exists(CompilerError ce | ce.getLocation().getFile() = e.getLocation().getFile()) + } + + override string getMessage() { + result = + "Unexpected " + e.getOrigin() + " error" + this.getLocationMessage() + ": " + e.getText() + } + + override string toString() { result = e.toString() } + + override Location getLocation() { result = e.getLocation() } +} + +from DiagnosticError error +select error.getMessage(), 2 diff --git a/csharp/ql/src/Diagnostics/DiagnosticNoExtractionErrors.ql b/csharp/ql/src/Diagnostics/DiagnosticNoExtractionErrors.ql new file mode 100644 index 000000000000..cb86e293efcb --- /dev/null +++ b/csharp/ql/src/Diagnostics/DiagnosticNoExtractionErrors.ql @@ -0,0 +1,17 @@ +/** + * @name Successfully extracted files + * @description A list of all files in the source code directory that were extracted + * without encountering an extraction or compiler error in the file. + * @kind diagnostic + * @id cs/diagnostics/successfully-extracted-files + */ + +import csharp +import semmle.code.csharp.commons.Diagnostics + +from File file +where + file.fromSource() and + not exists(ExtractorError e | e.getLocation().getFile() = file) and + not exists(CompilerError e | e.getLocation().getFile() = file) +select file, "" diff --git a/csharp/ql/src/Language Abuse/ForeachCapture.qhelp b/csharp/ql/src/Language Abuse/ForeachCapture.qhelp index da966c4d967e..516ed43cbf40 100644 --- a/csharp/ql/src/Language Abuse/ForeachCapture.qhelp +++ b/csharp/ql/src/Language Abuse/ForeachCapture.qhelp @@ -29,7 +29,7 @@ However on older compilers, the actual output is 10 10 10 10 10 10 10 10 1 -
  • Eric Lippert's Blog: Closing over the loop variable considered harmful.
  • +
  • Eric Lippert's Blog: Closing over the loop variable considered harmful.
  • diff --git a/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.qhelp b/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.qhelp index ee484d49d46c..a2000deb2d59 100644 --- a/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.qhelp +++ b/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.qhelp @@ -27,7 +27,7 @@ The Japanese Calendar's Y2K Moment.
  • - Era Handling for the Japanese Calendar. + Era Handling for the Japanese Calendar.
  • List of Japanese Eras (Wikipedia) diff --git a/csharp/ql/src/Likely Bugs/ReferenceEqualsOnValueTypes.qhelp b/csharp/ql/src/Likely Bugs/ReferenceEqualsOnValueTypes.qhelp index ede1d57f1cfe..96feb557d3b1 100644 --- a/csharp/ql/src/Likely Bugs/ReferenceEqualsOnValueTypes.qhelp +++ b/csharp/ql/src/Likely Bugs/ReferenceEqualsOnValueTypes.qhelp @@ -25,7 +25,7 @@ really be compared using i == j.

  • MSDN: Object.ReferenceEquals Method.
  • -
  • The Way I See It: Object.ReferenceEquals(ValueVar, ValueVar) will always return false.
  • +
  • The Way I See It: Object.ReferenceEquals(ValueVar, ValueVar) will always return false.
  • diff --git a/csharp/ql/src/Metrics/Files/FNumberOfTests.qhelp b/csharp/ql/src/Metrics/Files/FNumberOfTests.qhelp index 1ebd170eaad3..cddb5383bac9 100644 --- a/csharp/ql/src/Metrics/Files/FNumberOfTests.qhelp +++ b/csharp/ql/src/Metrics/Files/FNumberOfTests.qhelp @@ -53,7 +53,7 @@ Microsoft Visual Studio Unit Testing Framework: documentation at MSDN.
  • - xUnit.net: official website at https://xunit.github.io/. + xUnit.net: official website at https://xunit.net/.
  • diff --git a/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql b/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql new file mode 100644 index 000000000000..9156a5e4a7f2 --- /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/Security Features/CWE-011/ASPNetDebug.qhelp b/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.qhelp index 5a65094f39c1..5caf33d0cc90 100644 --- a/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.qhelp +++ b/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.qhelp @@ -42,7 +42,7 @@ To fix this problem, the 'debug' flag should be set to false, or re
  • MSDN: -Why debug=false in ASP.NET applications in production environment. +Why debug=false in ASP.NET applications in production environment.
  • MSDN: diff --git a/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.qhelp b/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.qhelp index fc508827c9f7..f36d8a3204e0 100644 --- a/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.qhelp +++ b/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.qhelp @@ -36,7 +36,4 @@ use the error log, but remote users will not see the information.

    - -
  • OWASP: Information Leak.
  • - diff --git a/csharp/ql/src/csharp.qll b/csharp/ql/src/csharp.qll index 3c821c9a4433..dc187fc8d923 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 2004ad0d2187..c1b456f4dbf2 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/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index af6f3d819824..13c384017379 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -1645,6 +1645,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll index 81d7b71b9524..04e05dc98140 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll @@ -57,7 +57,8 @@ private Element getRealParent(Expr expr) { result = expr.getParent() } */ predicate isIRConstant(Expr expr) { exists(expr.getValue()) } -// Pulled out to work around QL-796 +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. private predicate isOrphan(Expr expr) { not exists(getRealParent(expr)) } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index af6f3d819824..13c384017379 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -1645,6 +1645,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 19fb0490f808..e26d61b9792c 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -4,6 +4,71 @@ private import AliasAnalysisImports private class IntValue = Ints::IntValue; +/** + * If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side + * effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`. + */ +private CallInstruction getPrimaryCall(Instruction instr) { + result = instr + or + result = instr.(SideEffectInstruction).getPrimaryInstruction() +} + +/** + * Holds if `operand` serves as an input argument (or indirection) to `call`, in the position + * specified by `input`. + */ +private predicate isCallInput( + CallInstruction call, Operand operand, AliasModels::FunctionInput input +) { + call = getPrimaryCall(operand.getUse()) and + ( + exists(int index | + input.isParameterOrQualifierAddress(index) and + operand = call.getArgumentOperand(index) + ) + or + exists(int index, ReadSideEffectInstruction read | + input.isParameterDerefOrQualifierObject(index) and + read = call.getAParameterSideEffect(index) and + operand = read.getSideEffectOperand() + ) + ) +} + +/** + * Holds if `instr` serves as a return value or output argument indirection for `call`, in the + * position specified by `output`. + */ +private predicate isCallOutput( + CallInstruction call, Instruction instr, AliasModels::FunctionOutput output +) { + call = getPrimaryCall(instr) and + ( + output.isReturnValue() and instr = call + or + exists(int index, WriteSideEffectInstruction write | + output.isParameterDerefOrQualifierObject(index) and + write = call.getAParameterSideEffect(index) and + instr = write + ) + ) +} + +/** + * Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled + * address flow through a function call. + */ +private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) { + exists( + CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output + | + call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and + isCallInput(call, operand, input) and + isCallOutput(call, resultInstr, output) + ) +} + /** * Holds if the operand `tag` of instruction `instr` is used in a way that does * not result in any address held in that operand from escaping beyond the @@ -34,7 +99,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { private predicate operandEscapesDomain(Operand operand) { not operandIsConsumedWithoutEscaping(operand) and - not operandIsPropagated(operand, _) and + not operandIsPropagated(operand, _, _) and not isArgumentForParameter(_, operand, _) and not isOnlyEscapesViaReturnArgument(operand) and not operand.getUse() instanceof ReturnValueInstruction and @@ -69,67 +134,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) { } /** - * Holds if any address held in operand `tag` of instruction `instr` is - * propagated to the result of `instr`, offset by the number of bits in - * `bitOffset`. If the address is propagated, but the offset is not known to be - * a constant, then `bitOffset` is unknown. + * Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by + * the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to + * be a constant, then `bitOffset` is `unknown()`. */ -private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { - exists(Instruction instr | - instr = operand.getUse() and - ( - // Converting to a non-virtual base class adds the offset of the base class. - exists(ConvertToNonVirtualBaseInstruction convert | - convert = instr and - bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) - ) - or - // Conversion using dynamic_cast results in an unknown offset - instr instanceof CheckedConvertOrNullInstruction and - bitOffset = Ints::unknown() - or - // Converting to a derived class subtracts the offset of the base class. - exists(ConvertToDerivedInstruction convert | - convert = instr and - bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) - ) - or - // Converting to a virtual base class adds an unknown offset. - instr instanceof ConvertToVirtualBaseInstruction and - bitOffset = Ints::unknown() - or - // Conversion to another pointer type propagates the source address. - exists(ConvertInstruction convert, IRType resultType | - convert = instr and - resultType = convert.getResultIRType() and - resultType instanceof IRAddressType and - bitOffset = 0 - ) - or - // Adding an integer to or subtracting an integer from a pointer propagates - // the address with an offset. - exists(PointerOffsetInstruction ptrOffset | - ptrOffset = instr and - operand = ptrOffset.getLeftOperand() and - bitOffset = getPointerBitOffset(ptrOffset) - ) - or - // Computing a field address from a pointer propagates the address plus the - // offset of the field. - bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) - or - // A copy propagates the source value. - operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 - or - // Some functions are known to propagate an argument - isAlwaysReturnedArgument(operand) and bitOffset = 0 +private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) { + // Some functions are known to propagate an argument + hasAddressFlowThroughCall(operand, instr) and + bitOffset = 0 + or + instr = operand.getUse() and + ( + // Converting to a non-virtual base class adds the offset of the base class. + exists(ConvertToNonVirtualBaseInstruction convert | + convert = instr and + bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) + ) + or + // Conversion using dynamic_cast results in an unknown offset + instr instanceof CheckedConvertOrNullInstruction and + bitOffset = Ints::unknown() + or + // Converting to a derived class subtracts the offset of the base class. + exists(ConvertToDerivedInstruction convert | + convert = instr and + bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) + ) + or + // Converting to a virtual base class adds an unknown offset. + instr instanceof ConvertToVirtualBaseInstruction and + bitOffset = Ints::unknown() + or + // Conversion to another pointer type propagates the source address. + exists(ConvertInstruction convert, IRType resultType | + convert = instr and + resultType = convert.getResultIRType() and + resultType instanceof IRAddressType and + bitOffset = 0 ) + or + // Adding an integer to or subtracting an integer from a pointer propagates + // the address with an offset. + exists(PointerOffsetInstruction ptrOffset | + ptrOffset = instr and + operand = ptrOffset.getLeftOperand() and + bitOffset = getPointerBitOffset(ptrOffset) + ) + or + // Computing a field address from a pointer propagates the address plus the + // offset of the field. + bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) + or + // A copy propagates the source value. + operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 ) } private predicate operandEscapesNonReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -151,9 +216,11 @@ private predicate operandEscapesNonReturn(Operand operand) { } private predicate operandMayReachReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and - resultMayReachReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and + resultMayReachReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -173,9 +240,9 @@ private predicate operandMayReachReturn(Operand operand) { private predicate operandReturned(Operand operand, IntValue bitOffset) { // The address is propagated to the result of the instruction, and that result itself is returned - exists(IntValue bitOffset1, IntValue bitOffset2 | - operandIsPropagated(operand, bitOffset1) and - resultReturned(operand.getUse(), bitOffset2) and + exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 | + operandIsPropagated(operand, bitOffset1, instr) and + resultReturned(instr, bitOffset2) and bitOffset = Ints::add(bitOffset1, bitOffset2) ) or @@ -214,13 +281,6 @@ private predicate isArgumentForParameter( ) } -private predicate isAlwaysReturnedArgument(Operand operand) { - exists(AliasModels::AliasFunction f | - f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) - ) -} - private predicate isOnlyEscapesViaReturnArgument(Operand operand) { exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and @@ -270,12 +330,15 @@ predicate allocationEscapes(Configuration::Allocation allocation) { /** * Equivalent to `operandIsPropagated()`, but includes interprocedural propagation. */ -private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) { - operandIsPropagated(operand, bitOffset) +private predicate operandIsPropagatedIncludingByCall( + Operand operand, IntValue bitOffset, Instruction instr +) { + operandIsPropagated(operand, bitOffset, instr) or exists(CallInstruction call, Instruction init | isArgumentForParameter(call, operand, init) and - resultReturned(init, bitOffset) + resultReturned(init, bitOffset) and + instr = call ) } @@ -292,8 +355,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, // We already have an offset from `middle`. hasBaseAndOffset(addrOperand, middle, previousBitOffset) and // `middle` is propagated from `base`. - middleOperand = middle.getAnOperand() and - operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and + operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and base = middleOperand.getDef() and bitOffset = Ints::add(previousBitOffset, additionalBitOffset) ) diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll index 7992aa9ed146..e0bf271dcc72 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll @@ -3,6 +3,276 @@ import experimental.ir.implementation.IRConfiguration import experimental.ir.internal.IntegerConstant as Ints module AliasModels { + class ParameterIndex = int; + + /** + * An input to a function. This can be: + * - The value of one of the function's parameters + * - The value pointed to by one of function's pointer or reference parameters + * - The value of the function's `this` pointer + * - The value pointed to by the function's `this` pointer + */ + abstract class FunctionInput extends string { + FunctionInput() { none() } + + /** + * Holds if this is the input value of the parameter with index `index`. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - `isParameter(0)` holds for the `FunctionInput` that represents the value of `n` (with type + * `int`) on entry to the function. + * - `isParameter(1)` holds for the `FunctionInput` that represents the value of `p` (with type + * `char*`) on entry to the function. + * - `isParameter(2)` holds for the `FunctionInput` that represents the "value" of the reference + * `r` (with type `float&`) on entry to the function, _not_ the value of the referred-to + * `float`. + */ + predicate isParameter(ParameterIndex index) { none() } + + /** + * Holds if this is the input value of the parameter with index `index`. + * DEPRECATED: Use `isParameter(index)` instead. + */ + deprecated final predicate isInParameter(ParameterIndex index) { isParameter(index) } + + /** + * Holds if this is the input value pointed to by a pointer parameter to a function, or the input + * value referred to by a reference parameter to a function, where the parameter has index + * `index`. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - `isParameterDeref(1)` holds for the `FunctionInput` that represents the value of `*p` (with + * type `char`) on entry to the function. + * - `isParameterDeref(2)` holds for the `FunctionInput` that represents the value of `r` (with type + * `float`) on entry to the function. + * - There is no `FunctionInput` for which `isParameterDeref(0)` holds, because `n` is neither a + * pointer nor a reference. + */ + predicate isParameterDeref(ParameterIndex index) { none() } + + /** + * Holds if this is the input value pointed to by a pointer parameter to a function, or the input + * value referred to by a reference parameter to a function, where the parameter has index + * `index`. + * DEPRECATED: Use `isParameterDeref(index)` instead. + */ + deprecated final predicate isInParameterPointer(ParameterIndex index) { + isParameterDeref(index) + } + + /** + * Holds if this is the input value pointed to by the `this` pointer of an instance member + * function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r) const; + * }; + * ``` + * - `isQualifierObject()` holds for the `FunctionInput` that represents the value of `*this` + * (with type `C const`) on entry to the function. + */ + predicate isQualifierObject() { none() } + + /** + * Holds if this is the input value pointed to by the `this` pointer of an instance member + * function. + * DEPRECATED: Use `isQualifierObject()` instead. + */ + deprecated final predicate isInQualifier() { isQualifierObject() } + + /** + * Holds if this is the input value of the `this` pointer of an instance member function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r) const; + * }; + * ``` + * - `isQualifierAddress()` holds for the `FunctionInput` that represents the value of `this` + * (with type `C const *`) on entry to the function. + */ + predicate isQualifierAddress() { none() } + + /** + * Holds if `i >= 0` and `isParameter(i)` holds for this value, or + * if `i = -1` and `isQualifierAddress()` holds for this value. + */ + final predicate isParameterOrQualifierAddress(ParameterIndex i) { + i >= 0 and this.isParameter(i) + or + i = -1 and this.isQualifierAddress() + } + + /** + * Holds if this is the input value pointed to by the return value of a + * function, if the function returns a pointer, or the input value referred + * to by the return value of a function, if the function returns a reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `isReturnValueDeref()` holds for the `FunctionInput` that represents the + * value of `*getPointer()` (with type `char`). + * - `isReturnValueDeref()` holds for the `FunctionInput` that represents the + * value of `getReference()` (with type `float`). + * - There is no `FunctionInput` of `getInt()` for which + * `isReturnValueDeref()` holds because the return type of `getInt()` is + * neither a pointer nor a reference. + * + * Note that data flows in through function return values are relatively + * rare, but they do occur when a function returns a reference to itself, + * part of itself, or one of its other inputs. + */ + predicate isReturnValueDeref() { none() } + + /** + * Holds if `i >= 0` and `isParameterDeref(i)` holds for this value, or + * if `i = -1` and `isQualifierObject()` holds for this value. + */ + final predicate isParameterDerefOrQualifierObject(ParameterIndex i) { + i >= 0 and this.isParameterDeref(i) + or + i = -1 and this.isQualifierObject() + } + } + + /** + * An output from a function. This can be: + * - The value pointed to by one of function's pointer or reference parameters + * - The value pointed to by the function's `this` pointer + * - The function's return value + * - The value pointed to by the function's return value, if the return value is a pointer or + * reference + */ + abstract class FunctionOutput extends string { + FunctionOutput() { none() } + + /** + * Holds if this is the output value pointed to by a pointer parameter to a function, or the + * output value referred to by a reference parameter to a function, where the parameter has + * index `index`. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - `isParameterDeref(1)` holds for the `FunctionOutput` that represents the value of `*p` (with + * type `char`) on return from the function. + * - `isParameterDeref(2)` holds for the `FunctionOutput` that represents the value of `r` (with + * type `float`) on return from the function. + * - There is no `FunctionOutput` for which `isParameterDeref(0)` holds, because `n` is neither a + * pointer nor a reference. + */ + predicate isParameterDeref(ParameterIndex i) { none() } + + /** + * Holds if this is the output value pointed to by a pointer parameter to a function, or the + * output value referred to by a reference parameter to a function, where the parameter has + * index `index`. + * DEPRECATED: Use `isParameterDeref(index)` instead. + */ + deprecated final predicate isOutParameterPointer(ParameterIndex index) { + isParameterDeref(index) + } + + /** + * Holds if this is the output value pointed to by the `this` pointer of an instance member + * function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r); + * }; + * ``` + * - `isQualifierObject()` holds for the `FunctionOutput` that represents the value of `*this` + * (with type `C`) on return from the function. + */ + predicate isQualifierObject() { none() } + + /** + * Holds if this is the output value pointed to by the `this` pointer of an instance member + * function. + * DEPRECATED: Use `isQualifierObject()` instead. + */ + deprecated final predicate isOutQualifier() { isQualifierObject() } + + /** + * Holds if this is the value returned by a function. + * + * Example: + * ``` + * int getInt(); + * char* getPointer(); + * float& getReference(); + * ``` + * - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by + * `getInt()` (with type `int`). + * - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by + * `getPointer()` (with type `char*`). + * - `isReturnValue()` holds for the `FunctionOutput` that represents the "value" of the reference + * returned by `getReference()` (with type `float&`), _not_ the value of the referred-to + * `float`. + */ + predicate isReturnValue() { none() } + + /** + * Holds if this is the value returned by a function. + * DEPRECATED: Use `isReturnValue()` instead. + */ + deprecated final predicate isOutReturnValue() { isReturnValue() } + + /** + * Holds if this is the output value pointed to by the return value of a function, if the function + * returns a pointer, or the output value referred to by the return value of a function, if the + * function returns a reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of + * `*getPointer()` (with type `char`). + * - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of + * `getReference()` (with type `float`). + * - There is no `FunctionOutput` of `getInt()` for which `isReturnValueDeref()` holds because the + * return type of `getInt()` is neither a pointer nor a reference. + */ + predicate isReturnValueDeref() { none() } + + /** + * Holds if this is the output value pointed to by the return value of a function, if the function + * returns a pointer, or the output value referred to by the return value of a function, if the + * function returns a reference. + * DEPRECATED: Use `isReturnValueDeref()` instead. + */ + deprecated final predicate isOutReturnPointer() { isReturnValueDeref() } + + /** + * Holds if `i >= 0` and `isParameterDeref(i)` holds for this is the value, or + * if `i = -1` and `isQualifierObject()` holds for this value. + */ + final predicate isParameterDerefOrQualifierObject(ParameterIndex i) { + i >= 0 and this.isParameterDeref(i) + or + i = -1 and this.isQualifierObject() + } + } + /** * Models the aliasing behavior of a library function. */ @@ -44,5 +314,10 @@ module AliasModels { * Holds if the function always returns the value of the parameter at the specified index. */ abstract predicate parameterIsAlwaysReturned(int index); + + /** + * Holds if the address passed in via `input` is always propagated to `output`. + */ + abstract predicate hasAddressFlow(FunctionInput input, FunctionOutput output); } } diff --git a/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll b/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll index f828419a4409..f37a4f2d074a 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 95ffbf9ffc91..374ecaaa1839 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/File.qll b/csharp/ql/src/semmle/code/csharp/File.qll index 86656af41243..d8b23bb61f44 100644 --- a/csharp/ql/src/semmle/code/csharp/File.qll +++ b/csharp/ql/src/semmle/code/csharp/File.qll @@ -192,7 +192,7 @@ class File extends Container, @file { override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } /** Holds if this file contains source code. */ - predicate fromSource() { this.getNumberOfLinesOfCode() > 0 } + predicate fromSource() { files(this, _, _, "cs", _) } /** Holds if this file is a library. */ predicate fromLibrary() { diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll index bdcfa6ac4b75..8a721da5293d 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll @@ -36,7 +36,7 @@ class Guard extends Expr { * Holds if basic block `bb` is guarded by this expression having value `v`. */ predicate controlsBasicBlock(BasicBlock bb, AbstractValue v) { - Internal::guardControls(this, _, bb, v) + Internal::guardControls(this, bb, v) } /** @@ -50,6 +50,12 @@ class Guard extends Expr { polarity = v.getValue() ) } + + /** + * Gets a valid value for this guard. For example, if this guard is a test, then + * it can have Boolean values `true` and `false`. + */ + AbstractValue getAValue() { isGuard(this, result) } } /** An abstract value. */ @@ -967,13 +973,6 @@ module Internal { e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand()) } - /** Same as `this.getAChildExpr*()`, but avoids `fastTC`. */ - private Expr getAChildExprStar(Guard g) { - result = g - or - result = getAChildExprStar(g).getAChildExpr() - } - private Expr stripConditionalExpr(Expr e) { e = any(ConditionalExpr ce | @@ -1009,8 +1008,11 @@ module Internal { /** Holds if pre-basic-block `bb` only is reached when guard `g` has abstract value `v`. */ predicate preControls(Guard g, PreBasicBlocks::PreBasicBlock bb, AbstractValue v) { - exists(AbstractValue v0, Guard g0 | preControlsDirect(g0, bb, v0) | - preImpliesSteps(g0, v0, g, v) + preControlsDirect(g, bb, v) + or + exists(AbstractValue v0, Guard g0 | + preControls(g0, bb, v0) and + preImpliesStep(g0, v0, g, v) ) } @@ -1038,6 +1040,23 @@ module Internal { } } + private predicate canReturnBool(Callable c, Expr ret) { + canReturn(c, ret) and + c.getReturnType() instanceof BoolType + } + + private predicate boolReturnImplies(Expr ret, BooleanValue retVal, Guard g, AbstractValue v) { + canReturnBool(_, ret) and + isGuard(ret, retVal) and + g = ret and + v = retVal + or + exists(Guard g0, AbstractValue v0 | + boolReturnImplies(ret, retVal, g0, v0) and + preImpliesStep(g0, v0, g, v) + ) + } + /** * Holds if `ret` is an expression returned by the callable to which parameter * `p` belongs, and `ret` having Boolean value `retVal` allows the conclusion @@ -1046,14 +1065,14 @@ module Internal { private predicate validReturnInCustomNullCheck( Expr ret, Parameter p, BooleanValue retVal, boolean isNull ) { - exists(Callable c | canReturn(c, ret) | - p.getCallable() = c and - c.getReturnType() instanceof BoolType + exists(Callable c | + canReturnBool(c, ret) and + p.getCallable() = c ) and exists(PreSsaImplicitParameterDefinition def | p = def.getParameter() | def.nullGuardedReturn(ret, isNull) or - exists(NullValue nv | preImpliesSteps(ret, retVal, def.getARead(), nv) | + exists(NullValue nv | boolReturnImplies(ret, retVal, def.getARead(), nv) | if nv.isNull() then isNull = true else isNull = false ) ) @@ -1691,6 +1710,79 @@ module Internal { import PreCFG + private predicate interestingDescendantCandidate(Expr e) { + guardControls(e, _, _) + or + e instanceof AccessOrCallExpr + } + + /** + * An (interesting) descendant of a guard that controls some basic block. + * + * This class exists purely for performance reasons: It allows us to big-step + * through the child hierarchy in `guardControlsSub()` instead of using + * `getAChildExpr()`. + */ + private class ControlGuardDescendant extends Expr { + ControlGuardDescendant() { + guardControls(this, _, _) + or + any(ControlGuardDescendant other).interestingDescendant(this) + } + + private predicate descendant(Expr e) { + e = this.getAChildExpr() + or + exists(Expr mid | + descendant(mid) and + not interestingDescendantCandidate(mid) and + e = mid.getAChildExpr() + ) + } + + /** Holds if `e` is an interesting descendant of this descendant. */ + predicate interestingDescendant(Expr e) { + descendant(e) and + interestingDescendantCandidate(e) + } + } + + /** + * Holds if `g` controls basic block `bb`, and `sub` is some (interesting) + * sub expression of `g`. + * + * Sub expressions inside nested logical operations that themselve control `bb` + * are not included, since these will be sub expressions of their immediately + * enclosing logical operation. (This restriction avoids a quadratic blow-up.) + * + * For example, in + * + * ```csharp + * if (a && (b && c)) + * BLOCK + * ``` + * + * `a` is included as a sub expression of `a && (b && c)` (which controls `BLOCK`), + * while `b` and `c` are only included as sub expressions of `b && c` (which also + * controls `BLOCK`). + */ + pragma[nomagic] + private predicate guardControlsSub(Guard g, BasicBlock bb, ControlGuardDescendant sub) { + guardControls(g, bb, _) and + sub = g + or + exists(ControlGuardDescendant mid | + guardControlsSub(g, bb, mid) and + mid.interestingDescendant(sub) + | + not guardControls(sub, bb, _) + or + not mid instanceof UnaryLogicalOperation and + not mid instanceof BinaryLogicalOperation and + not mid instanceof BitwiseOperation + ) + } + /** * A helper class for calculating structurally equal access/call expressions. */ @@ -1710,13 +1802,13 @@ module Internal { /** * Holds if access/call expression `e` (targeting declaration `target`) - * is a sub expression of a condition that controls whether basic block + * is a sub expression of a guard that controls whether basic block * `bb` is reached. */ pragma[noinline] private predicate candidateAux(AccessOrCallExpr e, Declaration target, BasicBlock bb) { target = e.getTarget() and - exists(Guard g | e = getAChildExprStar(g) | guardControls(g, _, bb, _)) + guardControlsSub(_, bb, e) } } @@ -1727,49 +1819,67 @@ module Internal { /** * Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. - * - * `cb` records all of the possible condition blocks for `g` that a path from the - * callable entry point to `bb` may go through. */ cached - predicate guardControls(Guard g, ConditionBlock cb, BasicBlock bb, AbstractValue v) { + predicate guardControls(Guard g, BasicBlock bb, AbstractValue v) { + exists(ControlFlowElement cfe, ConditionalSuccessor cs | + v.branch(cfe, cs, g) and cfe.controlsBlock(bb, cs, _) + ) + or exists(AbstractValue v0, Guard g0 | - impliesSteps(g0, v0, g, v) and - exists(ControlFlowElement cfe, ConditionalSuccessor cs | - v0.branch(cfe, cs, g0) and cfe.controlsBlock(bb, cs, cb) - ) + guardControls(g0, bb, v0) and + impliesStep(g0, v0, g, v) ) } - pragma[noinline] + pragma[nomagic] + private predicate guardControlsSubSame(Guard g, BasicBlock bb, ControlGuardDescendant sub) { + guardControlsSub(g, bb, sub) and + any(ConditionOnExprComparisonConfig c).same(sub, _) + } + + pragma[nomagic] private predicate nodeIsGuardedBySameSubExpr0( - ControlFlow::Node guardedCfn, AccessOrCallExpr guarded, Guard g, ConditionBlock cb, + ControlFlow::Node guardedCfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { Stages::GuardsStage::forceCachingInSameStage() and guardedCfn = guarded.getAControlFlowNode() and - guardControls(g, cb, guardedCfn.getBasicBlock(), v) and - exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded)) + guardedBB = guardedCfn.getBasicBlock() and + guardControls(g, guardedBB, v) and + guardControlsSubSame(g, guardedBB, sub) and + any(ConditionOnExprComparisonConfig c).same(sub, guarded) } - pragma[noinline] + pragma[nomagic] private predicate nodeIsGuardedBySameSubExpr( - ControlFlow::Node guardedCfn, AccessOrCallExpr guarded, Guard g, ConditionBlock cb, + ControlFlow::Node guardedCfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { - nodeIsGuardedBySameSubExpr0(guardedCfn, guarded, g, cb, sub, v) and - sub = getAChildExprStar(g) + nodeIsGuardedBySameSubExpr0(guardedCfn, guardedBB, guarded, g, sub, v) and + guardControlsSub(g, guardedBB, sub) } - pragma[noinline] + pragma[nomagic] + private predicate nodeIsGuardedBySameSubExprSsaDef0( + ControlFlow::Node cfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g, + ControlFlow::Node subCfn, BasicBlock subCfnBB, AccessOrCallExpr sub, AbstractValue v, + Ssa::Definition def + ) { + nodeIsGuardedBySameSubExpr(cfn, guardedBB, guarded, g, sub, v) and + def = sub.getAnSsaQualifier(subCfn) and + subCfnBB = subCfn.getBasicBlock() + } + + pragma[nomagic] private predicate nodeIsGuardedBySameSubExprSsaDef( - ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, ControlFlow::Node subCfn, + ControlFlow::Node guardedCfn, AccessOrCallExpr guarded, Guard g, ControlFlow::Node subCfn, AccessOrCallExpr sub, AbstractValue v, Ssa::Definition def ) { - exists(ConditionBlock cb | - nodeIsGuardedBySameSubExpr(cfn, guarded, g, cb, sub, v) and - subCfn.getBasicBlock().dominates(cb) and - def = sub.getAnSsaQualifier(subCfn) + exists(BasicBlock guardedBB, BasicBlock subCfnBB | + nodeIsGuardedBySameSubExprSsaDef0(guardedCfn, guardedBB, guarded, g, subCfn, subCfnBB, sub, + v, def) and + subCfnBB.getASuccessor*() = guardedBB ) } @@ -1789,7 +1899,7 @@ module Internal { AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { forex(ControlFlow::Node cfn | cfn = guarded.getAControlFlowNode() | - nodeIsGuardedBySameSubExpr(cfn, guarded, g, _, sub, v) + nodeIsGuardedBySameSubExpr(cfn, _, guarded, g, sub, v) ) } @@ -1814,7 +1924,7 @@ module Internal { predicate isGuardedByNode( ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { - nodeIsGuardedBySameSubExpr(guarded, _, g, _, sub, v) and + nodeIsGuardedBySameSubExpr(guarded, _, _, g, sub, v) and forall(ControlFlow::Node subCfn, Ssa::Definition def | nodeIsGuardedBySameSubExprSsaDef(guarded, _, g, subCfn, sub, v, def) | @@ -1860,40 +1970,6 @@ module Internal { } import Cached - - /** - * Holds if the assumption that `g1` has abstract value `v1` implies that - * `g2` has abstract value `v2`, using zero or more steps of reasoning. That is, - * the evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`. - * - * This predicate does not rely on the control flow graph. - */ - predicate preImpliesSteps(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) { - g1 = g2 and - v1 = v2 and - isGuard(g1, v1) - or - exists(Expr mid, AbstractValue vMid | preImpliesSteps(g1, v1, mid, vMid) | - preImpliesStep(mid, vMid, g2, v2) - ) - } - - /** - * Holds if the assumption that `g1` has abstract value `v1` implies that - * `g2` has abstract value `v2`, using zero or more steps of reasoning. That is, - * the evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`. - * - * This predicate relies on the control flow graph. - */ - predicate impliesSteps(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) { - g1 = g2 and - v1 = v2 and - isGuard(g1, v1) - or - exists(Expr mid, AbstractValue vMid | impliesSteps(g1, v1, mid, vMid) | - impliesStep(mid, vMid, g2, v2) - ) - } } private import Internal 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 f828419a4409..f37a4f2d074a 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/LibraryTypeDataFlow.qll b/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll index 5798080a98ab..32f22b979148 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll @@ -807,17 +807,29 @@ class SystemTextStringBuilderFlow extends LibraryTypeDataFlow, SystemTextStringB sinkAp = AccessPath::empty() and preservesValue = false or - exists(int i, Type t | - name.regexpMatch("Append(Format|Line)?") and - t = m.getParameter(i).getType() and - source = TCallableFlowSourceArg(i) and + name.regexpMatch("Append(Format|Line|Join)?") and + preservesValue = true and + ( + exists(int i, Type t | + t = m.getParameter(i).getType() and + source = TCallableFlowSourceArg(i) and + sink = TCallableFlowSinkQualifier() and + sinkAp = AccessPath::element() + | + ( + t instanceof StringType or + t instanceof ObjectType + ) and + sourceAp = AccessPath::empty() + or + isCollectionType(t) and + sourceAp = AccessPath::element() + ) + or + source = TCallableFlowSourceQualifier() and sourceAp = AccessPath::empty() and - sink = [TCallableFlowSinkQualifier().(TCallableFlowSink), TCallableFlowSinkReturn()] and - sinkAp = AccessPath::element() and - preservesValue = true - | - t instanceof StringType or - t instanceof ObjectType + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() ) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll index 15133668d00a..131480a8b592 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll @@ -128,12 +128,19 @@ private predicate dereferenceAt(BasicBlock bb, int i, Ssa::Definition def, Deref * has abstract value `vDef`. */ private predicate exprImpliesSsaDef( - Expr e, G::AbstractValue vExpr, Ssa::Definition def, G::AbstractValue vDef + G::Guard e, G::AbstractValue vExpr, Ssa::Definition def, G::AbstractValue vDef ) { - exists(G::Guard g | G::Internal::impliesSteps(e, vExpr, g, vDef) | - g = def.getARead() + vExpr = e.getAValue() and + vExpr = vDef and + ( + e = def.getARead() or - g = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() + e = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() + ) + or + exists(Expr e0, G::AbstractValue vExpr0 | + exprImpliesSsaDef(e0, vExpr0, def, vDef) and + G::Internal::impliesStep(e, vExpr, e0, vExpr0) ) } 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 4eac274b04f2..0a7cf4acc41b 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 8b446d28b86d..9498e51e7e61 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 3b646973e282..f42410ca3a5c 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 { +class ArgumentNode extends Node { + ArgumentNode() { argumentNode(this, _, _) } + /** Holds if this argument occurs at the given position in the given call. */ - cached - abstract predicate argumentOf(DataFlowCall call, int pos); + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} - /** Gets the call in which this node is an argument. */ - final DataFlowCall getCall() { this.argumentOf(result, _) } +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 dd6e72d54f6d..12a62faf3873 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/FlowSummaryImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll index 3c8c94c913c3..bdf5498d8c63 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll @@ -446,7 +446,19 @@ module Private { summary(c, inputContents, outputContents, preservesValue) and pred = summaryNodeInputState(c, inputContents) and succ = summaryNodeOutputState(c, outputContents) + | + preservesValue = true + or + preservesValue = false and not summary(c, inputContents, outputContents, true) ) + or + // If flow through a method updates a parameter from some input A, and that + // parameter also is returned through B, then we'd like a combined flow from A + // to B as well. As an example, this simplifies modeling of fluent methods: + // for `StringBuilder.append(x)` with a specified value flow from qualifier to + // return value and taint flow from argument 0 to the qualifier, then this + // allows us to infer taint flow from argument 0 to the return value. + summaryPostUpdateNode(pred, succ) and preservesValue = true } /** 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 41da2b8f0b5d..63d6c902fed2 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 f828419a4409..f37a4f2d074a 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 f828419a4409..f37a4f2d074a 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/dataflow/internal/rangeanalysis/RangeUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll index 70ff26c1c586..2f8e93a4b750 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll @@ -122,7 +122,7 @@ private module Impl { ) or exists(G::AbstractValue v0 | - G::Internal::impliesSteps(result, v, eqFlowCondAbs(def, e, delta, isEq, v0), v0) + G::Internal::impliesStep(result, v, eqFlowCondAbs(def, e, delta, isEq, v0), v0) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll index e436fb2b99af..cca858d69baf 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, _) + source = any(DispatchMethodOrAccessorCall call).getAStaticTargetExt() } - pragma[noinline] - private Unification::ConstrainedTypeParameter getAConstrainedTypeParameterQualifierType( - DispatchMethodOrAccessorCall call - ) { - result = getAPossibleType(call.getQualifier(), false) - } + 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[noinline] - private predicate constrainedTypeParameterQualifierTypeSubsumes( - ValueOrRefType t, Unification::ConstrainedTypeParameter tp - ) { - tp = getAConstrainedTypeParameterQualifierType(_) and - tp.subsumes(t) - } + pragma[nomagic] + predicate hasQualifierTypeInherited(Type t) { t = getAPossibleType(this.getQualifier(), _) } - 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()] + 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 = t1 + t = Gvn::getGlobalValueNumber(t0) or - Unification::subsumes(t1, t) + Gvn::subsumes(Gvn::getGlobalValueNumber(t0), 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() - ) - } + pragma[noinline] + private predicate hasConstrainedTypeParameterQualifierType( + Unification::ConstrainedTypeParameter tp + ) { + tp = getAPossibleType(this.getQualifier(), false) + } - abstract private class DispatchMethodOrAccessorCall extends DispatchCallImpl { - pragma[nomagic] - predicate hasQualifierTypeInherited(Type t) { t = getAPossibleType(this.getQualifier(), _) } + 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()) + ) ) } 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 4538f09e2c0b..0ffabe60588b 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/csharp/security/dataflow/flowsources/Local.qll b/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsources/Local.qll index e867c9be3ddc..4c7f70921d90 100644 --- a/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsources/Local.qll +++ b/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsources/Local.qll @@ -20,3 +20,15 @@ class TextFieldSource extends LocalUserInputSource { override string getSourceType() { result = "TextBox text" } } + +/** A call to any `System.Console.Read*` method. */ +class SystemConsoleReadSource extends LocalUserInputSource { + SystemConsoleReadSource() { + this.asExpr() = + any(MethodCall call | + call.getTarget().hasQualifiedName("System.Console", ["ReadLine", "Read", "ReadKey"]) + ) + } + + override string getSourceType() { result = "System.Console input" } +} diff --git a/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.expected b/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.expected new file mode 100644 index 000000000000..b390b5be6394 --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.expected @@ -0,0 +1,797 @@ +| GuardsStressTest.cs:8:26:8:27 | access to field ch | +| GuardsStressTest.cs:9:4:9:5 | access to field ch | +| GuardsStressTest.cs:9:17:9:18 | access to field ch | +| GuardsStressTest.cs:10:4:10:5 | access to field ch | +| GuardsStressTest.cs:11:4:11:5 | access to field ch | +| GuardsStressTest.cs:11:17:11:18 | access to field ch | +| GuardsStressTest.cs:12:4:12:5 | access to field ch | +| GuardsStressTest.cs:13:4:13:5 | access to field ch | +| GuardsStressTest.cs:14:4:14:5 | access to field ch | +| GuardsStressTest.cs:15:4:15:5 | access to field ch | +| GuardsStressTest.cs:15:17:15:18 | access to field ch | +| GuardsStressTest.cs:16:4:16:5 | access to field ch | +| GuardsStressTest.cs:16:17:16:18 | access to field ch | +| GuardsStressTest.cs:17:4:17:5 | access to field ch | +| GuardsStressTest.cs:17:17:17:18 | access to field ch | +| GuardsStressTest.cs:18:4:18:5 | access to field ch | +| GuardsStressTest.cs:18:17:18:18 | access to field ch | +| GuardsStressTest.cs:19:4:19:5 | access to field ch | +| GuardsStressTest.cs:19:17:19:18 | access to field ch | +| GuardsStressTest.cs:20:4:20:5 | access to field ch | +| GuardsStressTest.cs:21:4:21:5 | access to field ch | +| GuardsStressTest.cs:22:4:22:5 | access to field ch | +| GuardsStressTest.cs:22:17:22:18 | access to field ch | +| GuardsStressTest.cs:23:4:23:5 | access to field ch | +| GuardsStressTest.cs:23:17:23:18 | access to field ch | +| GuardsStressTest.cs:24:4:24:5 | access to field ch | +| GuardsStressTest.cs:24:17:24:18 | access to field ch | +| GuardsStressTest.cs:25:4:25:5 | access to field ch | +| GuardsStressTest.cs:26:4:26:5 | access to field ch | +| GuardsStressTest.cs:26:17:26:18 | access to field ch | +| GuardsStressTest.cs:27:4:27:5 | access to field ch | +| GuardsStressTest.cs:28:4:28:5 | access to field ch | +| GuardsStressTest.cs:28:17:28:18 | access to field ch | +| GuardsStressTest.cs:29:4:29:5 | access to field ch | +| GuardsStressTest.cs:29:17:29:18 | access to field ch | +| GuardsStressTest.cs:30:4:30:5 | access to field ch | +| GuardsStressTest.cs:30:18:30:19 | access to field ch | +| GuardsStressTest.cs:31:4:31:5 | access to field ch | +| GuardsStressTest.cs:31:18:31:19 | access to field ch | +| GuardsStressTest.cs:32:4:32:5 | access to field ch | +| GuardsStressTest.cs:32:18:32:19 | access to field ch | +| GuardsStressTest.cs:33:4:33:5 | access to field ch | +| GuardsStressTest.cs:33:18:33:19 | access to field ch | +| GuardsStressTest.cs:34:4:34:5 | access to field ch | +| GuardsStressTest.cs:35:4:35:5 | access to field ch | +| GuardsStressTest.cs:35:18:35:19 | access to field ch | +| GuardsStressTest.cs:36:4:36:5 | access to field ch | +| GuardsStressTest.cs:36:18:36:19 | access to field ch | +| GuardsStressTest.cs:37:4:37:5 | access to field ch | +| GuardsStressTest.cs:38:4:38:5 | access to field ch | +| GuardsStressTest.cs:38:18:38:19 | access to field ch | +| GuardsStressTest.cs:39:4:39:5 | access to field ch | +| GuardsStressTest.cs:39:18:39:19 | access to field ch | +| GuardsStressTest.cs:40:4:40:5 | access to field ch | +| GuardsStressTest.cs:41:4:41:5 | access to field ch | +| GuardsStressTest.cs:41:18:41:19 | access to field ch | +| GuardsStressTest.cs:42:4:42:5 | access to field ch | +| GuardsStressTest.cs:42:18:42:19 | access to field ch | +| GuardsStressTest.cs:43:4:43:5 | access to field ch | +| GuardsStressTest.cs:43:18:43:19 | access to field ch | +| GuardsStressTest.cs:44:4:44:5 | access to field ch | +| GuardsStressTest.cs:44:18:44:19 | access to field ch | +| GuardsStressTest.cs:45:4:45:5 | access to field ch | +| GuardsStressTest.cs:45:18:45:19 | access to field ch | +| GuardsStressTest.cs:46:4:46:5 | access to field ch | +| GuardsStressTest.cs:46:18:46:19 | access to field ch | +| GuardsStressTest.cs:47:4:47:5 | access to field ch | +| GuardsStressTest.cs:47:18:47:19 | access to field ch | +| GuardsStressTest.cs:48:4:48:5 | access to field ch | +| GuardsStressTest.cs:48:18:48:19 | access to field ch | +| GuardsStressTest.cs:49:4:49:5 | access to field ch | +| GuardsStressTest.cs:50:4:50:5 | access to field ch | +| GuardsStressTest.cs:50:18:50:19 | access to field ch | +| GuardsStressTest.cs:51:4:51:5 | access to field ch | +| GuardsStressTest.cs:51:18:51:19 | access to field ch | +| GuardsStressTest.cs:52:4:52:5 | access to field ch | +| GuardsStressTest.cs:52:18:52:19 | access to field ch | +| GuardsStressTest.cs:53:4:53:5 | access to field ch | +| GuardsStressTest.cs:54:4:54:5 | access to field ch | +| GuardsStressTest.cs:54:18:54:19 | access to field ch | +| GuardsStressTest.cs:55:4:55:5 | access to field ch | +| GuardsStressTest.cs:55:18:55:19 | access to field ch | +| GuardsStressTest.cs:56:4:56:5 | access to field ch | +| GuardsStressTest.cs:57:4:57:5 | access to field ch | +| GuardsStressTest.cs:57:18:57:19 | access to field ch | +| GuardsStressTest.cs:58:4:58:5 | access to field ch | +| GuardsStressTest.cs:58:18:58:19 | access to field ch | +| GuardsStressTest.cs:59:4:59:5 | access to field ch | +| GuardsStressTest.cs:59:18:59:19 | access to field ch | +| GuardsStressTest.cs:60:4:60:5 | access to field ch | +| GuardsStressTest.cs:60:18:60:19 | access to field ch | +| GuardsStressTest.cs:61:4:61:5 | access to field ch | +| GuardsStressTest.cs:61:18:61:19 | access to field ch | +| GuardsStressTest.cs:62:4:62:5 | access to field ch | +| GuardsStressTest.cs:62:18:62:19 | access to field ch | +| GuardsStressTest.cs:63:4:63:5 | access to field ch | +| GuardsStressTest.cs:63:18:63:19 | access to field ch | +| GuardsStressTest.cs:64:4:64:5 | access to field ch | +| GuardsStressTest.cs:64:18:64:19 | access to field ch | +| GuardsStressTest.cs:65:4:65:5 | access to field ch | +| GuardsStressTest.cs:65:18:65:19 | access to field ch | +| GuardsStressTest.cs:66:4:66:5 | access to field ch | +| GuardsStressTest.cs:66:18:66:19 | access to field ch | +| GuardsStressTest.cs:67:4:67:5 | access to field ch | +| GuardsStressTest.cs:67:18:67:19 | access to field ch | +| GuardsStressTest.cs:68:4:68:5 | access to field ch | +| GuardsStressTest.cs:69:4:69:5 | access to field ch | +| GuardsStressTest.cs:69:18:69:19 | access to field ch | +| GuardsStressTest.cs:70:4:70:5 | access to field ch | +| GuardsStressTest.cs:70:18:70:19 | access to field ch | +| GuardsStressTest.cs:71:4:71:5 | access to field ch | +| GuardsStressTest.cs:71:18:71:19 | access to field ch | +| GuardsStressTest.cs:72:4:72:5 | access to field ch | +| GuardsStressTest.cs:72:18:72:19 | access to field ch | +| GuardsStressTest.cs:73:4:73:5 | access to field ch | +| GuardsStressTest.cs:74:4:74:5 | access to field ch | +| GuardsStressTest.cs:74:18:74:19 | access to field ch | +| GuardsStressTest.cs:75:4:75:5 | access to field ch | +| GuardsStressTest.cs:75:18:75:19 | access to field ch | +| GuardsStressTest.cs:76:4:76:5 | access to field ch | +| GuardsStressTest.cs:76:18:76:19 | access to field ch | +| GuardsStressTest.cs:77:4:77:5 | access to field ch | +| GuardsStressTest.cs:77:18:77:19 | access to field ch | +| GuardsStressTest.cs:78:4:78:5 | access to field ch | +| GuardsStressTest.cs:78:18:78:19 | access to field ch | +| GuardsStressTest.cs:79:4:79:5 | access to field ch | +| GuardsStressTest.cs:79:18:79:19 | access to field ch | +| GuardsStressTest.cs:80:4:80:5 | access to field ch | +| GuardsStressTest.cs:80:18:80:19 | access to field ch | +| GuardsStressTest.cs:81:4:81:5 | access to field ch | +| GuardsStressTest.cs:81:18:81:19 | access to field ch | +| GuardsStressTest.cs:82:4:82:5 | access to field ch | +| GuardsStressTest.cs:82:18:82:19 | access to field ch | +| GuardsStressTest.cs:83:4:83:5 | access to field ch | +| GuardsStressTest.cs:83:18:83:19 | access to field ch | +| GuardsStressTest.cs:84:4:84:5 | access to field ch | +| GuardsStressTest.cs:84:18:84:19 | access to field ch | +| GuardsStressTest.cs:85:4:85:5 | access to field ch | +| GuardsStressTest.cs:86:4:86:5 | access to field ch | +| GuardsStressTest.cs:86:18:86:19 | access to field ch | +| GuardsStressTest.cs:87:4:87:5 | access to field ch | +| GuardsStressTest.cs:87:18:87:19 | access to field ch | +| GuardsStressTest.cs:88:4:88:5 | access to field ch | +| GuardsStressTest.cs:88:18:88:19 | access to field ch | +| GuardsStressTest.cs:89:4:89:5 | access to field ch | +| GuardsStressTest.cs:90:4:90:5 | access to field ch | +| GuardsStressTest.cs:90:18:90:19 | access to field ch | +| GuardsStressTest.cs:91:4:91:5 | access to field ch | +| GuardsStressTest.cs:92:4:92:5 | access to field ch | +| GuardsStressTest.cs:92:18:92:19 | access to field ch | +| GuardsStressTest.cs:93:4:93:5 | access to field ch | +| GuardsStressTest.cs:93:18:93:19 | access to field ch | +| GuardsStressTest.cs:94:4:94:5 | access to field ch | +| GuardsStressTest.cs:94:18:94:19 | access to field ch | +| GuardsStressTest.cs:95:4:95:5 | access to field ch | +| GuardsStressTest.cs:95:18:95:19 | access to field ch | +| GuardsStressTest.cs:96:4:96:5 | access to field ch | +| GuardsStressTest.cs:96:18:96:19 | access to field ch | +| GuardsStressTest.cs:97:4:97:5 | access to field ch | +| GuardsStressTest.cs:97:18:97:19 | access to field ch | +| GuardsStressTest.cs:98:4:98:5 | access to field ch | +| GuardsStressTest.cs:98:18:98:19 | access to field ch | +| GuardsStressTest.cs:99:4:99:5 | access to field ch | +| GuardsStressTest.cs:99:18:99:19 | access to field ch | +| GuardsStressTest.cs:100:4:100:5 | access to field ch | +| GuardsStressTest.cs:100:18:100:19 | access to field ch | +| GuardsStressTest.cs:101:4:101:5 | access to field ch | +| GuardsStressTest.cs:101:18:101:19 | access to field ch | +| GuardsStressTest.cs:102:4:102:5 | access to field ch | +| GuardsStressTest.cs:102:18:102:19 | access to field ch | +| GuardsStressTest.cs:103:4:103:5 | access to field ch | +| GuardsStressTest.cs:104:4:104:5 | access to field ch | +| GuardsStressTest.cs:104:18:104:19 | access to field ch | +| GuardsStressTest.cs:105:4:105:5 | access to field ch | +| GuardsStressTest.cs:105:18:105:19 | access to field ch | +| GuardsStressTest.cs:106:4:106:5 | access to field ch | +| GuardsStressTest.cs:106:18:106:19 | access to field ch | +| GuardsStressTest.cs:107:4:107:5 | access to field ch | +| GuardsStressTest.cs:107:18:107:19 | access to field ch | +| GuardsStressTest.cs:108:4:108:5 | access to field ch | +| GuardsStressTest.cs:108:18:108:19 | access to field ch | +| GuardsStressTest.cs:109:4:109:5 | access to field ch | +| GuardsStressTest.cs:109:18:109:19 | access to field ch | +| GuardsStressTest.cs:110:4:110:5 | access to field ch | +| GuardsStressTest.cs:110:18:110:19 | access to field ch | +| GuardsStressTest.cs:111:4:111:5 | access to field ch | +| GuardsStressTest.cs:111:18:111:19 | access to field ch | +| GuardsStressTest.cs:112:4:112:5 | access to field ch | +| GuardsStressTest.cs:112:18:112:19 | access to field ch | +| GuardsStressTest.cs:113:4:113:5 | access to field ch | +| GuardsStressTest.cs:113:18:113:19 | access to field ch | +| GuardsStressTest.cs:114:4:114:5 | access to field ch | +| GuardsStressTest.cs:114:18:114:19 | access to field ch | +| GuardsStressTest.cs:115:4:115:5 | access to field ch | +| GuardsStressTest.cs:115:18:115:19 | access to field ch | +| GuardsStressTest.cs:116:4:116:5 | access to field ch | +| GuardsStressTest.cs:116:18:116:19 | access to field ch | +| GuardsStressTest.cs:117:4:117:5 | access to field ch | +| GuardsStressTest.cs:117:18:117:19 | access to field ch | +| GuardsStressTest.cs:118:4:118:5 | access to field ch | +| GuardsStressTest.cs:118:18:118:19 | access to field ch | +| GuardsStressTest.cs:119:4:119:5 | access to field ch | +| GuardsStressTest.cs:119:18:119:19 | access to field ch | +| GuardsStressTest.cs:120:4:120:5 | access to field ch | +| GuardsStressTest.cs:121:4:121:5 | access to field ch | +| GuardsStressTest.cs:121:18:121:19 | access to field ch | +| GuardsStressTest.cs:122:4:122:5 | access to field ch | +| GuardsStressTest.cs:122:18:122:19 | access to field ch | +| GuardsStressTest.cs:123:4:123:5 | access to field ch | +| GuardsStressTest.cs:123:18:123:19 | access to field ch | +| GuardsStressTest.cs:124:4:124:5 | access to field ch | +| GuardsStressTest.cs:124:18:124:19 | access to field ch | +| GuardsStressTest.cs:125:4:125:5 | access to field ch | +| GuardsStressTest.cs:125:18:125:19 | access to field ch | +| GuardsStressTest.cs:126:4:126:5 | access to field ch | +| GuardsStressTest.cs:127:4:127:5 | access to field ch | +| GuardsStressTest.cs:127:18:127:19 | access to field ch | +| GuardsStressTest.cs:128:4:128:5 | access to field ch | +| GuardsStressTest.cs:128:18:128:19 | access to field ch | +| GuardsStressTest.cs:129:4:129:5 | access to field ch | +| GuardsStressTest.cs:129:18:129:19 | access to field ch | +| GuardsStressTest.cs:130:4:130:5 | access to field ch | +| GuardsStressTest.cs:130:18:130:19 | access to field ch | +| GuardsStressTest.cs:131:4:131:5 | access to field ch | +| GuardsStressTest.cs:131:18:131:19 | access to field ch | +| GuardsStressTest.cs:132:4:132:5 | access to field ch | +| GuardsStressTest.cs:132:18:132:19 | access to field ch | +| GuardsStressTest.cs:133:4:133:5 | access to field ch | +| GuardsStressTest.cs:133:18:133:19 | access to field ch | +| GuardsStressTest.cs:134:4:134:5 | access to field ch | +| GuardsStressTest.cs:135:4:135:5 | access to field ch | +| GuardsStressTest.cs:136:4:136:5 | access to field ch | +| GuardsStressTest.cs:136:18:136:19 | access to field ch | +| GuardsStressTest.cs:137:4:137:5 | access to field ch | +| GuardsStressTest.cs:137:18:137:19 | access to field ch | +| GuardsStressTest.cs:138:4:138:5 | access to field ch | +| GuardsStressTest.cs:138:18:138:19 | access to field ch | +| GuardsStressTest.cs:139:4:139:5 | access to field ch | +| GuardsStressTest.cs:139:18:139:19 | access to field ch | +| GuardsStressTest.cs:140:4:140:5 | access to field ch | +| GuardsStressTest.cs:140:18:140:19 | access to field ch | +| GuardsStressTest.cs:141:4:141:5 | access to field ch | +| GuardsStressTest.cs:141:18:141:19 | access to field ch | +| GuardsStressTest.cs:142:4:142:5 | access to field ch | +| GuardsStressTest.cs:142:18:142:19 | access to field ch | +| GuardsStressTest.cs:143:4:143:5 | access to field ch | +| GuardsStressTest.cs:143:18:143:19 | access to field ch | +| GuardsStressTest.cs:144:4:144:5 | access to field ch | +| GuardsStressTest.cs:144:18:144:19 | access to field ch | +| GuardsStressTest.cs:145:4:145:5 | access to field ch | +| GuardsStressTest.cs:145:18:145:19 | access to field ch | +| GuardsStressTest.cs:146:4:146:5 | access to field ch | +| GuardsStressTest.cs:146:18:146:19 | access to field ch | +| GuardsStressTest.cs:147:4:147:5 | access to field ch | +| GuardsStressTest.cs:147:18:147:19 | access to field ch | +| GuardsStressTest.cs:148:4:148:5 | access to field ch | +| GuardsStressTest.cs:148:18:148:19 | access to field ch | +| GuardsStressTest.cs:149:4:149:5 | access to field ch | +| GuardsStressTest.cs:149:18:149:19 | access to field ch | +| GuardsStressTest.cs:150:4:150:5 | access to field ch | +| GuardsStressTest.cs:150:18:150:19 | access to field ch | +| GuardsStressTest.cs:151:4:151:5 | access to field ch | +| GuardsStressTest.cs:151:18:151:19 | access to field ch | +| GuardsStressTest.cs:152:4:152:5 | access to field ch | +| GuardsStressTest.cs:152:18:152:19 | access to field ch | +| GuardsStressTest.cs:153:4:153:5 | access to field ch | +| GuardsStressTest.cs:153:18:153:19 | access to field ch | +| GuardsStressTest.cs:154:4:154:5 | access to field ch | +| GuardsStressTest.cs:154:18:154:19 | access to field ch | +| GuardsStressTest.cs:155:4:155:5 | access to field ch | +| GuardsStressTest.cs:155:18:155:19 | access to field ch | +| GuardsStressTest.cs:156:4:156:5 | access to field ch | +| GuardsStressTest.cs:156:18:156:19 | access to field ch | +| GuardsStressTest.cs:157:4:157:5 | access to field ch | +| GuardsStressTest.cs:157:18:157:19 | access to field ch | +| GuardsStressTest.cs:158:4:158:5 | access to field ch | +| GuardsStressTest.cs:158:18:158:19 | access to field ch | +| GuardsStressTest.cs:159:4:159:5 | access to field ch | +| GuardsStressTest.cs:159:18:159:19 | access to field ch | +| GuardsStressTest.cs:160:4:160:5 | access to field ch | +| GuardsStressTest.cs:161:4:161:5 | access to field ch | +| GuardsStressTest.cs:161:18:161:19 | access to field ch | +| GuardsStressTest.cs:162:4:162:5 | access to field ch | +| GuardsStressTest.cs:162:18:162:19 | access to field ch | +| GuardsStressTest.cs:163:4:163:5 | access to field ch | +| GuardsStressTest.cs:163:18:163:19 | access to field ch | +| GuardsStressTest.cs:164:4:164:5 | access to field ch | +| GuardsStressTest.cs:164:18:164:19 | access to field ch | +| GuardsStressTest.cs:165:4:165:5 | access to field ch | +| GuardsStressTest.cs:165:18:165:19 | access to field ch | +| GuardsStressTest.cs:166:4:166:5 | access to field ch | +| GuardsStressTest.cs:166:18:166:19 | access to field ch | +| GuardsStressTest.cs:167:4:167:5 | access to field ch | +| GuardsStressTest.cs:167:18:167:19 | access to field ch | +| GuardsStressTest.cs:168:4:168:5 | access to field ch | +| GuardsStressTest.cs:168:18:168:19 | access to field ch | +| GuardsStressTest.cs:169:4:169:5 | access to field ch | +| GuardsStressTest.cs:169:18:169:19 | access to field ch | +| GuardsStressTest.cs:170:4:170:5 | access to field ch | +| GuardsStressTest.cs:170:18:170:19 | access to field ch | +| GuardsStressTest.cs:171:4:171:5 | access to field ch | +| GuardsStressTest.cs:172:4:172:5 | access to field ch | +| GuardsStressTest.cs:172:18:172:19 | access to field ch | +| GuardsStressTest.cs:173:4:173:5 | access to field ch | +| GuardsStressTest.cs:173:18:173:19 | access to field ch | +| GuardsStressTest.cs:174:4:174:5 | access to field ch | +| GuardsStressTest.cs:174:18:174:19 | access to field ch | +| GuardsStressTest.cs:175:4:175:5 | access to field ch | +| GuardsStressTest.cs:175:18:175:19 | access to field ch | +| GuardsStressTest.cs:176:4:176:5 | access to field ch | +| GuardsStressTest.cs:176:18:176:19 | access to field ch | +| GuardsStressTest.cs:177:4:177:5 | access to field ch | +| GuardsStressTest.cs:177:18:177:19 | access to field ch | +| GuardsStressTest.cs:178:4:178:5 | access to field ch | +| GuardsStressTest.cs:178:18:178:19 | access to field ch | +| GuardsStressTest.cs:179:4:179:5 | access to field ch | +| GuardsStressTest.cs:180:4:180:5 | access to field ch | +| GuardsStressTest.cs:180:18:180:19 | access to field ch | +| GuardsStressTest.cs:181:4:181:5 | access to field ch | +| GuardsStressTest.cs:182:4:182:5 | access to field ch | +| GuardsStressTest.cs:182:18:182:19 | access to field ch | +| GuardsStressTest.cs:183:4:183:5 | access to field ch | +| GuardsStressTest.cs:184:4:184:5 | access to field ch | +| GuardsStressTest.cs:184:18:184:19 | access to field ch | +| GuardsStressTest.cs:185:4:185:5 | access to field ch | +| GuardsStressTest.cs:185:18:185:19 | access to field ch | +| GuardsStressTest.cs:186:4:186:5 | access to field ch | +| GuardsStressTest.cs:186:18:186:19 | access to field ch | +| GuardsStressTest.cs:187:4:187:5 | access to field ch | +| GuardsStressTest.cs:187:18:187:19 | access to field ch | +| GuardsStressTest.cs:188:4:188:5 | access to field ch | +| GuardsStressTest.cs:188:18:188:19 | access to field ch | +| GuardsStressTest.cs:189:4:189:5 | access to field ch | +| GuardsStressTest.cs:189:18:189:19 | access to field ch | +| GuardsStressTest.cs:190:4:190:5 | access to field ch | +| GuardsStressTest.cs:191:4:191:5 | access to field ch | +| GuardsStressTest.cs:191:18:191:19 | access to field ch | +| GuardsStressTest.cs:192:4:192:5 | access to field ch | +| GuardsStressTest.cs:193:4:193:5 | access to field ch | +| GuardsStressTest.cs:194:4:194:5 | access to field ch | +| GuardsStressTest.cs:194:18:194:19 | access to field ch | +| GuardsStressTest.cs:195:4:195:5 | access to field ch | +| GuardsStressTest.cs:195:18:195:19 | access to field ch | +| GuardsStressTest.cs:196:4:196:5 | access to field ch | +| GuardsStressTest.cs:196:18:196:19 | access to field ch | +| GuardsStressTest.cs:197:4:197:5 | access to field ch | +| GuardsStressTest.cs:198:4:198:5 | access to field ch | +| GuardsStressTest.cs:199:4:199:5 | access to field ch | +| GuardsStressTest.cs:199:18:199:19 | access to field ch | +| GuardsStressTest.cs:200:4:200:5 | access to field ch | +| GuardsStressTest.cs:200:18:200:19 | access to field ch | +| GuardsStressTest.cs:201:4:201:5 | access to field ch | +| GuardsStressTest.cs:201:18:201:19 | access to field ch | +| GuardsStressTest.cs:202:4:202:5 | access to field ch | +| GuardsStressTest.cs:202:18:202:19 | access to field ch | +| GuardsStressTest.cs:203:4:203:5 | access to field ch | +| GuardsStressTest.cs:204:4:204:5 | access to field ch | +| GuardsStressTest.cs:204:18:204:19 | access to field ch | +| GuardsStressTest.cs:205:4:205:5 | access to field ch | +| GuardsStressTest.cs:205:18:205:19 | access to field ch | +| GuardsStressTest.cs:206:4:206:5 | access to field ch | +| GuardsStressTest.cs:206:18:206:19 | access to field ch | +| GuardsStressTest.cs:207:4:207:5 | access to field ch | +| GuardsStressTest.cs:208:4:208:5 | access to field ch | +| GuardsStressTest.cs:208:18:208:19 | access to field ch | +| GuardsStressTest.cs:209:4:209:5 | access to field ch | +| GuardsStressTest.cs:209:18:209:19 | access to field ch | +| GuardsStressTest.cs:210:4:210:5 | access to field ch | +| GuardsStressTest.cs:211:4:211:5 | access to field ch | +| GuardsStressTest.cs:212:4:212:5 | access to field ch | +| GuardsStressTest.cs:213:4:213:5 | access to field ch | +| GuardsStressTest.cs:213:18:213:19 | access to field ch | +| GuardsStressTest.cs:214:4:214:5 | access to field ch | +| GuardsStressTest.cs:214:18:214:19 | access to field ch | +| GuardsStressTest.cs:215:4:215:5 | access to field ch | +| GuardsStressTest.cs:215:18:215:19 | access to field ch | +| GuardsStressTest.cs:216:4:216:5 | access to field ch | +| GuardsStressTest.cs:216:18:216:19 | access to field ch | +| GuardsStressTest.cs:217:4:217:5 | access to field ch | +| GuardsStressTest.cs:217:18:217:19 | access to field ch | +| GuardsStressTest.cs:218:4:218:5 | access to field ch | +| GuardsStressTest.cs:219:4:219:5 | access to field ch | +| GuardsStressTest.cs:219:18:219:19 | access to field ch | +| GuardsStressTest.cs:220:4:220:5 | access to field ch | +| GuardsStressTest.cs:220:18:220:19 | access to field ch | +| GuardsStressTest.cs:221:4:221:5 | access to field ch | +| GuardsStressTest.cs:221:18:221:19 | access to field ch | +| GuardsStressTest.cs:222:4:222:5 | access to field ch | +| GuardsStressTest.cs:223:4:223:5 | access to field ch | +| GuardsStressTest.cs:224:4:224:5 | access to field ch | +| GuardsStressTest.cs:224:18:224:19 | access to field ch | +| GuardsStressTest.cs:225:4:225:5 | access to field ch | +| GuardsStressTest.cs:225:18:225:19 | access to field ch | +| GuardsStressTest.cs:226:4:226:5 | access to field ch | +| GuardsStressTest.cs:226:18:226:19 | access to field ch | +| GuardsStressTest.cs:227:4:227:5 | access to field ch | +| GuardsStressTest.cs:227:18:227:19 | access to field ch | +| GuardsStressTest.cs:228:4:228:5 | access to field ch | +| GuardsStressTest.cs:229:4:229:5 | access to field ch | +| GuardsStressTest.cs:229:18:229:19 | access to field ch | +| GuardsStressTest.cs:230:4:230:5 | access to field ch | +| GuardsStressTest.cs:230:18:230:19 | access to field ch | +| GuardsStressTest.cs:231:4:231:5 | access to field ch | +| GuardsStressTest.cs:231:18:231:19 | access to field ch | +| GuardsStressTest.cs:232:4:232:5 | access to field ch | +| GuardsStressTest.cs:232:18:232:19 | access to field ch | +| GuardsStressTest.cs:233:4:233:5 | access to field ch | +| GuardsStressTest.cs:233:18:233:19 | access to field ch | +| GuardsStressTest.cs:234:4:234:5 | access to field ch | +| GuardsStressTest.cs:234:18:234:19 | access to field ch | +| GuardsStressTest.cs:235:4:235:5 | access to field ch | +| GuardsStressTest.cs:236:4:236:5 | access to field ch | +| GuardsStressTest.cs:236:18:236:19 | access to field ch | +| GuardsStressTest.cs:237:4:237:5 | access to field ch | +| GuardsStressTest.cs:237:18:237:19 | access to field ch | +| GuardsStressTest.cs:238:4:238:5 | access to field ch | +| GuardsStressTest.cs:238:18:238:19 | access to field ch | +| GuardsStressTest.cs:239:4:239:5 | access to field ch | +| GuardsStressTest.cs:239:18:239:19 | access to field ch | +| GuardsStressTest.cs:240:4:240:5 | access to field ch | +| GuardsStressTest.cs:240:18:240:19 | access to field ch | +| GuardsStressTest.cs:241:4:241:5 | access to field ch | +| GuardsStressTest.cs:241:18:241:19 | access to field ch | +| GuardsStressTest.cs:242:4:242:5 | access to field ch | +| GuardsStressTest.cs:242:18:242:19 | access to field ch | +| GuardsStressTest.cs:243:4:243:5 | access to field ch | +| GuardsStressTest.cs:243:18:243:19 | access to field ch | +| GuardsStressTest.cs:244:4:244:5 | access to field ch | +| GuardsStressTest.cs:244:18:244:19 | access to field ch | +| GuardsStressTest.cs:245:4:245:5 | access to field ch | +| GuardsStressTest.cs:245:18:245:19 | access to field ch | +| GuardsStressTest.cs:246:4:246:5 | access to field ch | +| GuardsStressTest.cs:246:18:246:19 | access to field ch | +| GuardsStressTest.cs:247:4:247:5 | access to field ch | +| GuardsStressTest.cs:247:18:247:19 | access to field ch | +| GuardsStressTest.cs:248:4:248:5 | access to field ch | +| GuardsStressTest.cs:248:18:248:19 | access to field ch | +| GuardsStressTest.cs:249:4:249:5 | access to field ch | +| GuardsStressTest.cs:249:18:249:19 | access to field ch | +| GuardsStressTest.cs:250:4:250:5 | access to field ch | +| GuardsStressTest.cs:250:18:250:19 | access to field ch | +| GuardsStressTest.cs:251:4:251:5 | access to field ch | +| GuardsStressTest.cs:251:18:251:19 | access to field ch | +| GuardsStressTest.cs:252:4:252:5 | access to field ch | +| GuardsStressTest.cs:252:18:252:19 | access to field ch | +| GuardsStressTest.cs:253:4:253:5 | access to field ch | +| GuardsStressTest.cs:253:18:253:19 | access to field ch | +| GuardsStressTest.cs:254:4:254:5 | access to field ch | +| GuardsStressTest.cs:254:18:254:19 | access to field ch | +| GuardsStressTest.cs:255:4:255:5 | access to field ch | +| GuardsStressTest.cs:255:18:255:19 | access to field ch | +| GuardsStressTest.cs:256:4:256:5 | access to field ch | +| GuardsStressTest.cs:256:18:256:19 | access to field ch | +| GuardsStressTest.cs:257:4:257:5 | access to field ch | +| GuardsStressTest.cs:258:4:258:5 | access to field ch | +| GuardsStressTest.cs:258:18:258:19 | access to field ch | +| GuardsStressTest.cs:259:4:259:5 | access to field ch | +| GuardsStressTest.cs:259:18:259:19 | access to field ch | +| GuardsStressTest.cs:260:4:260:5 | access to field ch | +| GuardsStressTest.cs:260:18:260:19 | access to field ch | +| GuardsStressTest.cs:261:4:261:5 | access to field ch | +| GuardsStressTest.cs:261:18:261:19 | access to field ch | +| GuardsStressTest.cs:262:4:262:5 | access to field ch | +| GuardsStressTest.cs:262:18:262:19 | access to field ch | +| GuardsStressTest.cs:263:4:263:5 | access to field ch | +| GuardsStressTest.cs:263:18:263:19 | access to field ch | +| GuardsStressTest.cs:264:4:264:5 | access to field ch | +| GuardsStressTest.cs:264:18:264:19 | access to field ch | +| GuardsStressTest.cs:265:4:265:5 | access to field ch | +| GuardsStressTest.cs:265:18:265:19 | access to field ch | +| GuardsStressTest.cs:266:4:266:5 | access to field ch | +| GuardsStressTest.cs:266:18:266:19 | access to field ch | +| GuardsStressTest.cs:267:4:267:5 | access to field ch | +| GuardsStressTest.cs:267:18:267:19 | access to field ch | +| GuardsStressTest.cs:268:4:268:5 | access to field ch | +| GuardsStressTest.cs:268:18:268:19 | access to field ch | +| GuardsStressTest.cs:269:4:269:5 | access to field ch | +| GuardsStressTest.cs:269:18:269:19 | access to field ch | +| GuardsStressTest.cs:270:4:270:5 | access to field ch | +| GuardsStressTest.cs:270:18:270:19 | access to field ch | +| GuardsStressTest.cs:271:4:271:5 | access to field ch | +| GuardsStressTest.cs:271:18:271:19 | access to field ch | +| GuardsStressTest.cs:272:4:272:5 | access to field ch | +| GuardsStressTest.cs:272:18:272:19 | access to field ch | +| GuardsStressTest.cs:273:4:273:5 | access to field ch | +| GuardsStressTest.cs:273:18:273:19 | access to field ch | +| GuardsStressTest.cs:274:4:274:5 | access to field ch | +| GuardsStressTest.cs:274:18:274:19 | access to field ch | +| GuardsStressTest.cs:275:4:275:5 | access to field ch | +| GuardsStressTest.cs:275:18:275:19 | access to field ch | +| GuardsStressTest.cs:276:4:276:5 | access to field ch | +| GuardsStressTest.cs:276:18:276:19 | access to field ch | +| GuardsStressTest.cs:277:4:277:5 | access to field ch | +| GuardsStressTest.cs:277:18:277:19 | access to field ch | +| GuardsStressTest.cs:278:4:278:5 | access to field ch | +| GuardsStressTest.cs:279:4:279:5 | access to field ch | +| GuardsStressTest.cs:279:18:279:19 | access to field ch | +| GuardsStressTest.cs:280:4:280:5 | access to field ch | +| GuardsStressTest.cs:280:18:280:19 | access to field ch | +| GuardsStressTest.cs:281:4:281:5 | access to field ch | +| GuardsStressTest.cs:281:18:281:19 | access to field ch | +| GuardsStressTest.cs:282:4:282:5 | access to field ch | +| GuardsStressTest.cs:282:18:282:19 | access to field ch | +| GuardsStressTest.cs:283:4:283:5 | access to field ch | +| GuardsStressTest.cs:283:18:283:19 | access to field ch | +| GuardsStressTest.cs:284:4:284:5 | access to field ch | +| GuardsStressTest.cs:284:18:284:19 | access to field ch | +| GuardsStressTest.cs:285:4:285:5 | access to field ch | +| GuardsStressTest.cs:285:18:285:19 | access to field ch | +| GuardsStressTest.cs:286:4:286:5 | access to field ch | +| GuardsStressTest.cs:286:18:286:19 | access to field ch | +| GuardsStressTest.cs:287:4:287:5 | access to field ch | +| GuardsStressTest.cs:287:18:287:19 | access to field ch | +| GuardsStressTest.cs:288:4:288:5 | access to field ch | +| GuardsStressTest.cs:288:18:288:19 | access to field ch | +| GuardsStressTest.cs:289:4:289:5 | access to field ch | +| GuardsStressTest.cs:289:18:289:19 | access to field ch | +| GuardsStressTest.cs:290:4:290:5 | access to field ch | +| GuardsStressTest.cs:290:18:290:19 | access to field ch | +| GuardsStressTest.cs:291:4:291:5 | access to field ch | +| GuardsStressTest.cs:291:18:291:19 | access to field ch | +| GuardsStressTest.cs:292:4:292:5 | access to field ch | +| GuardsStressTest.cs:292:18:292:19 | access to field ch | +| GuardsStressTest.cs:293:4:293:5 | access to field ch | +| GuardsStressTest.cs:293:18:293:19 | access to field ch | +| GuardsStressTest.cs:294:4:294:5 | access to field ch | +| GuardsStressTest.cs:295:4:295:5 | access to field ch | +| GuardsStressTest.cs:296:4:296:5 | access to field ch | +| GuardsStressTest.cs:297:4:297:5 | access to field ch | +| GuardsStressTest.cs:297:18:297:19 | access to field ch | +| GuardsStressTest.cs:298:4:298:5 | access to field ch | +| GuardsStressTest.cs:298:18:298:19 | access to field ch | +| GuardsStressTest.cs:299:4:299:5 | access to field ch | +| GuardsStressTest.cs:299:18:299:19 | access to field ch | +| GuardsStressTest.cs:300:4:300:5 | access to field ch | +| GuardsStressTest.cs:301:4:301:5 | access to field ch | +| GuardsStressTest.cs:301:18:301:19 | access to field ch | +| GuardsStressTest.cs:302:4:302:5 | access to field ch | +| GuardsStressTest.cs:302:18:302:19 | access to field ch | +| GuardsStressTest.cs:303:4:303:5 | access to field ch | +| GuardsStressTest.cs:303:18:303:19 | access to field ch | +| GuardsStressTest.cs:304:4:304:5 | access to field ch | +| GuardsStressTest.cs:304:18:304:19 | access to field ch | +| GuardsStressTest.cs:305:4:305:5 | access to field ch | +| GuardsStressTest.cs:305:18:305:19 | access to field ch | +| GuardsStressTest.cs:306:4:306:5 | access to field ch | +| GuardsStressTest.cs:306:18:306:19 | access to field ch | +| GuardsStressTest.cs:307:4:307:5 | access to field ch | +| GuardsStressTest.cs:307:18:307:19 | access to field ch | +| GuardsStressTest.cs:308:4:308:5 | access to field ch | +| GuardsStressTest.cs:308:18:308:19 | access to field ch | +| GuardsStressTest.cs:309:4:309:5 | access to field ch | +| GuardsStressTest.cs:309:18:309:19 | access to field ch | +| GuardsStressTest.cs:310:4:310:5 | access to field ch | +| GuardsStressTest.cs:311:4:311:5 | access to field ch | +| GuardsStressTest.cs:312:4:312:5 | access to field ch | +| GuardsStressTest.cs:313:4:313:5 | access to field ch | +| GuardsStressTest.cs:313:18:313:19 | access to field ch | +| GuardsStressTest.cs:314:4:314:5 | access to field ch | +| GuardsStressTest.cs:314:18:314:19 | access to field ch | +| GuardsStressTest.cs:315:4:315:5 | access to field ch | +| GuardsStressTest.cs:316:4:316:5 | access to field ch | +| GuardsStressTest.cs:316:18:316:19 | access to field ch | +| GuardsStressTest.cs:317:4:317:5 | access to field ch | +| GuardsStressTest.cs:318:4:318:5 | access to field ch | +| GuardsStressTest.cs:319:4:319:5 | access to field ch | +| GuardsStressTest.cs:319:18:319:19 | access to field ch | +| GuardsStressTest.cs:320:4:320:5 | access to field ch | +| GuardsStressTest.cs:321:4:321:5 | access to field ch | +| GuardsStressTest.cs:321:18:321:19 | access to field ch | +| GuardsStressTest.cs:322:4:322:5 | access to field ch | +| GuardsStressTest.cs:323:4:323:5 | access to field ch | +| GuardsStressTest.cs:324:4:324:5 | access to field ch | +| GuardsStressTest.cs:325:4:325:5 | access to field ch | +| GuardsStressTest.cs:325:18:325:19 | access to field ch | +| GuardsStressTest.cs:326:4:326:5 | access to field ch | +| GuardsStressTest.cs:326:18:326:19 | access to field ch | +| GuardsStressTest.cs:327:4:327:5 | access to field ch | +| GuardsStressTest.cs:327:18:327:19 | access to field ch | +| GuardsStressTest.cs:328:4:328:5 | access to field ch | +| GuardsStressTest.cs:328:18:328:19 | access to field ch | +| GuardsStressTest.cs:329:4:329:5 | access to field ch | +| GuardsStressTest.cs:330:4:330:5 | access to field ch | +| GuardsStressTest.cs:330:18:330:19 | access to field ch | +| GuardsStressTest.cs:331:4:331:5 | access to field ch | +| GuardsStressTest.cs:331:19:331:20 | access to field ch | +| GuardsStressTest.cs:332:4:332:5 | access to field ch | +| GuardsStressTest.cs:332:19:332:20 | access to field ch | +| GuardsStressTest.cs:333:4:333:5 | access to field ch | +| GuardsStressTest.cs:333:19:333:20 | access to field ch | +| GuardsStressTest.cs:334:4:334:5 | access to field ch | +| GuardsStressTest.cs:334:19:334:20 | access to field ch | +| GuardsStressTest.cs:335:4:335:5 | access to field ch | +| GuardsStressTest.cs:335:19:335:20 | access to field ch | +| GuardsStressTest.cs:336:4:336:5 | access to field ch | +| GuardsStressTest.cs:337:4:337:5 | access to field ch | +| GuardsStressTest.cs:338:4:338:5 | access to field ch | +| GuardsStressTest.cs:338:19:338:20 | access to field ch | +| GuardsStressTest.cs:339:4:339:5 | access to field ch | +| GuardsStressTest.cs:340:4:340:5 | access to field ch | +| GuardsStressTest.cs:340:19:340:20 | access to field ch | +| GuardsStressTest.cs:341:4:341:5 | access to field ch | +| GuardsStressTest.cs:341:19:341:20 | access to field ch | +| GuardsStressTest.cs:342:4:342:5 | access to field ch | +| GuardsStressTest.cs:342:19:342:20 | access to field ch | +| GuardsStressTest.cs:343:4:343:5 | access to field ch | +| GuardsStressTest.cs:343:19:343:20 | access to field ch | +| GuardsStressTest.cs:344:4:344:5 | access to field ch | +| GuardsStressTest.cs:344:19:344:20 | access to field ch | +| GuardsStressTest.cs:345:4:345:5 | access to field ch | +| GuardsStressTest.cs:345:19:345:20 | access to field ch | +| GuardsStressTest.cs:346:4:346:5 | access to field ch | +| GuardsStressTest.cs:346:19:346:20 | access to field ch | +| GuardsStressTest.cs:347:4:347:5 | access to field ch | +| GuardsStressTest.cs:347:19:347:20 | access to field ch | +| GuardsStressTest.cs:348:4:348:5 | access to field ch | +| GuardsStressTest.cs:348:19:348:20 | access to field ch | +| GuardsStressTest.cs:349:4:349:5 | access to field ch | +| GuardsStressTest.cs:349:19:349:20 | access to field ch | +| GuardsStressTest.cs:350:4:350:5 | access to field ch | +| GuardsStressTest.cs:351:4:351:5 | access to field ch | +| GuardsStressTest.cs:351:19:351:20 | access to field ch | +| GuardsStressTest.cs:352:4:352:5 | access to field ch | +| GuardsStressTest.cs:352:19:352:20 | access to field ch | +| GuardsStressTest.cs:353:4:353:5 | access to field ch | +| GuardsStressTest.cs:353:19:353:20 | access to field ch | +| GuardsStressTest.cs:354:4:354:5 | access to field ch | +| GuardsStressTest.cs:354:19:354:20 | access to field ch | +| GuardsStressTest.cs:355:4:355:5 | access to field ch | +| GuardsStressTest.cs:355:19:355:20 | access to field ch | +| GuardsStressTest.cs:356:4:356:5 | access to field ch | +| GuardsStressTest.cs:356:19:356:20 | access to field ch | +| GuardsStressTest.cs:357:4:357:5 | access to field ch | +| GuardsStressTest.cs:357:19:357:20 | access to field ch | +| GuardsStressTest.cs:358:4:358:5 | access to field ch | +| GuardsStressTest.cs:358:19:358:20 | access to field ch | +| GuardsStressTest.cs:359:4:359:5 | access to field ch | +| GuardsStressTest.cs:359:19:359:20 | access to field ch | +| GuardsStressTest.cs:360:4:360:5 | access to field ch | +| GuardsStressTest.cs:360:19:360:20 | access to field ch | +| GuardsStressTest.cs:361:4:361:5 | access to field ch | +| GuardsStressTest.cs:361:19:361:20 | access to field ch | +| GuardsStressTest.cs:362:4:362:5 | access to field ch | +| GuardsStressTest.cs:362:19:362:20 | access to field ch | +| GuardsStressTest.cs:363:4:363:5 | access to field ch | +| GuardsStressTest.cs:363:19:363:20 | access to field ch | +| GuardsStressTest.cs:364:4:364:5 | access to field ch | +| GuardsStressTest.cs:364:19:364:20 | access to field ch | +| GuardsStressTest.cs:365:4:365:5 | access to field ch | +| GuardsStressTest.cs:365:19:365:20 | access to field ch | +| GuardsStressTest.cs:366:4:366:5 | access to field ch | +| GuardsStressTest.cs:366:19:366:20 | access to field ch | +| GuardsStressTest.cs:367:4:367:5 | access to field ch | +| GuardsStressTest.cs:367:19:367:20 | access to field ch | +| GuardsStressTest.cs:368:4:368:5 | access to field ch | +| GuardsStressTest.cs:368:19:368:20 | access to field ch | +| GuardsStressTest.cs:369:4:369:5 | access to field ch | +| GuardsStressTest.cs:369:19:369:20 | access to field ch | +| GuardsStressTest.cs:370:4:370:5 | access to field ch | +| GuardsStressTest.cs:370:19:370:20 | access to field ch | +| GuardsStressTest.cs:371:4:371:5 | access to field ch | +| GuardsStressTest.cs:371:19:371:20 | access to field ch | +| GuardsStressTest.cs:372:4:372:5 | access to field ch | +| GuardsStressTest.cs:372:19:372:20 | access to field ch | +| GuardsStressTest.cs:373:4:373:5 | access to field ch | +| GuardsStressTest.cs:373:19:373:20 | access to field ch | +| GuardsStressTest.cs:374:4:374:5 | access to field ch | +| GuardsStressTest.cs:374:19:374:20 | access to field ch | +| GuardsStressTest.cs:375:4:375:5 | access to field ch | +| GuardsStressTest.cs:375:19:375:20 | access to field ch | +| GuardsStressTest.cs:376:4:376:5 | access to field ch | +| GuardsStressTest.cs:376:19:376:20 | access to field ch | +| GuardsStressTest.cs:377:4:377:5 | access to field ch | +| GuardsStressTest.cs:377:19:377:20 | access to field ch | +| GuardsStressTest.cs:378:4:378:5 | access to field ch | +| GuardsStressTest.cs:378:19:378:20 | access to field ch | +| GuardsStressTest.cs:379:4:379:5 | access to field ch | +| GuardsStressTest.cs:379:19:379:20 | access to field ch | +| GuardsStressTest.cs:380:4:380:5 | access to field ch | +| GuardsStressTest.cs:380:19:380:20 | access to field ch | +| GuardsStressTest.cs:381:4:381:5 | access to field ch | +| GuardsStressTest.cs:381:19:381:20 | access to field ch | +| GuardsStressTest.cs:382:4:382:5 | access to field ch | +| GuardsStressTest.cs:382:19:382:20 | access to field ch | +| GuardsStressTest.cs:383:4:383:5 | access to field ch | +| GuardsStressTest.cs:383:19:383:20 | access to field ch | +| GuardsStressTest.cs:384:4:384:5 | access to field ch | +| GuardsStressTest.cs:385:4:385:5 | access to field ch | +| GuardsStressTest.cs:385:19:385:20 | access to field ch | +| GuardsStressTest.cs:386:4:386:5 | access to field ch | +| GuardsStressTest.cs:386:19:386:20 | access to field ch | +| GuardsStressTest.cs:387:4:387:5 | access to field ch | +| GuardsStressTest.cs:387:19:387:20 | access to field ch | +| GuardsStressTest.cs:388:4:388:5 | access to field ch | +| GuardsStressTest.cs:388:19:388:20 | access to field ch | +| GuardsStressTest.cs:389:4:389:5 | access to field ch | +| GuardsStressTest.cs:389:19:389:20 | access to field ch | +| GuardsStressTest.cs:390:4:390:5 | access to field ch | +| GuardsStressTest.cs:390:19:390:20 | access to field ch | +| GuardsStressTest.cs:391:4:391:5 | access to field ch | +| GuardsStressTest.cs:391:19:391:20 | access to field ch | +| GuardsStressTest.cs:392:4:392:5 | access to field ch | +| GuardsStressTest.cs:392:19:392:20 | access to field ch | +| GuardsStressTest.cs:393:4:393:5 | access to field ch | +| GuardsStressTest.cs:393:19:393:20 | access to field ch | +| GuardsStressTest.cs:394:4:394:5 | access to field ch | +| GuardsStressTest.cs:394:19:394:20 | access to field ch | +| GuardsStressTest.cs:395:4:395:5 | access to field ch | +| GuardsStressTest.cs:395:19:395:20 | access to field ch | +| GuardsStressTest.cs:396:4:396:5 | access to field ch | +| GuardsStressTest.cs:396:19:396:20 | access to field ch | +| GuardsStressTest.cs:397:4:397:5 | access to field ch | +| GuardsStressTest.cs:397:19:397:20 | access to field ch | +| GuardsStressTest.cs:398:4:398:5 | access to field ch | +| GuardsStressTest.cs:398:19:398:20 | access to field ch | +| GuardsStressTest.cs:399:4:399:5 | access to field ch | +| GuardsStressTest.cs:399:19:399:20 | access to field ch | +| GuardsStressTest.cs:400:4:400:5 | access to field ch | +| GuardsStressTest.cs:400:19:400:20 | access to field ch | +| GuardsStressTest.cs:401:4:401:5 | access to field ch | +| GuardsStressTest.cs:401:19:401:20 | access to field ch | +| GuardsStressTest.cs:402:4:402:5 | access to field ch | +| GuardsStressTest.cs:402:19:402:20 | access to field ch | +| GuardsStressTest.cs:403:4:403:5 | access to field ch | +| GuardsStressTest.cs:403:19:403:20 | access to field ch | +| GuardsStressTest.cs:404:4:404:5 | access to field ch | +| GuardsStressTest.cs:404:19:404:20 | access to field ch | +| GuardsStressTest.cs:405:4:405:5 | access to field ch | +| GuardsStressTest.cs:405:19:405:20 | access to field ch | +| GuardsStressTest.cs:406:4:406:5 | access to field ch | +| GuardsStressTest.cs:406:19:406:20 | access to field ch | +| GuardsStressTest.cs:407:4:407:5 | access to field ch | +| GuardsStressTest.cs:407:19:407:20 | access to field ch | +| GuardsStressTest.cs:408:4:408:5 | access to field ch | +| GuardsStressTest.cs:408:19:408:20 | access to field ch | +| GuardsStressTest.cs:409:4:409:5 | access to field ch | +| GuardsStressTest.cs:409:19:409:20 | access to field ch | +| GuardsStressTest.cs:410:4:410:5 | access to field ch | +| GuardsStressTest.cs:410:19:410:20 | access to field ch | +| GuardsStressTest.cs:411:4:411:5 | access to field ch | +| GuardsStressTest.cs:411:19:411:20 | access to field ch | +| GuardsStressTest.cs:412:4:412:5 | access to field ch | +| GuardsStressTest.cs:412:19:412:20 | access to field ch | +| GuardsStressTest.cs:413:4:413:5 | access to field ch | +| GuardsStressTest.cs:413:19:413:20 | access to field ch | +| GuardsStressTest.cs:414:4:414:5 | access to field ch | +| GuardsStressTest.cs:414:19:414:20 | access to field ch | +| GuardsStressTest.cs:415:4:415:5 | access to field ch | +| GuardsStressTest.cs:415:19:415:20 | access to field ch | +| GuardsStressTest.cs:416:4:416:5 | access to field ch | +| GuardsStressTest.cs:416:19:416:20 | access to field ch | +| GuardsStressTest.cs:417:4:417:5 | access to field ch | +| GuardsStressTest.cs:418:4:418:5 | access to field ch | +| GuardsStressTest.cs:418:19:418:20 | access to field ch | +| GuardsStressTest.cs:419:4:419:5 | access to field ch | +| GuardsStressTest.cs:419:19:419:20 | access to field ch | +| GuardsStressTest.cs:420:4:420:5 | access to field ch | +| GuardsStressTest.cs:420:19:420:20 | access to field ch | +| GuardsStressTest.cs:421:4:421:5 | access to field ch | +| GuardsStressTest.cs:421:19:421:20 | access to field ch | +| GuardsStressTest.cs:422:4:422:5 | access to field ch | +| GuardsStressTest.cs:422:19:422:20 | access to field ch | +| GuardsStressTest.cs:423:4:423:5 | access to field ch | +| GuardsStressTest.cs:423:19:423:20 | access to field ch | +| GuardsStressTest.cs:424:4:424:5 | access to field ch | +| GuardsStressTest.cs:424:19:424:20 | access to field ch | +| GuardsStressTest.cs:425:4:425:5 | access to field ch | +| GuardsStressTest.cs:425:19:425:20 | access to field ch | +| GuardsStressTest.cs:426:4:426:5 | access to field ch | +| GuardsStressTest.cs:426:19:426:20 | access to field ch | +| GuardsStressTest.cs:427:4:427:5 | access to field ch | +| GuardsStressTest.cs:427:19:427:20 | access to field ch | +| GuardsStressTest.cs:428:4:428:5 | access to field ch | +| GuardsStressTest.cs:428:19:428:20 | access to field ch | +| GuardsStressTest.cs:429:4:429:5 | access to field ch | +| GuardsStressTest.cs:429:19:429:20 | access to field ch | +| GuardsStressTest.cs:430:4:430:5 | access to field ch | +| GuardsStressTest.cs:430:19:430:20 | access to field ch | +| GuardsStressTest.cs:431:4:431:5 | access to field ch | +| GuardsStressTest.cs:431:19:431:20 | access to field ch | +| GuardsStressTest.cs:432:4:432:5 | access to field ch | +| GuardsStressTest.cs:432:19:432:20 | access to field ch | +| GuardsStressTest.cs:433:4:433:5 | access to field ch | +| GuardsStressTest.cs:434:4:434:5 | access to field ch | +| GuardsStressTest.cs:434:19:434:20 | access to field ch | +| GuardsStressTest.cs:435:4:435:5 | access to field ch | +| GuardsStressTest.cs:435:19:435:20 | access to field ch | +| GuardsStressTest.cs:436:4:436:5 | access to field ch | +| GuardsStressTest.cs:436:19:436:20 | access to field ch | +| GuardsStressTest.cs:437:4:437:5 | access to field ch | +| GuardsStressTest.cs:437:19:437:20 | access to field ch | +| GuardsStressTest.cs:438:4:438:5 | access to field ch | +| GuardsStressTest.cs:438:19:438:20 | access to field ch | +| GuardsStressTest.cs:439:4:439:5 | access to field ch | +| GuardsStressTest.cs:439:19:439:20 | access to field ch | +| GuardsStressTest.cs:439:41:439:42 | access to field ch | +| GuardsStressTest.cs:440:23:440:24 | access to field ch | diff --git a/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.ql b/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.ql new file mode 100644 index 000000000000..dbb472735b4d --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.ql @@ -0,0 +1,5 @@ +import csharp +import semmle.code.csharp.controlflow.Guards + +from GuardedExpr ge +select ge diff --git a/csharp/ql/test/library-tests/controlflow/guards-large/GuardsStressTest.cs b/csharp/ql/test/library-tests/controlflow/guards-large/GuardsStressTest.cs new file mode 100644 index 000000000000..200c51d89fd3 --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/guards-large/GuardsStressTest.cs @@ -0,0 +1,443 @@ +using System; +#nullable enable +public class StressTest +{ + int ch; + int Ors() + { + if (ch >= '0' && ch <= '9' +|| ch >= 'A' && ch <= 'Z' +|| ch == '_' +|| ch >= 'a' && ch <= 'z' +|| ch == 170 +|| ch == 181 +|| ch == 186 +|| ch >= 192 && ch <= 214 +|| ch >= 216 && ch <= 246 +|| ch >= 248 && ch <= 705 +|| ch >= 710 && ch <= 721 +|| ch >= 736 && ch <= 740 +|| ch == 748 +|| ch == 750 +|| ch >= 768 && ch <= 884 +|| ch >= 886 && ch <= 887 +|| ch >= 890 && ch <= 893 +|| ch == 902 +|| ch >= 904 && ch <= 906 +|| ch == 908 +|| ch >= 910 && ch <= 929 +|| ch >= 931 && ch <= 1013 +|| ch >= 1015 && ch <= 1153 +|| ch >= 1155 && ch <= 1159 +|| ch >= 1162 && ch <= 1319 +|| ch >= 1329 && ch <= 1366 +|| ch == 1369 +|| ch >= 1377 && ch <= 1415 +|| ch >= 1425 && ch <= 1469 +|| ch == 1471 +|| ch >= 1473 && ch <= 1474 +|| ch >= 1476 && ch <= 1477 +|| ch == 1479 +|| ch >= 1488 && ch <= 1514 +|| ch >= 1520 && ch <= 1522 +|| ch >= 1552 && ch <= 1562 +|| ch >= 1568 && ch <= 1641 +|| ch >= 1646 && ch <= 1747 +|| ch >= 1749 && ch <= 1756 +|| ch >= 1759 && ch <= 1768 +|| ch >= 1770 && ch <= 1788 +|| ch == 1791 +|| ch >= 1808 && ch <= 1866 +|| ch >= 1869 && ch <= 1969 +|| ch >= 1984 && ch <= 2037 +|| ch == 2042 +|| ch >= 2048 && ch <= 2093 +|| ch >= 2112 && ch <= 2139 +|| ch == 2208 +|| ch >= 2210 && ch <= 2220 +|| ch >= 2276 && ch <= 2302 +|| ch >= 2304 && ch <= 2403 +|| ch >= 2406 && ch <= 2415 +|| ch >= 2417 && ch <= 2423 +|| ch >= 2425 && ch <= 2431 +|| ch >= 2433 && ch <= 2435 +|| ch >= 2437 && ch <= 2444 +|| ch >= 2447 && ch <= 2448 +|| ch >= 2451 && ch <= 2472 +|| ch >= 2474 && ch <= 2480 +|| ch == 2482 +|| ch >= 2486 && ch <= 2489 +|| ch >= 2492 && ch <= 2500 +|| ch >= 2503 && ch <= 2504 +|| ch >= 2507 && ch <= 2510 +|| ch == 2519 +|| ch >= 2524 && ch <= 2525 +|| ch >= 2527 && ch <= 2531 +|| ch >= 2534 && ch <= 2545 +|| ch >= 2561 && ch <= 2563 +|| ch >= 2565 && ch <= 2570 +|| ch >= 2575 && ch <= 2576 +|| ch >= 2579 && ch <= 2600 +|| ch >= 2602 && ch <= 2608 +|| ch >= 2610 && ch <= 2611 +|| ch >= 2613 && ch <= 2614 +|| ch >= 2616 && ch <= 2617 +|| ch == 2620 +|| ch >= 2622 && ch <= 2626 +|| ch >= 2631 && ch <= 2632 +|| ch >= 2635 && ch <= 2637 +|| ch == 2641 +|| ch >= 2649 && ch <= 2652 +|| ch == 2654 +|| ch >= 2662 && ch <= 2677 +|| ch >= 2689 && ch <= 2691 +|| ch >= 2693 && ch <= 2701 +|| ch >= 2703 && ch <= 2705 +|| ch >= 2707 && ch <= 2728 +|| ch >= 2730 && ch <= 2736 +|| ch >= 2738 && ch <= 2739 +|| ch >= 2741 && ch <= 2745 +|| ch >= 2748 && ch <= 2757 +|| ch >= 2759 && ch <= 2761 +|| ch >= 2763 && ch <= 2765 +|| ch == 2768 +|| ch >= 2784 && ch <= 2787 +|| ch >= 2790 && ch <= 2799 +|| ch >= 2817 && ch <= 2819 +|| ch >= 2821 && ch <= 2828 +|| ch >= 2831 && ch <= 2832 +|| ch >= 2835 && ch <= 2856 +|| ch >= 2858 && ch <= 2864 +|| ch >= 2866 && ch <= 2867 +|| ch >= 2869 && ch <= 2873 +|| ch >= 2876 && ch <= 2884 +|| ch >= 2887 && ch <= 2888 +|| ch >= 2891 && ch <= 2893 +|| ch >= 2902 && ch <= 2903 +|| ch >= 2908 && ch <= 2909 +|| ch >= 2911 && ch <= 2915 +|| ch >= 2918 && ch <= 2927 +|| ch == 2929 +|| ch >= 2946 && ch <= 2947 +|| ch >= 2949 && ch <= 2954 +|| ch >= 2958 && ch <= 2960 +|| ch >= 2962 && ch <= 2965 +|| ch >= 2969 && ch <= 2970 +|| ch == 2972 +|| ch >= 2974 && ch <= 2975 +|| ch >= 2979 && ch <= 2980 +|| ch >= 2984 && ch <= 2986 +|| ch >= 2990 && ch <= 3001 +|| ch >= 3006 && ch <= 3010 +|| ch >= 3014 && ch <= 3016 +|| ch >= 3018 && ch <= 3021 +|| ch == 3024 +|| ch == 3031 +|| ch >= 3046 && ch <= 3055 +|| ch >= 3073 && ch <= 3075 +|| ch >= 3077 && ch <= 3084 +|| ch >= 3086 && ch <= 3088 +|| ch >= 3090 && ch <= 3112 +|| ch >= 3114 && ch <= 3123 +|| ch >= 3125 && ch <= 3129 +|| ch >= 3133 && ch <= 3140 +|| ch >= 3142 && ch <= 3144 +|| ch >= 3146 && ch <= 3149 +|| ch >= 3157 && ch <= 3158 +|| ch >= 3160 && ch <= 3161 +|| ch >= 3168 && ch <= 3171 +|| ch >= 3174 && ch <= 3183 +|| ch >= 3202 && ch <= 3203 +|| ch >= 3205 && ch <= 3212 +|| ch >= 3214 && ch <= 3216 +|| ch >= 3218 && ch <= 3240 +|| ch >= 3242 && ch <= 3251 +|| ch >= 3253 && ch <= 3257 +|| ch >= 3260 && ch <= 3268 +|| ch >= 3270 && ch <= 3272 +|| ch >= 3274 && ch <= 3277 +|| ch >= 3285 && ch <= 3286 +|| ch == 3294 +|| ch >= 3296 && ch <= 3299 +|| ch >= 3302 && ch <= 3311 +|| ch >= 3313 && ch <= 3314 +|| ch >= 3330 && ch <= 3331 +|| ch >= 3333 && ch <= 3340 +|| ch >= 3342 && ch <= 3344 +|| ch >= 3346 && ch <= 3386 +|| ch >= 3389 && ch <= 3396 +|| ch >= 3398 && ch <= 3400 +|| ch >= 3402 && ch <= 3406 +|| ch == 3415 +|| ch >= 3424 && ch <= 3427 +|| ch >= 3430 && ch <= 3439 +|| ch >= 3450 && ch <= 3455 +|| ch >= 3458 && ch <= 3459 +|| ch >= 3461 && ch <= 3478 +|| ch >= 3482 && ch <= 3505 +|| ch >= 3507 && ch <= 3515 +|| ch == 3517 +|| ch >= 3520 && ch <= 3526 +|| ch == 3530 +|| ch >= 3535 && ch <= 3540 +|| ch == 3542 +|| ch >= 3544 && ch <= 3551 +|| ch >= 3570 && ch <= 3571 +|| ch >= 3585 && ch <= 3642 +|| ch >= 3648 && ch <= 3662 +|| ch >= 3664 && ch <= 3673 +|| ch >= 3713 && ch <= 3714 +|| ch == 3716 +|| ch >= 3719 && ch <= 3720 +|| ch == 3722 +|| ch == 3725 +|| ch >= 3732 && ch <= 3735 +|| ch >= 3737 && ch <= 3743 +|| ch >= 3745 && ch <= 3747 +|| ch == 3749 +|| ch == 3751 +|| ch >= 3754 && ch <= 3755 +|| ch >= 3757 && ch <= 3769 +|| ch >= 3771 && ch <= 3773 +|| ch >= 3776 && ch <= 3780 +|| ch == 3782 +|| ch >= 3784 && ch <= 3789 +|| ch >= 3792 && ch <= 3801 +|| ch >= 3804 && ch <= 3807 +|| ch == 3840 +|| ch >= 3864 && ch <= 3865 +|| ch >= 3872 && ch <= 3881 +|| ch == 3893 +|| ch == 3895 +|| ch == 3897 +|| ch >= 3902 && ch <= 3911 +|| ch >= 3913 && ch <= 3948 +|| ch >= 3953 && ch <= 3972 +|| ch >= 3974 && ch <= 3991 +|| ch >= 3993 && ch <= 4028 +|| ch == 4038 +|| ch >= 4096 && ch <= 4169 +|| ch >= 4176 && ch <= 4253 +|| ch >= 4256 && ch <= 4293 +|| ch == 4295 +|| ch == 4301 +|| ch >= 4304 && ch <= 4346 +|| ch >= 4348 && ch <= 4680 +|| ch >= 4682 && ch <= 4685 +|| ch >= 4688 && ch <= 4694 +|| ch == 4696 +|| ch >= 4698 && ch <= 4701 +|| ch >= 4704 && ch <= 4744 +|| ch >= 4746 && ch <= 4749 +|| ch >= 4752 && ch <= 4784 +|| ch >= 4786 && ch <= 4789 +|| ch >= 4792 && ch <= 4798 +|| ch == 4800 +|| ch >= 4802 && ch <= 4805 +|| ch >= 4808 && ch <= 4822 +|| ch >= 4824 && ch <= 4880 +|| ch >= 4882 && ch <= 4885 +|| ch >= 4888 && ch <= 4954 +|| ch >= 4957 && ch <= 4959 +|| ch >= 4992 && ch <= 5007 +|| ch >= 5024 && ch <= 5108 +|| ch >= 5121 && ch <= 5740 +|| ch >= 5743 && ch <= 5759 +|| ch >= 5761 && ch <= 5786 +|| ch >= 5792 && ch <= 5866 +|| ch >= 5870 && ch <= 5872 +|| ch >= 5888 && ch <= 5900 +|| ch >= 5902 && ch <= 5908 +|| ch >= 5920 && ch <= 5940 +|| ch >= 5952 && ch <= 5971 +|| ch >= 5984 && ch <= 5996 +|| ch >= 5998 && ch <= 6000 +|| ch >= 6002 && ch <= 6003 +|| ch >= 6016 && ch <= 6099 +|| ch == 6103 +|| ch >= 6108 && ch <= 6109 +|| ch >= 6112 && ch <= 6121 +|| ch >= 6155 && ch <= 6157 +|| ch >= 6160 && ch <= 6169 +|| ch >= 6176 && ch <= 6263 +|| ch >= 6272 && ch <= 6314 +|| ch >= 6320 && ch <= 6389 +|| ch >= 6400 && ch <= 6428 +|| ch >= 6432 && ch <= 6443 +|| ch >= 6448 && ch <= 6459 +|| ch >= 6470 && ch <= 6509 +|| ch >= 6512 && ch <= 6516 +|| ch >= 6528 && ch <= 6571 +|| ch >= 6576 && ch <= 6601 +|| ch >= 6608 && ch <= 6617 +|| ch >= 6656 && ch <= 6683 +|| ch >= 6688 && ch <= 6750 +|| ch >= 6752 && ch <= 6780 +|| ch >= 6783 && ch <= 6793 +|| ch >= 6800 && ch <= 6809 +|| ch == 6823 +|| ch >= 6912 && ch <= 6987 +|| ch >= 6992 && ch <= 7001 +|| ch >= 7019 && ch <= 7027 +|| ch >= 7040 && ch <= 7155 +|| ch >= 7168 && ch <= 7223 +|| ch >= 7232 && ch <= 7241 +|| ch >= 7245 && ch <= 7293 +|| ch >= 7376 && ch <= 7378 +|| ch >= 7380 && ch <= 7414 +|| ch >= 7424 && ch <= 7654 +|| ch >= 7676 && ch <= 7957 +|| ch >= 7960 && ch <= 7965 +|| ch >= 7968 && ch <= 8005 +|| ch >= 8008 && ch <= 8013 +|| ch >= 8016 && ch <= 8023 +|| ch == 8025 +|| ch == 8027 +|| ch == 8029 +|| ch >= 8031 && ch <= 8061 +|| ch >= 8064 && ch <= 8116 +|| ch >= 8118 && ch <= 8124 +|| ch == 8126 +|| ch >= 8130 && ch <= 8132 +|| ch >= 8134 && ch <= 8140 +|| ch >= 8144 && ch <= 8147 +|| ch >= 8150 && ch <= 8155 +|| ch >= 8160 && ch <= 8172 +|| ch >= 8178 && ch <= 8180 +|| ch >= 8182 && ch <= 8188 +|| ch >= 8204 && ch <= 8205 +|| ch >= 8255 && ch <= 8256 +|| ch == 8276 +|| ch == 8305 +|| ch == 8319 +|| ch >= 8336 && ch <= 8348 +|| ch >= 8400 && ch <= 8412 +|| ch == 8417 +|| ch >= 8421 && ch <= 8432 +|| ch == 8450 +|| ch == 8455 +|| ch >= 8458 && ch <= 8467 +|| ch == 8469 +|| ch >= 8473 && ch <= 8477 +|| ch == 8484 +|| ch == 8486 +|| ch == 8488 +|| ch >= 8490 && ch <= 8493 +|| ch >= 8495 && ch <= 8505 +|| ch >= 8508 && ch <= 8511 +|| ch >= 8517 && ch <= 8521 +|| ch == 8526 +|| ch >= 8544 && ch <= 8584 +|| ch >= 11264 && ch <= 11310 +|| ch >= 11312 && ch <= 11358 +|| ch >= 11360 && ch <= 11492 +|| ch >= 11499 && ch <= 11507 +|| ch >= 11520 && ch <= 11557 +|| ch == 11559 +|| ch == 11565 +|| ch >= 11568 && ch <= 11623 +|| ch == 11631 +|| ch >= 11647 && ch <= 11670 +|| ch >= 11680 && ch <= 11686 +|| ch >= 11688 && ch <= 11694 +|| ch >= 11696 && ch <= 11702 +|| ch >= 11704 && ch <= 11710 +|| ch >= 11712 && ch <= 11718 +|| ch >= 11720 && ch <= 11726 +|| ch >= 11728 && ch <= 11734 +|| ch >= 11736 && ch <= 11742 +|| ch >= 11744 && ch <= 11775 +|| ch == 11823 +|| ch >= 12293 && ch <= 12295 +|| ch >= 12321 && ch <= 12335 +|| ch >= 12337 && ch <= 12341 +|| ch >= 12344 && ch <= 12348 +|| ch >= 12353 && ch <= 12438 +|| ch >= 12441 && ch <= 12442 +|| ch >= 12445 && ch <= 12447 +|| ch >= 12449 && ch <= 12538 +|| ch >= 12540 && ch <= 12543 +|| ch >= 12549 && ch <= 12589 +|| ch >= 12593 && ch <= 12686 +|| ch >= 12704 && ch <= 12730 +|| ch >= 12784 && ch <= 12799 +|| ch >= 13312 && ch <= 19893 +|| ch >= 19968 && ch <= 40908 +|| ch >= 40960 && ch <= 42124 +|| ch >= 42192 && ch <= 42237 +|| ch >= 42240 && ch <= 42508 +|| ch >= 42512 && ch <= 42539 +|| ch >= 42560 && ch <= 42607 +|| ch >= 42612 && ch <= 42621 +|| ch >= 42623 && ch <= 42647 +|| ch >= 42655 && ch <= 42737 +|| ch >= 42775 && ch <= 42783 +|| ch >= 42786 && ch <= 42888 +|| ch >= 42891 && ch <= 42894 +|| ch >= 42896 && ch <= 42899 +|| ch >= 42912 && ch <= 42922 +|| ch >= 43000 && ch <= 43047 +|| ch >= 43072 && ch <= 43123 +|| ch >= 43136 && ch <= 43204 +|| ch >= 43216 && ch <= 43225 +|| ch >= 43232 && ch <= 43255 +|| ch == 43259 +|| ch >= 43264 && ch <= 43309 +|| ch >= 43312 && ch <= 43347 +|| ch >= 43360 && ch <= 43388 +|| ch >= 43392 && ch <= 43456 +|| ch >= 43471 && ch <= 43481 +|| ch >= 43520 && ch <= 43574 +|| ch >= 43584 && ch <= 43597 +|| ch >= 43600 && ch <= 43609 +|| ch >= 43616 && ch <= 43638 +|| ch >= 43642 && ch <= 43643 +|| ch >= 43648 && ch <= 43714 +|| ch >= 43739 && ch <= 43741 +|| ch >= 43744 && ch <= 43759 +|| ch >= 43762 && ch <= 43766 +|| ch >= 43777 && ch <= 43782 +|| ch >= 43785 && ch <= 43790 +|| ch >= 43793 && ch <= 43798 +|| ch >= 43808 && ch <= 43814 +|| ch >= 43816 && ch <= 43822 +|| ch >= 43968 && ch <= 44010 +|| ch >= 44012 && ch <= 44013 +|| ch >= 44016 && ch <= 44025 +|| ch >= 44032 && ch <= 55203 +|| ch >= 55216 && ch <= 55238 +|| ch >= 55243 && ch <= 55291 +|| ch >= 63744 && ch <= 64109 +|| ch >= 64112 && ch <= 64217 +|| ch >= 64256 && ch <= 64262 +|| ch >= 64275 && ch <= 64279 +|| ch >= 64285 && ch <= 64296 +|| ch >= 64298 && ch <= 64310 +|| ch >= 64312 && ch <= 64316 +|| ch == 64318 +|| ch >= 64320 && ch <= 64321 +|| ch >= 64323 && ch <= 64324 +|| ch >= 64326 && ch <= 64433 +|| ch >= 64467 && ch <= 64829 +|| ch >= 64848 && ch <= 64911 +|| ch >= 64914 && ch <= 64967 +|| ch >= 65008 && ch <= 65019 +|| ch >= 65024 && ch <= 65039 +|| ch >= 65056 && ch <= 65062 +|| ch >= 65075 && ch <= 65076 +|| ch >= 65101 && ch <= 65103 +|| ch >= 65136 && ch <= 65140 +|| ch >= 65142 && ch <= 65276 +|| ch >= 65296 && ch <= 65305 +|| ch >= 65313 && ch <= 65338 +|| ch == 65343 +|| ch >= 65345 && ch <= 65370 +|| ch >= 65382 && ch <= 65470 +|| ch >= 65474 && ch <= 65479 +|| ch >= 65482 && ch <= 65487 +|| ch >= 65490 && ch <= 65495 +|| ch >= 65498 && ch <= 65500) { return ch; } + else { return ch + 1; } + } +} + diff --git a/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected b/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected index 120e3e2c5ced..40d144f37194 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected @@ -5,16 +5,12 @@ | Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:24:52:24 | access to local variable s | false | | Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | false | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true | | Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false | | Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true | | Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false | -| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false | | Assert.cs:94:16:94:17 | access to parameter b1 | Assert.cs:93:25:93:26 | access to parameter b1 | Assert.cs:93:25:93:26 | access to parameter b1 | true | | Assert.cs:94:23:94:24 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | false | | Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | @@ -22,39 +18,17 @@ | Collections.cs:54:13:54:16 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:68:13:68:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:12:13:12:24 | ... > ... | Guards.cs:12:13:12:13 | access to parameter s | true | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:21 | ... != ... | Guards.cs:24:13:24:13 | access to parameter s | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:36 | !... | Guards.cs:32:35:32:35 | access to parameter x | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:35:32:35 | access to parameter x | true | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:14:32:36 | call to method IsNullOrEmpty | Guards.cs:32:35:32:35 | access to parameter x | false | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:42:32:42 | access to parameter y | true | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:40:32:51 | !... | Guards.cs:32:42:32:42 | access to parameter y | true | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:50 | ... == ... | Guards.cs:32:42:32:42 | access to parameter y | false | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:21 | ... == ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:26:35:26 | access to parameter y | false | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:34 | ... == ... | Guards.cs:35:26:35:26 | access to parameter y | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:13:38:37 | !... | Guards.cs:38:15:38:15 | access to parameter x | true | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:23 | ... == ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:13:38:37 | !... | Guards.cs:38:28:38:28 | access to parameter y | true | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:28:38:28 | access to parameter y | false | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:36 | ... == ... | Guards.cs:38:28:38:28 | access to parameter y | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:13:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:14:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:15:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:25 | ... != ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:13:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:14:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:15:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:38 | ... != ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:48:31:48:40 | access to field Field | Guards.cs:47:13:47:25 | ... != ... | Guards.cs:47:13:47:17 | access to field Field | true | | Guards.cs:55:27:55:27 | access to parameter g | Guards.cs:53:13:53:27 | ... == ... | Guards.cs:53:13:53:13 | access to parameter g | false | @@ -85,16 +59,11 @@ | Guards.cs:138:20:138:20 | access to parameter s | Guards.cs:137:13:137:25 | ... is ... | Guards.cs:137:13:137:13 | access to parameter s | true | | Guards.cs:139:16:139:16 | access to parameter s | Guards.cs:137:13:137:25 | ... is ... | Guards.cs:137:13:137:13 | access to parameter s | false | | Guards.cs:146:16:146:16 | access to parameter o | Guards.cs:144:13:144:25 | ... is ... | Guards.cs:144:13:144:13 | access to parameter o | false | -| Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:13:168:41 | !... | Guards.cs:168:40:168:40 | access to parameter x | true | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:14:168:41 | call to method IsNullOrWhiteSpace | Guards.cs:168:40:168:40 | access to parameter x | false | -| Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:13:189:25 | !... | Guards.cs:189:24:189:24 | access to parameter s | true | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:14:189:25 | call to method NullTest1 | Guards.cs:189:24:189:24 | access to parameter s | false | -| Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:13:191:25 | !... | Guards.cs:191:24:191:24 | access to parameter s | true | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:14:191:25 | call to method NullTest2 | Guards.cs:191:24:191:24 | access to parameter s | false | -| Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:13:193:25 | !... | Guards.cs:193:24:193:24 | access to parameter s | true | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:14:193:25 | call to method NullTest3 | Guards.cs:193:24:193:24 | access to parameter s | false | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:13:195:27 | call to method NotNullTest4 | Guards.cs:195:26:195:26 | access to parameter s | true | -| Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:13:197:29 | !... | Guards.cs:197:28:197:28 | access to parameter s | true | | Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:14:197:29 | call to method NullTestWrong | Guards.cs:197:28:197:28 | access to parameter s | false | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:21 | ... != ... | Guards.cs:203:13:203:13 | access to parameter o | true | | Guards.cs:208:17:208:17 | access to parameter o | Guards.cs:203:13:203:21 | ... != ... | Guards.cs:203:13:203:13 | access to parameter o | true | @@ -102,7 +71,6 @@ | Guards.cs:271:13:271:14 | access to parameter o1 | Guards.cs:270:13:270:42 | call to operator == | Guards.cs:270:13:270:14 | access to parameter o1 | true | | Guards.cs:342:27:342:27 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | false | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:21 | ... != ... | Guards.cs:342:13:342:13 | access to local variable s | true | -| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:27 | ... && ... | Guards.cs:342:13:342:13 | access to local variable s | true | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:25 | ... is ... | Guards.cs:348:13:348:13 | access to parameter o | true | | Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:25 | ... != ... | Splitting.cs:12:17:12:17 | access to parameter o | true | | Splitting.cs:23:24:23:24 | access to parameter o | Splitting.cs:22:17:22:25 | ... != ... | Splitting.cs:22:17:22:17 | access to parameter o | true | diff --git a/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected b/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected index 3db5997056c0..404c01975560 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected @@ -17,7 +17,6 @@ | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true | | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | @@ -25,7 +24,6 @@ | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false | | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | false | | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | @@ -33,7 +31,6 @@ | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true | | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | false | | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | @@ -41,7 +38,6 @@ | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false | -| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false | | Assert.cs:93:33:93:34 | [assertion failure, b1 (line 91): false] access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | false | | Assert.cs:93:33:93:34 | [assertion failure, b1 (line 91): false] access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | true | | Assert.cs:93:33:93:34 | [assertion failure, b1 (line 91): true] access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | true | @@ -56,49 +52,27 @@ | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:68:13:68:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:96:31:96:34 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | non-empty | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:12:13:12:24 | ... > ... | Guards.cs:12:13:12:13 | access to parameter s | true | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:13 | access to parameter s | Guards.cs:24:13:24:13 | access to parameter s | non-null | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:21 | ... != ... | Guards.cs:24:13:24:13 | access to parameter s | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:36 | !... | Guards.cs:32:35:32:35 | access to parameter x | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:35:32:35 | access to parameter x | true | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:14:32:36 | call to method IsNullOrEmpty | Guards.cs:32:35:32:35 | access to parameter x | false | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:35:32:35 | access to parameter x | Guards.cs:32:35:32:35 | access to parameter x | non-null | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:42:32:42 | access to parameter y | true | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:40:32:51 | !... | Guards.cs:32:42:32:42 | access to parameter y | true | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:42 | access to parameter y | Guards.cs:32:42:32:42 | access to parameter y | non-null | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:50 | ... == ... | Guards.cs:32:42:32:42 | access to parameter y | false | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:13 | access to parameter x | Guards.cs:35:13:35:13 | access to parameter x | non-null | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:21 | ... == ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:26:35:26 | access to parameter y | false | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:26 | access to parameter y | Guards.cs:35:26:35:26 | access to parameter y | non-null | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:34 | ... == ... | Guards.cs:35:26:35:26 | access to parameter y | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:13:38:37 | !... | Guards.cs:38:15:38:15 | access to parameter x | true | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:15 | access to parameter x | Guards.cs:38:15:38:15 | access to parameter x | non-null | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:23 | ... == ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:13:38:37 | !... | Guards.cs:38:28:38:28 | access to parameter y | true | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:28:38:28 | access to parameter y | false | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:28 | access to parameter y | Guards.cs:38:28:38:28 | access to parameter y | non-null | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:36 | ... == ... | Guards.cs:38:28:38:28 | access to parameter y | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:13:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:14:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:15:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:17 | access to parameter x | Guards.cs:41:17:41:17 | access to parameter x | non-null | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:25 | ... != ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:13:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:14:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:15:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:30 | access to parameter y | Guards.cs:41:30:41:30 | access to parameter y | non-null | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:38 | ... != ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:48:31:48:40 | access to field Field | Guards.cs:47:13:47:17 | access to field Field | Guards.cs:47:13:47:17 | access to field Field | non-null | @@ -193,21 +167,16 @@ | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-match access to type Action | | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-match null | | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-null | -| Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:13:168:41 | !... | Guards.cs:168:40:168:40 | access to parameter x | true | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:14:168:41 | call to method IsNullOrWhiteSpace | Guards.cs:168:40:168:40 | access to parameter x | false | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:40:168:40 | access to parameter x | Guards.cs:168:40:168:40 | access to parameter x | non-null | -| Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:13:189:25 | !... | Guards.cs:189:24:189:24 | access to parameter s | true | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:14:189:25 | call to method NullTest1 | Guards.cs:189:24:189:24 | access to parameter s | false | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:24:189:24 | access to parameter s | Guards.cs:189:24:189:24 | access to parameter s | non-null | -| Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:13:191:25 | !... | Guards.cs:191:24:191:24 | access to parameter s | true | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:14:191:25 | call to method NullTest2 | Guards.cs:191:24:191:24 | access to parameter s | false | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:24:191:24 | access to parameter s | Guards.cs:191:24:191:24 | access to parameter s | non-null | -| Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:13:193:25 | !... | Guards.cs:193:24:193:24 | access to parameter s | true | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:14:193:25 | call to method NullTest3 | Guards.cs:193:24:193:24 | access to parameter s | false | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:24:193:24 | access to parameter s | Guards.cs:193:24:193:24 | access to parameter s | non-null | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:13:195:27 | call to method NotNullTest4 | Guards.cs:195:26:195:26 | access to parameter s | true | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | non-null | -| Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:13:197:29 | !... | Guards.cs:197:28:197:28 | access to parameter s | true | | Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:14:197:29 | call to method NullTestWrong | Guards.cs:197:28:197:28 | access to parameter s | false | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:13 | access to parameter o | Guards.cs:203:13:203:13 | access to parameter o | non-null | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:21 | ... != ... | Guards.cs:203:13:203:13 | access to parameter o | true | @@ -241,7 +210,6 @@ | Guards.cs:342:27:342:27 | [b (line 339): true] access to parameter b | Guards.cs:341:20:341:32 | ... ? ... : ... | Guards.cs:341:20:341:20 | access to parameter b | non-null | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | non-null | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:21 | ... != ... | Guards.cs:342:13:342:13 | access to local variable s | true | -| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:27 | ... && ... | Guards.cs:342:13:342:13 | access to local variable s | true | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:13 | access to parameter o | Guards.cs:348:13:348:13 | access to parameter o | non-null | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:25 | ... is ... | Guards.cs:348:13:348:13 | access to parameter o | true | | Splitting.cs:13:17:13:17 | [b (line 9): true] access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | non-null | diff --git a/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected b/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected index aad97097f21f..bd5e3dbf1a14 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected @@ -14,22 +14,18 @@ | Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true | | Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | | Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false | | Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | | Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true | | Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | | Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false | -| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false | | Assert.cs:94:16:94:17 | access to parameter b1 | Assert.cs:93:25:93:26 | access to parameter b1 | Assert.cs:93:25:93:26 | access to parameter b1 | true | | Assert.cs:94:23:94:24 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | false | | Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:16 | access to parameter args | Collections.cs:50:13:50:16 | access to parameter args | non-empty | @@ -40,49 +36,27 @@ | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:68:13:68:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:96:31:96:34 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | non-empty | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:12:13:12:24 | ... > ... | Guards.cs:12:13:12:13 | access to parameter s | true | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:13 | access to parameter s | Guards.cs:24:13:24:13 | access to parameter s | non-null | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:21 | ... != ... | Guards.cs:24:13:24:13 | access to parameter s | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:36 | !... | Guards.cs:32:35:32:35 | access to parameter x | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:35:32:35 | access to parameter x | true | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:14:32:36 | call to method IsNullOrEmpty | Guards.cs:32:35:32:35 | access to parameter x | false | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:35:32:35 | access to parameter x | Guards.cs:32:35:32:35 | access to parameter x | non-null | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:42:32:42 | access to parameter y | true | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:40:32:51 | !... | Guards.cs:32:42:32:42 | access to parameter y | true | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:42 | access to parameter y | Guards.cs:32:42:32:42 | access to parameter y | non-null | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:50 | ... == ... | Guards.cs:32:42:32:42 | access to parameter y | false | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:13 | access to parameter x | Guards.cs:35:13:35:13 | access to parameter x | non-null | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:21 | ... == ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:26:35:26 | access to parameter y | false | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:26 | access to parameter y | Guards.cs:35:26:35:26 | access to parameter y | non-null | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:34 | ... == ... | Guards.cs:35:26:35:26 | access to parameter y | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:13:38:37 | !... | Guards.cs:38:15:38:15 | access to parameter x | true | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:15 | access to parameter x | Guards.cs:38:15:38:15 | access to parameter x | non-null | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:23 | ... == ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:13:38:37 | !... | Guards.cs:38:28:38:28 | access to parameter y | true | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:28:38:28 | access to parameter y | false | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:28 | access to parameter y | Guards.cs:38:28:38:28 | access to parameter y | non-null | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:36 | ... == ... | Guards.cs:38:28:38:28 | access to parameter y | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:13:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:14:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:15:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:17 | access to parameter x | Guards.cs:41:17:41:17 | access to parameter x | non-null | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:25 | ... != ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:13:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:14:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:15:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:30 | access to parameter y | Guards.cs:41:30:41:30 | access to parameter y | non-null | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:38 | ... != ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:48:31:48:40 | access to field Field | Guards.cs:47:13:47:17 | access to field Field | Guards.cs:47:13:47:17 | access to field Field | non-null | @@ -177,21 +151,16 @@ | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-match access to type Action | | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-match null | | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-null | -| Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:13:168:41 | !... | Guards.cs:168:40:168:40 | access to parameter x | true | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:14:168:41 | call to method IsNullOrWhiteSpace | Guards.cs:168:40:168:40 | access to parameter x | false | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:40:168:40 | access to parameter x | Guards.cs:168:40:168:40 | access to parameter x | non-null | -| Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:13:189:25 | !... | Guards.cs:189:24:189:24 | access to parameter s | true | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:14:189:25 | call to method NullTest1 | Guards.cs:189:24:189:24 | access to parameter s | false | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:24:189:24 | access to parameter s | Guards.cs:189:24:189:24 | access to parameter s | non-null | -| Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:13:191:25 | !... | Guards.cs:191:24:191:24 | access to parameter s | true | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:14:191:25 | call to method NullTest2 | Guards.cs:191:24:191:24 | access to parameter s | false | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:24:191:24 | access to parameter s | Guards.cs:191:24:191:24 | access to parameter s | non-null | -| Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:13:193:25 | !... | Guards.cs:193:24:193:24 | access to parameter s | true | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:14:193:25 | call to method NullTest3 | Guards.cs:193:24:193:24 | access to parameter s | false | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:24:193:24 | access to parameter s | Guards.cs:193:24:193:24 | access to parameter s | non-null | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:13:195:27 | call to method NotNullTest4 | Guards.cs:195:26:195:26 | access to parameter s | true | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | non-null | -| Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:13:197:29 | !... | Guards.cs:197:28:197:28 | access to parameter s | true | | Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:14:197:29 | call to method NullTestWrong | Guards.cs:197:28:197:28 | access to parameter s | false | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:13 | access to parameter o | Guards.cs:203:13:203:13 | access to parameter o | non-null | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:21 | ... != ... | Guards.cs:203:13:203:13 | access to parameter o | true | @@ -222,7 +191,6 @@ | Guards.cs:342:27:342:27 | access to parameter b | Guards.cs:341:20:341:32 | ... ? ... : ... | Guards.cs:341:20:341:20 | access to parameter b | non-null | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | non-null | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:21 | ... != ... | Guards.cs:342:13:342:13 | access to local variable s | true | -| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:27 | ... && ... | Guards.cs:342:13:342:13 | access to local variable s | true | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:13 | access to parameter o | Guards.cs:348:13:348:13 | access to parameter o | non-null | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:25 | ... is ... | Guards.cs:348:13:348:13 | access to parameter o | true | | Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | non-null | diff --git a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected index 30b6ee553a16..faaef439eb55 100644 --- a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected +++ b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected @@ -2211,54 +2211,83 @@ | System.Text.RegularExpressions.MatchCollection.get_Item(int) | element of argument -1 -> return (normal) | true | | System.Text.RegularExpressions.MatchCollection.set_Item(int, Match) | argument 1 -> element of argument -1 | true | | System.Text.RegularExpressions.MatchCollection.set_Item(int, object) | argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.Append(Char[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(Char[]) | element of argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.Append(Char[], int, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(Char[], int, int) | element of argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.Append(ReadOnlyMemory) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(ReadOnlySpan) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(StringBuilder) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(StringBuilder, int, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(bool) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(byte) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(char) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(char*, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(char, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(decimal) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(double) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(float) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(long) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.Append(object) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.Append(object) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.Append(object) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(sbyte) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(short) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.Append(string) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.Append(string) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.Append(string) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.Append(string, int, int) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.Append(string, int, int) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.Append(string, int, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(uint) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(ulong) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(ushort) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 2 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 2 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 3 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 3 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 2 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 3 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 3 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 4 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 4 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | argument 1 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | element of argument 2 -> element of argument -1 | true | | System.Text.StringBuilder.AppendFormat(string, object) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object) | argument 1 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 2 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, object, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 2 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 3 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 3 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, params Object[]) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, params Object[]) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, params Object[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, params Object[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(char, params Object[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(char, params Object[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(char, params String[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(char, params String[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, params Object[]) | argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, params Object[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(string, params Object[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, params String[]) | argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, params String[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(string, params String[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(char, IEnumerable) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(char, IEnumerable) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, IEnumerable) | argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, IEnumerable) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(string, IEnumerable) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendLine() | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendLine(string) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendLine(string) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendLine(string) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.StringBuilder(string) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.StringBuilder(string, int) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.StringBuilder(string, int, int, int) | argument 0 -> element of return (normal) | true | diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected index e6c0dd9bedaa..f8d274d364d9 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected @@ -310,6 +310,7 @@ | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | | LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | | LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine | | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | @@ -322,6 +323,7 @@ | LocalDataFlow.cs:242:15:242:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | | LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | | LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine | | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | | LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | | LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList | diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected index e20fecfbe556..b5a6e1ba9297 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected @@ -400,9 +400,9 @@ | LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | | LocalDataFlow.cs:234:40:234:41 | "" | LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | | LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine | | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | -| LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine | | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | @@ -416,9 +416,9 @@ | LocalDataFlow.cs:242:15:242:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | | LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | | LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine | | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | -| LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine | | LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | | LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList | | LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AString) | LocalDataFlow.cs:248:22:248:48 | access to property AString | diff --git a/csharp/ql/test/library-tests/diagnostics/A.cs b/csharp/ql/test/library-tests/diagnostics/A.cs new file mode 100644 index 000000000000..21a5b7f52616 --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/A.cs @@ -0,0 +1,4 @@ +public class A +{ + public void M() { } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.expected b/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.expected new file mode 100644 index 000000000000..e4ae5ec1ef29 --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.expected @@ -0,0 +1 @@ +| Unexpected C# extractor error in Program.cs: Unable to resolve target for call. (Compilation error?) | 2 | diff --git a/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.qlref b/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.qlref new file mode 100644 index 000000000000..7068705cc1be --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.qlref @@ -0,0 +1 @@ +Diagnostics/DiagnosticExtractionErrors.ql diff --git a/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.expected b/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.expected new file mode 100644 index 000000000000..17e4dc54b7dd --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.expected @@ -0,0 +1 @@ +| A.cs:0:0:0:0 | A.cs | | diff --git a/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.qlref b/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.qlref new file mode 100644 index 000000000000..7994e050699f --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.qlref @@ -0,0 +1 @@ +Diagnostics/DiagnosticNoExtractionErrors.ql diff --git a/csharp/ql/test/library-tests/diagnostics/Program.cs b/csharp/ql/test/library-tests/diagnostics/Program.cs new file mode 100644 index 000000000000..7f0a7e57d1cd --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/Program.cs @@ -0,0 +1,17 @@ +// semmle-extractor-options: --standalone + +using System; + +class Class +{ + static void Main(string[] args) + { + int z = GetParamLength(__arglist(1, 2)); + } + + public static int GetParamLength(__arglist) + { + ArgIterator iterator = new ArgIterator(__arglist); + return iterator.GetRemainingCount(); + } +} diff --git a/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected b/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected index ff6d7e236a41..917d625c10c3 100644 --- a/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected +++ b/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected @@ -1,2 +1,13 @@ compilationMessages extractorMessages +| errors.cs:8:1:8:22 | Namespace not found | +| errors.cs:24:31:24:32 | Failed to determine type | +| errors.cs:24:31:24:40 | Failed to determine type | +| errors.cs:24:31:24:40 | Unable to resolve target for call. (Compilation error?) | +| errors.cs:24:38:24:39 | Failed to determine type | +| errors.cs:57:20:57:20 | Failed to determine type | +| errors.cs:93:45:93:45 | Failed to determine type | +| errors.cs:93:45:93:45 | Failed to resolve name | +| errors.cs:94:45:94:45 | Failed to determine type | +| errors.cs:94:45:94:45 | Failed to resolve name | +| 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 000000000000..b5a514b9ffa6 --- /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 000000000000..8c18065043fd --- /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 000000000000..0c12535ee09f --- /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-134/ConsoleUncontrolledFormatString.cs b/csharp/ql/test/query-tests/Security Features/CWE-134/ConsoleUncontrolledFormatString.cs new file mode 100644 index 000000000000..c9d4440cf787 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-134/ConsoleUncontrolledFormatString.cs @@ -0,0 +1,13 @@ +using System; +using System; + +public class Program +{ + public static void Main() + { + var format = Console.ReadLine(); + + // BAD: Uncontrolled format string. + var x = string.Format(format, 1, 2); + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected index bc87c96b194b..4923cd34e70d 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected @@ -1,8 +1,11 @@ edges +| ConsoleUncontrolledFormatString.cs:8:22:8:39 | call to method ReadLine : String | ConsoleUncontrolledFormatString.cs:11:31:11:36 | access to local variable format | | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | | UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString : NameValueCollection | UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | nodes +| ConsoleUncontrolledFormatString.cs:8:22:8:39 | call to method ReadLine : String | semmle.label | call to method ReadLine : String | +| ConsoleUncontrolledFormatString.cs:11:31:11:36 | access to local variable format | semmle.label | access to local variable format | | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | semmle.label | access to local variable path | | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | semmle.label | access to local variable path | @@ -10,6 +13,7 @@ nodes | UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | semmle.label | access to local variable format | #select +| ConsoleUncontrolledFormatString.cs:11:31:11:36 | access to local variable format | ConsoleUncontrolledFormatString.cs:8:22:8:39 | call to method ReadLine : String | ConsoleUncontrolledFormatString.cs:11:31:11:36 | access to local variable format | $@ flows to here and is used as a format string. | ConsoleUncontrolledFormatString.cs:8:22:8:39 | call to method ReadLine | call to method ReadLine | | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | access to property QueryString | | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | access to property QueryString | | UncontrolledFormatString.cs:34:23:34:31 | access to property Text | UncontrolledFormatString.cs:34:23:34:31 | access to property Text | UncontrolledFormatString.cs:34:23:34:31 | access to property Text | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:34:23:34:31 | access to property Text | access to property Text | diff --git a/docs/codeql/ql-language-reference/annotations.rst b/docs/codeql/ql-language-reference/annotations.rst index ec60e55bf9bd..e801f0a95aa1 100644 --- a/docs/codeql/ql-language-reference/annotations.rst +++ b/docs/codeql/ql-language-reference/annotations.rst @@ -259,8 +259,6 @@ This means that it is part of the output of the QL program. Compiler pragmas ================ -**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| - The following compiler pragmas affect the compilation and optimization of queries. You should avoid using these annotations unless you experience significant performance issues. @@ -284,6 +282,8 @@ predicates. ``pragma[inline]`` ------------------ +**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| + The ``pragma[inline]`` annotation tells the QL optimizer to always inline the annotated predicate into the places where it is called. This can be useful when a predicate body is very expensive to compute entirely, as it ensures that the predicate is evaluated with the other contextual information @@ -292,6 +292,8 @@ at the places where it is called. ``pragma[noinline]`` -------------------- +**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| + The ``pragma[noinline]`` annotation is used to prevent a predicate from being inlined into the place where it is called. In practice, this annotation is useful when you've already grouped certain variables together in a "helper" predicate, to ensure that the relation is evaluated @@ -301,6 +303,8 @@ work of the helper predicate, so it's a good idea to annotate it with ``pragma[n ``pragma[nomagic]`` ------------------- +**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| + The ``pragma[nomagic]`` annotation is used to prevent the QL optimizer from performing the "magic sets" optimization on a predicate. @@ -314,6 +318,8 @@ Note that ``nomagic`` implies ``noinline``. ``pragma[noopt]`` ----------------- +**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| + The ``pragma[noopt]`` annotation is used to prevent the QL optimizer from optimizing a predicate, except when it's absolutely necessary for compilation and evaluation to work. @@ -364,7 +370,33 @@ When you use this annotation, be aware of the following issues: succ.getSucc() = 3 ) } - + +``pragma[only_bind_out]`` +------------------------- + +**Available for**: |expressions| + +The ``pragma[only_bind_out]`` annotation lets you specify the direction in which the QL compiler should bind expressions. +This can be useful to improve performance in rare cases where the QL optimizer orders parts of the QL program in an inefficient way. + +For example, ``x = pragma[only_bind_out](y)`` is semantically equivalent to ``x = y``, but has different binding behavior. +``x = y`` binds ``x`` from ``y`` and vice versa, while ``x = pragma[only_bind_out](y)`` only binds ``x`` from ``y``. + +For more information, see ":ref:`Binding `." + +``pragma[only_bind_into]`` +-------------------------- + +**Available for**: |expressions| + +The ``pragma[only_bind_into]`` annotation lets you specify the direction in which the QL compiler should bind expressions. +This can be useful to improve performance in rare cases where the QL optimizer orders parts of the QL program in an inefficient way. + +For example, ``x = pragma[only_bind_into](y)`` is semantically equivalent to ``x = y``, but has different binding behavior. +``x = y`` binds ``x`` from ``y`` and vice versa, while ``x = pragma[only_bind_into](y)`` only binds ``y`` from ``x``. + +For more information, see ":ref:`Binding `." + .. _language: Language pragmas @@ -413,4 +445,5 @@ The ``bindingset`` annotation takes a comma-separated list of variables. .. |fields| replace:: :ref:`fields ` .. |modules| replace:: :ref:`modules ` .. |aliases| replace:: :ref:`aliases ` -.. |algebraic datatypes| replace:: :ref:`algebraic datatypes ` \ No newline at end of file +.. |algebraic datatypes| replace:: :ref:`algebraic datatypes ` +.. |expressions| replace:: :ref:`expressions ` diff --git a/docs/codeql/ql-language-reference/ql-language-specification.rst b/docs/codeql/ql-language-reference/ql-language-specification.rst index 0fe542105046..fb89100b4018 100644 --- a/docs/codeql/ql-language-reference/ql-language-specification.rst +++ b/docs/codeql/ql-language-reference/ql-language-specification.rst @@ -1116,8 +1116,6 @@ A super expression may only occur in a QL program as the receiver expression for If a super expression includes a ``type``, then that type must be a class that the enclosing class inherits from. -If the super expression does not include a type, then the enclosing class must have a single declared base type, and that base type must be a class. - The value of a super expression is the same as the value of ``this`` in the named tuple. Casts @@ -1169,7 +1167,12 @@ A valid call with results *resolves* to a set of predicates. The ways a call can - If the call has no receiver and the predicate name is a selection identifier, then the qualifier is resolved as a module (see "`Module resolution <#module-resolution>`__"). The identifier is then resolved in the exported predicate environment of the qualifier module. -- If the call has a super expression as the receiver, then it resolves to a member predicate in a class the enclosing class inherits from. If the super expression is unqualified, then the super-class is the single class that the current class inherits from. If there is not exactly one such class, then the program is invalid. Otherwise the super-class is the class named by the qualifier of the super expression. The predicate is resolved by looking up its name and arity in the exported predicate environment of the super-class. +- If the call has a super expression as the receiver, then it resolves to a member predicate in a class that the enclosing class inherits from: + - If the super expression is unqualified and there is a single class that the current class inherits from, then the super-class is that class. + - If the super expression is unqualified and there are multiple classes that the current class inherits from, then the super-class is the domain type. + - Otherwise, the super-class is the class named by the qualifier of the super expression. + + The predicate is resolved by looking up its name and arity in the exported predicate environment of the super-class. - If the type of the receiver is the same as the enclosing class, the predicate is resolved by looking up its name and arity in the visible predicate environment of the class. diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index 22569cbdcc66..7266aa4b0284 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -23,6 +23,7 @@ C# built-in support ASP.NET, Web application framework ASP.NET Core, Web application framework ASP.NET Razor templates, Web application framework + Dapper, Database ORM EntityFramework, Database ORM EntityFramework Core, Database ORM Json.NET, Serialization diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst index a338791953b1..9bc3c855e586 100644 --- a/docs/codeql/support/reusables/versions-compilers.rst +++ b/docs/codeql/support/reusables/versions-compilers.rst @@ -11,16 +11,18 @@ 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), Eclipse compiler for Java (ECJ) [4]_",``.java`` - JavaScript,ECMAScript 2019 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [5]_" + JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [5]_" Python,"2.7, 3.5, 3.6, 3.7, 3.8",Not applicable,``.py`` - TypeScript [6]_,"2.6-3.7",Standard TypeScript compiler,"``.ts``, ``.tsx``" + TypeScript [6]_,"2.6-4.2",Standard TypeScript compiler,"``.ts``, ``.tsx``" .. container:: footnote-group @@ -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/codeql/writing-codeql-queries/find-the-thief.rst b/docs/codeql/writing-codeql-queries/find-the-thief.rst index 131423b80585..ff9ee49f5dc4 100644 --- a/docs/codeql/writing-codeql-queries/find-the-thief.rst +++ b/docs/codeql/writing-codeql-queries/find-the-thief.rst @@ -39,11 +39,13 @@ You start asking some creative questions and making notes of the answers so you +------+--------------------------------------------------------------------+--------+ | (7) | Is the thief taller than 180cm and shorter than 190cm? | no | +------+--------------------------------------------------------------------+--------+ -| (8) | Is the thief the tallest person in the village? | no | +| (8) | Is the thief the oldest person in the village? | no | +------+--------------------------------------------------------------------+--------+ -| (9) | Is the thief shorter than the average villager? | yes | +| (9) | Is the thief the tallest person in the village? | no | +------+--------------------------------------------------------------------+--------+ -| (10) | Is the thief the oldest person in the eastern part of the village? | yes | +| (10) | Is the thief shorter than the average villager? | yes | ++------+--------------------------------------------------------------------+--------+ +| (11) | Is the thief the oldest person in the eastern part of the village? | yes | +------+--------------------------------------------------------------------+--------+ There is too much information to search through by hand, so you decide to use your newly acquired QL skills to help you with your investigation... diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md index 3c46436a87a5..ea3379f3967b 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 000000000000..910a5c914232 --- /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 000000000000..76ce4cdfd500 --- /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 000000000000..8b045df256e0 --- /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 000000000000..161c9f17e42b --- /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/Likely Bugs/Concurrency/StartInConstructor.qhelp b/java/ql/src/Likely Bugs/Concurrency/StartInConstructor.qhelp index 1d0d5b0a1fbf..1401c92b0d66 100644 --- a/java/ql/src/Likely Bugs/Concurrency/StartInConstructor.qhelp +++ b/java/ql/src/Likely Bugs/Concurrency/StartInConstructor.qhelp @@ -41,7 +41,7 @@ initialized. This results in the program outputting "hello my friend".

  • IBM developerWorks: - Don't start threads from within constructors. + Don't start threads from within constructors.
  • diff --git a/java/ql/src/Likely Bugs/Frameworks/Swing/ThreadSafety.qhelp b/java/ql/src/Likely Bugs/Frameworks/Swing/ThreadSafety.qhelp index af79f45d59ca..060db52dd61b 100644 --- a/java/ql/src/Likely Bugs/Frameworks/Swing/ThreadSafety.qhelp +++ b/java/ql/src/Likely Bugs/Frameworks/Swing/ThreadSafety.qhelp @@ -94,7 +94,7 @@ D. Flanagan, Java Foundation Classes in a Nutshell, p.28. O'Reilly, 199
  • Java Developer's Journal: -Building Thread-Safe GUIs with Swing. +Building Thread-Safe GUIs with Swing.
  • The Java Tutorials: diff --git a/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.qhelp b/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.qhelp index 48004a1f1186..d76c09e872bc 100644 --- a/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.qhelp +++ b/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.qhelp @@ -41,7 +41,7 @@ J. Bloch and N. Gafter, Java Puzzlers: Traps, Pitfalls, and Corner Cases
  • -NetBeans IDE: Java Hints +NetBeans IDE: Java Hints
  • PMD: Rule StringBufferInstantiationWithChar diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp index 4d06bef040e3..b0ded8e53a17 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp @@ -51,7 +51,7 @@ In this case, the inner expression needs to be assigned to a local variable and
  • - IBM developerWorks: Java theory and practice: Good housekeeping practices. + IBM developerWorks: Java theory and practice: Good housekeeping practices.
  • The Java Tutorials: The try-with-resources Statement. diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.qhelp index 8f89d16ef1d2..eea0513dc8ea 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.qhelp +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.qhelp @@ -40,7 +40,7 @@ by the code that created it or by a server shutdown procedure, as appropriate. - IBM developerWorks: Java theory and practice: Good housekeeping practices. + IBM developerWorks: Java theory and practice: Good housekeeping practices.
  • The Java Tutorials: The try-with-resources Statement. diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp index 36e834ecefda..0b348a3f9b81 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp @@ -50,7 +50,7 @@ In this case, the inner expression needs to be assigned to a local variable and
  • - IBM developerWorks: Java theory and practice: Good housekeeping practices. + IBM developerWorks: Java theory and practice: Good housekeeping practices.
  • The Java Tutorials: The try-with-resources Statement. diff --git a/java/ql/src/Metrics/RefTypes/TEfferentCoupling.qhelp b/java/ql/src/Metrics/RefTypes/TEfferentCoupling.qhelp index 40e9e0ef5de1..535faedf81e3 100644 --- a/java/ql/src/Metrics/RefTypes/TEfferentCoupling.qhelp +++ b/java/ql/src/Metrics/RefTypes/TEfferentCoupling.qhelp @@ -51,7 +51,7 @@ so the general technique is quite widely applicable.
  • -IBM developerWorks: Evolutionary architecture and emergent design: Emergent design through metrics. +IBM developerWorks: Evolutionary architecture and emergent design: Emergent design through metrics.
  • R. Martin, Agile Software Development: Principles, Patterns and Practices. Pearson, 2011. diff --git a/java/ql/src/Metrics/RefTypes/TEfferentSourceCoupling.qhelp b/java/ql/src/Metrics/RefTypes/TEfferentSourceCoupling.qhelp index ceb74639c1e7..965fb2795fbf 100644 --- a/java/ql/src/Metrics/RefTypes/TEfferentSourceCoupling.qhelp +++ b/java/ql/src/Metrics/RefTypes/TEfferentSourceCoupling.qhelp @@ -81,7 +81,7 @@ so the general technique is quite widely applicable.
  • -A. Glover. Code quality for software architects. Published online, 2006. +A. Glover. Code quality for software architects. Published online, 2006.
  • R. Martin. Agile Software Development: Principles, Patterns and Practices. Pearson, 2011. diff --git a/java/ql/src/Metrics/Summaries/LinesOfCode.ql b/java/ql/src/Metrics/Summaries/LinesOfCode.ql new file mode 100644 index 000000000000..d6db7c6ee6b0 --- /dev/null +++ b/java/ql/src/Metrics/Summaries/LinesOfCode.ql @@ -0,0 +1,13 @@ +/** + * @id java/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 java + +select sum(CompilationUnit f | f.fromSource() | f.getNumberOfLinesOfCode()) diff --git a/java/ql/src/Performance/InnerClassCouldBeStatic.ql b/java/ql/src/Performance/InnerClassCouldBeStatic.ql index a18d801549d3..5dba77761c6e 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-022/ZipSlip.ql b/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql index 7d74f8b79ac4..6342a444a646 100644 --- a/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql +++ b/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql @@ -17,6 +17,7 @@ import semmle.code.java.dataflow.SSA import semmle.code.java.dataflow.TaintTracking import DataFlow import PathGraph +private import semmle.code.java.dataflow.ExternalFlow /** * A method that returns the name of an archive entry. @@ -33,34 +34,6 @@ class ArchiveEntryNameMethod extends Method { } } -/** - * An expression that will be treated as the destination of a write. - */ -class WrittenFileName extends Expr { - WrittenFileName() { - // Constructors that write to their first argument. - exists(ConstructorCall ctr | this = ctr.getArgument(0) | - exists(Class c | ctr.getConstructor() = c.getAConstructor() | - c.hasQualifiedName("java.io", "FileOutputStream") or - c.hasQualifiedName("java.io", "RandomAccessFile") or - c.hasQualifiedName("java.io", "FileWriter") - ) - ) - or - // Methods that write to their n'th argument - exists(MethodAccess call, int n | this = call.getArgument(n) | - call.getMethod().getDeclaringType().hasQualifiedName("java.nio.file", "Files") and - ( - call.getMethod().getName().regexpMatch("new.*Reader|newOutputStream|create.*") and n = 0 - or - call.getMethod().hasName("copy") and n = 1 - or - call.getMethod().hasName("move") and n = 1 - ) - ) - } -} - /** * Holds if `n1` to `n2` is a dataflow step that converts between `String`, * `File`, and `Path`. @@ -151,7 +124,7 @@ class ZipSlipConfiguration extends TaintTracking::Configuration { source.asExpr().(MethodAccess).getMethod() instanceof ArchiveEntryNameMethod } - override predicate isSink(Node sink) { sink.asExpr() instanceof WrittenFileName } + override predicate isSink(Node sink) { sink instanceof FileCreationSink } override predicate isAdditionalTaintStep(Node n1, Node n2) { filePathStep(n1, n2) or fileTaintStep(n1, n2) @@ -173,6 +146,13 @@ class ZipSlipConfiguration extends TaintTracking::Configuration { } } +/** + * A sink that represents a file creation, such as a file write, copy or move operation. + */ +private class FileCreationSink extends DataFlow::Node { + FileCreationSink() { sinkNode(this, "create-file") } +} + from PathNode source, PathNode sink where any(ZipSlipConfiguration c).hasFlowPath(source, sink) select source.getNode(), source, sink, diff --git a/java/ql/src/Security/CWE/CWE-094/InsecureBeanValidation.ql b/java/ql/src/Security/CWE/CWE-094/InsecureBeanValidation.ql index 6b8ab0851329..aef404aabd13 100644 --- a/java/ql/src/Security/CWE/CWE-094/InsecureBeanValidation.ql +++ b/java/ql/src/Security/CWE/CWE-094/InsecureBeanValidation.ql @@ -13,6 +13,7 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources import DataFlow::PathGraph +private import semmle.code.java.dataflow.ExternalFlow /** * A message interpolator Type that perform Expression Language (EL) evaluations @@ -50,19 +51,6 @@ class SetMessageInterpolatorCall extends MethodAccess { predicate isSafe() { not this.getAnArgument().getType() instanceof ELMessageInterpolatorType } } -/** - * A method named `buildConstraintViolationWithTemplate` declared on a subtype - * of `javax.validation.ConstraintValidatorContext`. - */ -class BuildConstraintViolationWithTemplateMethod extends Method { - BuildConstraintViolationWithTemplateMethod() { - this.getDeclaringType() - .getASupertype*() - .hasQualifiedName("javax.validation", "ConstraintValidatorContext") and - this.hasName("buildConstraintViolationWithTemplate") - } -} - /** * Taint tracking BeanValidationConfiguration describing the flow of data from user input * to the argument of a method that builds constraint error messages. @@ -72,12 +60,15 @@ class BeanValidationConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess ma | - ma.getMethod() instanceof BuildConstraintViolationWithTemplateMethod and - sink.asExpr() = ma.getArgument(0) - ) - } + override predicate isSink(DataFlow::Node sink) { sink instanceof BeanValidationSink } +} + +/** + * A bean validation sink, such as method `buildConstraintViolationWithTemplate` + * declared on a subtype of `javax.validation.ConstraintValidatorContext`. + */ +private class BeanValidationSink extends DataFlow::Node { + BeanValidationSink() { sinkNode(this, "bean-validation") } } from BeanValidationConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql index c9c1e2917c0e..3426d9f6f62b 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-297/UnsafeHostnameVerification.ql b/java/ql/src/Security/CWE/CWE-297/UnsafeHostnameVerification.ql index 9c060565f284..0bf7a164826a 100644 --- a/java/ql/src/Security/CWE/CWE-297/UnsafeHostnameVerification.ql +++ b/java/ql/src/Security/CWE/CWE-297/UnsafeHostnameVerification.ql @@ -15,6 +15,7 @@ import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.FlowSources import semmle.code.java.security.Encryption import DataFlow::PathGraph +private import semmle.code.java.dataflow.ExternalFlow /** * Holds if `m` always returns `true` ignoring any exceptional flow. @@ -49,14 +50,7 @@ class TrustAllHostnameVerifierConfiguration extends DataFlow::Configuration { source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof TrustAllHostnameVerifier } - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess ma, Method m | - (m instanceof SetDefaultHostnameVerifierMethod or m instanceof SetHostnameVerifierMethod) and - ma.getMethod() = m - | - ma.getArgument(0) = sink.asExpr() - ) - } + override predicate isSink(DataFlow::Node sink) { sink instanceof HostnameVerifierSink } override predicate isBarrier(DataFlow::Node barrier) { // ignore nodes that are in functions that intentionally disable hostname verification @@ -84,6 +78,13 @@ class TrustAllHostnameVerifierConfiguration extends DataFlow::Configuration { } } +/** + * A sink that sets the `HostnameVerifier` on `HttpsURLConnection`. + */ +private class HostnameVerifierSink extends DataFlow::Node { + HostnameVerifierSink() { sinkNode(this, "set-hostname-verifier") } +} + bindingset[result] private string getAFlagName() { result diff --git a/java/ql/src/Security/CWE/CWE-319/HttpsUrls.ql b/java/ql/src/Security/CWE/CWE-319/HttpsUrls.ql index 306bf27ab9c0..77980f033f0b 100644 --- a/java/ql/src/Security/CWE/CWE-319/HttpsUrls.ql +++ b/java/ql/src/Security/CWE/CWE-319/HttpsUrls.ql @@ -13,9 +13,10 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.frameworks.Networking import DataFlow::PathGraph +private import semmle.code.java.dataflow.ExternalFlow -class HTTPString extends StringLiteral { - HTTPString() { +class HttpString extends StringLiteral { + HttpString() { // Avoid matching "https" here. exists(string s | this.getRepresentedString() = s | ( @@ -30,26 +31,12 @@ class HTTPString extends StringLiteral { } } -class URLOpenMethod extends Method { - URLOpenMethod() { - this.getDeclaringType().getQualifiedName() = "java.net.URL" and - ( - this.getName() = "openConnection" or - this.getName() = "openStream" - ) - } -} +class HttpStringToUrlOpenMethodFlowConfig extends TaintTracking::Configuration { + HttpStringToUrlOpenMethodFlowConfig() { this = "HttpsUrls::HttpStringToUrlOpenMethodFlowConfig" } -class HTTPStringToURLOpenMethodFlowConfig extends TaintTracking::Configuration { - HTTPStringToURLOpenMethodFlowConfig() { this = "HttpsUrls::HTTPStringToURLOpenMethodFlowConfig" } + override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof HttpString } - override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof HTTPString } - - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess m | - sink.asExpr() = m.getQualifier() and m.getMethod() instanceof URLOpenMethod - ) - } + override predicate isSink(DataFlow::Node sink) { sink instanceof UrlOpenSink } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { exists(UrlConstructorCall u | @@ -63,10 +50,17 @@ class HTTPStringToURLOpenMethodFlowConfig extends TaintTracking::Configuration { } } -from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess m, HTTPString s +/** + * A sink that represents a URL opening method call, such as a call to `java.net.URL.openConnection()`. + */ +private class UrlOpenSink extends DataFlow::Node { + UrlOpenSink() { sinkNode(this, "open-url") } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess m, HttpString s where source.getNode().asExpr() = s and sink.getNode().asExpr() = m.getQualifier() and - any(HTTPStringToURLOpenMethodFlowConfig c).hasFlowPath(source, sink) + any(HttpStringToUrlOpenMethodFlowConfig c).hasFlowPath(source, sink) select m, source, sink, "URL may have been constructed with HTTP protocol, using $@.", s, "this source" diff --git a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql index c84abe1ff75d..7b026efb7ae2 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-502/UnsafeDeserialization.qhelp b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp index 61b50a986e34..0d1f21ca87fc 100644 --- a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp +++ b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp @@ -69,7 +69,7 @@ Or How I Learned to Start Worrying and Hate Java Object Deserialization.
  • Alvaro MuÃąoz & Christian Schneider, RSAConference 2016: -Serial Killer: Silently Pwning Your Java Endpoints. +Serial Killer: Silently Pwning Your Java Endpoints.
  • SnakeYaml documentation on deserialization: diff --git a/java/ql/src/Security/CWE/CWE-611/XXE.qhelp b/java/ql/src/Security/CWE/CWE-611/XXE.qhelp index 93d420f7495c..c2557883080b 100644 --- a/java/ql/src/Security/CWE/CWE-611/XXE.qhelp +++ b/java/ql/src/Security/CWE/CWE-611/XXE.qhelp @@ -56,11 +56,11 @@ OWASP guidance on parsing xml files:
  • Paper by Timothy Morgen: -XML Schema, DTD, and Entity Attacks +XML Schema, DTD, and Entity Attacks
  • Out-of-band data retrieval: Timur Yunusov & Alexey Osipov, Black hat EU 2013: -XML Out-Of-Band Data Retrieval. +XML Out-Of-Band Data Retrieval.
  • Denial of service attack (Billion laughs): diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql index 4fb3f14c9934..c76527bc5389 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/Dead Code/FinalizerNullsFields.qhelp b/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.qhelp index f85888f651c6..5be6edda989e 100644 --- a/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.qhelp +++ b/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.qhelp @@ -77,7 +77,7 @@ severely affect performance, and you should avoid defining finalize
  • IBM developerWorks: - Java theory and practice: Explicit nulling. + Java theory and practice: Explicit nulling.
  • Oracle Technology Network: 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 40a4e36d70cd..c13956694056 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 7d9ef5b85f93..c6eaf5af2cbf 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/Violations of Best Practice/legacy/ParameterAssignment.qhelp b/java/ql/src/Violations of Best Practice/legacy/ParameterAssignment.qhelp index a4a39d81bc61..260f8f7c9c91 100644 --- a/java/ql/src/Violations of Best Practice/legacy/ParameterAssignment.qhelp +++ b/java/ql/src/Violations of Best Practice/legacy/ParameterAssignment.qhelp @@ -41,7 +41,7 @@ Help - Eclipse Platform:
  • Java Basics: -Methods 4 - Local variables. +Methods 4 - Local variables.
  • 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 000000000000..e201156728a4 --- /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 000000000000..e6965959d13f --- /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 000000000000..4f5defdd948e --- /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 000000000000..9dd5c9c188b4 --- /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 000000000000..89f577f21e59 --- /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 000000000000..5371c51aa8f0 --- /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 000000000000..9bf84f710dc1 --- /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 000000000000..0622c9cd7e31 --- /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 000000000000..8afe77d2a39e --- /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 000000000000..61159a3137f2 --- /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 000000000000..61ddb9b9a1a1 --- /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 000000000000..216bdeaebf38 --- /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 000000000000..bae3ed63d700 --- /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 000000000000..0ee0954216e7 --- /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 000000000000..869b6bc571c6 --- /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 000000000000..e45e3938f7b8 --- /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 000000000000..a8d3cd0fe70e --- /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 000000000000..8190ec3d61f1 --- /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 000000000000..430909743647 --- /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 561d7e46ae90..89d7cb496a41 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 000000000000..3dfaaead68a5 --- /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 000000000000..27afa0fcb497 --- /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 000000000000..48d80707ff83 --- /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 000000000000..ee3e8a4181a9 --- /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 000000000000..4c0dc624d078 --- /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 467d78ae1c48..9fa2fe596fd9 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 000000000000..b8f1a13b119b --- /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 000000000000..8f39efbc2b6a --- /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 000000000000..e8fb89d3989f --- /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 000000000000..71ee842f1627 --- /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 000000000000..6da4f87294a5 --- /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 `