mirror of
https://github.com/github/codeql.git
synced 2026-07-06 20:15:37 +02:00
Compare commits
24 Commits
codeql-cli
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb3d0f5b0e | ||
|
|
09cf8e8b01 | ||
|
|
bd8212c090 | ||
|
|
f106d186e4 | ||
|
|
e2c84407b4 | ||
|
|
67b15125c7 | ||
|
|
caf763a969 | ||
|
|
4f8f5048f3 | ||
|
|
2366679d9b | ||
|
|
66399c055e | ||
|
|
85c02a430e | ||
|
|
29945b8ed0 | ||
|
|
a8ef1bc32a | ||
|
|
0781a138af | ||
|
|
6fd67c4d8e | ||
|
|
89747ecf83 | ||
|
|
c013e3f9c3 | ||
|
|
3b14b27635 | ||
|
|
2ae32be934 | ||
|
|
6647f6b9c4 | ||
|
|
41ceb291de | ||
|
|
615418d2e3 | ||
|
|
0ba76f7d0e | ||
|
|
d97a10ef8a |
30
.github/workflows/close-stale.yml
vendored
30
.github/workflows/close-stale.yml
vendored
@@ -1,30 +0,0 @@
|
|||||||
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
|
|
||||||
11
.github/workflows/codeql-analysis.yml
vendored
11
.github/workflows/codeql-analysis.yml
vendored
@@ -19,18 +19,13 @@ jobs:
|
|||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
pull-requests: read
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@main
|
uses: github/codeql-action/init@v1
|
||||||
# Override language selection by uncommenting this and choosing your languages
|
# Override language selection by uncommenting this and choosing your languages
|
||||||
with:
|
with:
|
||||||
languages: csharp
|
languages: csharp
|
||||||
@@ -39,7 +34,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@main
|
uses: github/codeql-action/autobuild@v1
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -53,4 +48,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@main
|
uses: github/codeql-action/analyze@v1
|
||||||
|
|||||||
@@ -70,4 +70,3 @@
|
|||||||
|
|
||||||
## Changes to libraries
|
## Changes to libraries
|
||||||
* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction.
|
* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction.
|
||||||
* The class `DomBasedXss::Configuration` has been deprecated, as it has been split into `DomBasedXss::HtmlInjectionConfiguration` and `DomBasedXss::JQueryHtmlOrSelectorInjectionConfiguration`. Unless specifically working with jQuery sinks, subclasses should instead be based on `HtmlInjectionConfiguration`. To use both configurations in a query, see [Xss.ql](https://github.com/github/codeql/blob/main/javascript/ql/src/Security/CWE-079/Xss.ql) for an example.
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
|
"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/DataFlowImpl4.qll",
|
||||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.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/DataFlowImpl.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||||
@@ -57,10 +56,6 @@
|
|||||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
|
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||||
"python/ql/src/semmle/python/dataflow/new/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#": [
|
"SsaReadPosition Java/C#": [
|
||||||
"java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
|
"java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
|
||||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
|
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
|
||||||
@@ -440,9 +435,5 @@
|
|||||||
"CryptoAlgorithms Python/JS": [
|
"CryptoAlgorithms Python/JS": [
|
||||||
"javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll",
|
"javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||||
"python/ql/src/semmle/crypto/Crypto.qll"
|
"python/ql/src/semmle/crypto/Crypto.qll"
|
||||||
],
|
|
||||||
"SensitiveDataHeuristics Python/JS": [
|
|
||||||
"javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
|
||||||
"python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
lgtm,codescanning
|
|
||||||
* The 'Unsigned difference expression compared to zero' (cpp/unsigned-difference-expression-compared-zero) query has been improved to produce fewer false positive results.
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
lgtm
|
|
||||||
* The queries cpp/tainted-arithmetic, cpp/uncontrolled-arithmetic, and cpp/arithmetic-with-extreme-values have been improved to produce fewer false positives.
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
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).
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
lgtm,codescanning
|
|
||||||
* The 'Comparison with wider type' (cpp/comparison-with-wider-type) query has been improved to produce fewer false positives.
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
lgtm,codescanning
|
|
||||||
* The `exprMightOverflowPositively` and `exprMightOverflowNegatively` predicates from the `SimpleRangeAnalysis` library now recognize more expressions that might overflow.
|
|
||||||
@@ -39,7 +39,7 @@ then replace all the relevant occurrences in the code.</p>
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
||||||
Chapter 5: Object Life Cycle, Rec 5.4 (<a href="https://web.archive.org/web/20190919025638/https://mongers.org/industrial-c++/">PDF</a>).
|
Chapter 5: Object Life Cycle, Rec 5.4 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://www.securecoding.cert.org/confluence/display/c/DCL06-C.+Use+meaningful+symbolic+constants+to+represent+literal+values">DCL06-C. Use meaningful symbolic constants to represent literal values</a>
|
<a href="https://www.securecoding.cert.org/confluence/display/c/DCL06-C.+Use+meaningful+symbolic+constants+to+represent+literal+values">DCL06-C. Use meaningful symbolic constants to represent literal values</a>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ constant.</p>
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
||||||
Chapter 5: Object Life Cycle, Rec 5.4 (<a href="https://web.archive.org/web/20190919025638/https://mongers.org/industrial-c++/">PDF</a>).
|
Chapter 5: Object Life Cycle, Rec 5.4 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://www.securecoding.cert.org/confluence/display/c/DCL06-C.+Use+meaningful+symbolic+constants+to+represent+literal+values">DCL06-C. Use meaningful symbolic constants to represent literal values</a>
|
<a href="https://www.securecoding.cert.org/confluence/display/c/DCL06-C.+Use+meaningful+symbolic+constants+to+represent+literal+values">DCL06-C. Use meaningful symbolic constants to represent literal values</a>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Review the purpose of the each global variable flagged by this rule and update e
|
|||||||
|
|
||||||
<li>
|
<li>
|
||||||
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
||||||
Chapter 1: Naming, Rec 1.1 (<a href="https://web.archive.org/web/20190919025638/https://mongers.org/industrial-c++/">PDF</a>).
|
Chapter 1: Naming, Rec 1.1 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="http://www.learncpp.com/cpp-tutorial/42-global-variables/">Global variables</a>.
|
<a href="http://www.learncpp.com/cpp-tutorial/42-global-variables/">Global variables</a>.
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ this rule.
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, Rule 4.6. Prentice Hall PTR, 1997.
|
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, Rule 4.6. Prentice Hall PTR, 1997.
|
||||||
(<a href="https://web.archive.org/web/20190919025638/https://mongers.org/industrial-c++/">PDF</a>).
|
(<a href="http://mongers.org/industrial-c++/">PDF</a>).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
cplusplus.com: <a href="http://www.cplusplus.com/doc/tutorial/control/">Control Structures</a>.
|
cplusplus.com: <a href="http://www.cplusplus.com/doc/tutorial/control/">Control Structures</a>.
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
* @tags reliability
|
* @tags reliability
|
||||||
* security
|
* security
|
||||||
* external/cwe/cwe-562
|
* 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
|
import semmle.code.cpp.pointsto.PointsTo
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Check the return value of functions that return status information.
|
|||||||
<references>
|
<references>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
M. Henricson and E. Nyquist, <i>Industrial Strength C++</i>, Chapter 12: Error handling. Prentice Hall PTR, 1997 (<a href="https://web.archive.org/web/20190919025638/https://mongers.org/industrial-c++/">available online</a>).
|
M. Henricson and E. Nyquist, <i>Industrial Strength C++</i>, Chapter 12: Error handling. Prentice Hall PTR, 1997 (<a href="http://mongers.org/industrial-c++/">available online</a>).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
The CERT C Secure Coding Standard: <a href="https://www.securecoding.cert.org/confluence/display/perl/EXP32-PL.+Do+not+ignore+function+return+values">EXP32-PL. Do not ignore function return values</a>.
|
The CERT C Secure Coding Standard: <a href="https://www.securecoding.cert.org/confluence/display/perl/EXP32-PL.+Do+not+ignore+function+return+values">EXP32-PL. Do not ignore function return values</a>.
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
* @id cpp/signed-overflow-check
|
* @id cpp/signed-overflow-check
|
||||||
* @tags correctness
|
* @tags correctness
|
||||||
* security
|
* security
|
||||||
* external/cwe/cwe-128
|
|
||||||
* external/cwe/cwe-190
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|||||||
@@ -7,12 +7,12 @@
|
|||||||
* @kind path-problem
|
* @kind path-problem
|
||||||
* @problem.severity warning
|
* @problem.severity warning
|
||||||
* @precision high
|
* @precision high
|
||||||
* @id cpp/upcast-array-pointer-arithmetic
|
|
||||||
* @tags correctness
|
* @tags correctness
|
||||||
* reliability
|
* reliability
|
||||||
* security
|
* security
|
||||||
* external/cwe/cwe-119
|
* external/cwe/cwe-119
|
||||||
* external/cwe/cwe-843
|
* external/cwe/cwe-843
|
||||||
|
* @id cpp/upcast-array-pointer-arithmetic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
* @tags reliability
|
* @tags reliability
|
||||||
* correctness
|
* correctness
|
||||||
* security
|
* security
|
||||||
* external/cwe/cwe-190
|
|
||||||
* external/cwe/cwe-253
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
* @tags reliability
|
* @tags reliability
|
||||||
* correctness
|
* correctness
|
||||||
* security
|
* security
|
||||||
* external/cwe/cwe-234
|
|
||||||
* external/cwe/cwe-685
|
* external/cwe/cwe-685
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ indication that there may be cases unhandled by the <code>switch</code> statemen
|
|||||||
MSDN Library: <a href="https://docs.microsoft.com/en-us/cpp/cpp/switch-statement-cpp">switch statement (C++)</a>
|
MSDN Library: <a href="https://docs.microsoft.com/en-us/cpp/cpp/switch-statement-cpp">switch statement (C++)</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
M. Henricson and E. Nyquist, <i>Industrial Strength C++</i>, Chapter 4: Control Flow, Rec 4.5. Prentice Hall PTR, 1997 (<a href="https://web.archive.org/web/20190919025638/https://mongers.org/industrial-c++/">available online</a>).
|
M. Henricson and E. Nyquist, <i>Industrial Strength C++</i>, Chapter 4: Control Flow, Rec 4.5. Prentice Hall PTR, 1997 (<a href="http://mongers.org/industrial-c++/">available online</a>).
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
* @id cpp/pointer-overflow-check
|
* @id cpp/pointer-overflow-check
|
||||||
* @tags reliability
|
* @tags reliability
|
||||||
* security
|
* security
|
||||||
* external/cwe/cwe-758
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.dataflow.EscapesTree
|
import semmle.code.cpp.dataflow.EscapesTree
|
||||||
import semmle.code.cpp.models.interfaces.PointerWrapper
|
|
||||||
import semmle.code.cpp.dataflow.DataFlow
|
import semmle.code.cpp.dataflow.DataFlow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,10 +39,6 @@ predicate hasNontrivialConversion(Expr e) {
|
|||||||
e instanceof ParenthesisExpr
|
e instanceof ParenthesisExpr
|
||||||
)
|
)
|
||||||
or
|
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())
|
hasNontrivialConversion(e.getConversion())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
* @tags correctness
|
* @tags correctness
|
||||||
* language-features
|
* language-features
|
||||||
* security
|
* security
|
||||||
* external/cwe/cwe-670
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|||||||
@@ -12,8 +12,6 @@
|
|||||||
* @tags correctness
|
* @tags correctness
|
||||||
* maintainability
|
* maintainability
|
||||||
* security
|
* security
|
||||||
* external/cwe/cwe-234
|
|
||||||
* external/cwe/cwe-685
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ build time: the more included files, the longer the compilation time.</p>
|
|||||||
<a href="http://www.drdobbs.com/cpp/decoupling-c-header-files/212701130">Decoupling C Header Files</a>
|
<a href="http://www.drdobbs.com/cpp/decoupling-c-header-files/212701130">Decoupling C Header Files</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://accu.org/journals/overload/14/72/griffiths_1995/">C++ Best Practice -
|
<a href="https://wiki.hsr.ch/Prog3/files/overload72-FINAL_DesigningHeaderFiles.pdf">C++ Best Practice -
|
||||||
Designing Header Files</a>
|
Designing Header Files</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ they are contributing to unnecessarily long build times and creating artificial
|
|||||||
<a href="http://www.drdobbs.com/cpp/decoupling-c-header-files/212701130">Decoupling C Header Files</a>
|
<a href="http://www.drdobbs.com/cpp/decoupling-c-header-files/212701130">Decoupling C Header Files</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://accu.org/journals/overload/14/72/griffiths_1995/">C++ Best Practice -
|
<a href="https://wiki.hsr.ch/Prog3/files/overload72-FINAL_DesigningHeaderFiles.pdf">C++ Best Practice -
|
||||||
Designing Header Files</a>
|
Designing Header Files</a>
|
||||||
</li>
|
</li>
|
||||||
</references>
|
</references>
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ where
|
|||||||
small = rel.getLesserOperand() and
|
small = rel.getLesserOperand() and
|
||||||
large = rel.getGreaterOperand() and
|
large = rel.getGreaterOperand() and
|
||||||
rel = l.getCondition().getAChild*() and
|
rel = l.getCondition().getAChild*() and
|
||||||
forall(Expr conv | conv = large.getConversion*() |
|
upperBound(large).log2() > getComparisonSize(small) * 8 and
|
||||||
upperBound(conv).log2() > getComparisonSize(small) * 8
|
|
||||||
) and
|
|
||||||
// Ignore cases where the smaller type is int or larger
|
// Ignore cases where the smaller type is int or larger
|
||||||
// These are still bugs, but you should need a very large string or array to
|
// These are still bugs, but you should need a very large string or array to
|
||||||
// trigger them. We will want to disable this for some applications, but it's
|
// trigger them. We will want to disable this for some applications, but it's
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ predicate outOfBoundsExpr(Expr expr, string kind) {
|
|||||||
|
|
||||||
from Expr use, Expr origin, string kind
|
from Expr use, Expr origin, string kind
|
||||||
where
|
where
|
||||||
not use.getUnspecifiedType() instanceof PointerType and
|
|
||||||
outOfBoundsExpr(use, kind) and
|
outOfBoundsExpr(use, kind) and
|
||||||
tainted(origin, use) and
|
tainted(origin, use) and
|
||||||
origin != use and
|
origin != use and
|
||||||
|
|||||||
@@ -12,60 +12,29 @@
|
|||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.commons.Exclusions
|
import semmle.code.cpp.commons.Exclusions
|
||||||
|
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
|
||||||
import semmle.code.cpp.controlflow.Guards
|
import semmle.code.cpp.controlflow.Guards
|
||||||
import semmle.code.cpp.dataflow.DataFlow
|
|
||||||
|
|
||||||
/**
|
/** Holds if `sub` is guarded by a condition which ensures that `left >= right`. */
|
||||||
* Holds if `sub` is guarded by a condition which ensures that
|
|
||||||
* `left >= right`.
|
|
||||||
*/
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
predicate isGuarded(SubExpr sub, Expr left, Expr right) {
|
predicate isGuarded(SubExpr sub, Expr left, Expr right) {
|
||||||
exists(GuardCondition guard, int k |
|
exists(GuardCondition guard |
|
||||||
guard.controls(sub.getBasicBlock(), _) and
|
guard.controls(sub.getBasicBlock(), true) and
|
||||||
guard.ensuresLt(left, right, k, sub.getBasicBlock(), false) and
|
guard.ensuresLt(left, right, 0, sub.getBasicBlock(), false)
|
||||||
k >= 0
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Holds if `sub` will never be negative. */
|
||||||
* Holds if `n` is known or suspected to be less than or equal to
|
predicate nonNegative(SubExpr sub) {
|
||||||
* `sub.getLeftOperand()`.
|
not exprMightOverflowNegatively(sub.getFullyConverted())
|
||||||
*/
|
|
||||||
predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) {
|
|
||||||
n.asExpr() = sub.getLeftOperand()
|
|
||||||
or
|
or
|
||||||
exists(DataFlow::Node other |
|
// The subtraction is guarded by a check of the form `left >= right`.
|
||||||
// dataflow
|
exists(GVN left, GVN right |
|
||||||
exprIsSubLeftOrLess(sub, other) and
|
// This is basically a poor man's version of a directional unbind operator.
|
||||||
(
|
strictcount([left, globalValueNumber(sub.getLeftOperand())]) = 1 and
|
||||||
DataFlow::localFlowStep(n, other) or
|
strictcount([right, globalValueNumber(sub.getRightOperand())]) = 1 and
|
||||||
DataFlow::localFlowStep(other, n)
|
isGuarded(sub, left.getAnExpr(), right.getAnExpr())
|
||||||
)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(DataFlow::Node other |
|
|
||||||
// guard constraining `sub`
|
|
||||||
exprIsSubLeftOrLess(sub, other) and
|
|
||||||
isGuarded(sub, other.asExpr(), n.asExpr()) // other >= n
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(DataFlow::Node other, float p, float q |
|
|
||||||
// linear access of `other`
|
|
||||||
exprIsSubLeftOrLess(sub, other) and
|
|
||||||
linearAccess(n.asExpr(), other.asExpr(), p, q) and // n = p * other + q
|
|
||||||
p <= 1 and
|
|
||||||
q <= 0
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(DataFlow::Node other, float p, float q |
|
|
||||||
// linear access of `n`
|
|
||||||
exprIsSubLeftOrLess(sub, other) and
|
|
||||||
linearAccess(other.asExpr(), n.asExpr(), p, q) and // other = p * n + q
|
|
||||||
p >= 1 and
|
|
||||||
q >= 0
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +45,5 @@ where
|
|||||||
ro.getLesserOperand().getValue().toInt() = 0 and
|
ro.getLesserOperand().getValue().toInt() = 0 and
|
||||||
ro.getGreaterOperand() = sub and
|
ro.getGreaterOperand() = sub and
|
||||||
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and
|
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and
|
||||||
exprMightOverflowNegatively(sub.getFullyConverted()) and // generally catches false positives involving constants
|
not nonNegative(sub)
|
||||||
not exprIsSubLeftOrLess(sub, DataFlow::exprNode(sub.getRightOperand())) // generally catches false positives where there's a relation between the left and right operands
|
|
||||||
select ro, "Unsigned subtraction can never be negative."
|
select ro, "Unsigned subtraction can never be negative."
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
// BAD: the allocation will throw an unhandled exception
|
|
||||||
// instead of returning a null pointer.
|
|
||||||
void bad1(std::size_t length) noexcept {
|
|
||||||
int* dest = new int[length];
|
|
||||||
if(!dest) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::memset(dest, 0, length);
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// BAD: the allocation won't throw an exception, but
|
|
||||||
// instead return a null pointer.
|
|
||||||
void bad2(std::size_t length) noexcept {
|
|
||||||
try {
|
|
||||||
int* dest = new(std::nothrow) int[length];
|
|
||||||
std::memset(dest, 0, length);
|
|
||||||
// ...
|
|
||||||
} catch(std::bad_alloc&) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GOOD: the allocation failure is handled appropiately.
|
|
||||||
void good1(std::size_t length) noexcept {
|
|
||||||
try {
|
|
||||||
int* dest = new int[length];
|
|
||||||
std::memset(dest, 0, length);
|
|
||||||
// ...
|
|
||||||
} catch(std::bad_alloc&) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GOOD: the allocation failure is handled appropiately.
|
|
||||||
void good2(std::size_t length) noexcept {
|
|
||||||
int* dest = new int[length];
|
|
||||||
if(!dest) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::memset(dest, 0, length);
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<!DOCTYPE qhelp PUBLIC
|
|
||||||
"-//Semmle//qhelp//EN"
|
|
||||||
"qhelp.dtd">
|
|
||||||
<qhelp>
|
|
||||||
<overview>
|
|
||||||
<p>Different overloads of the <code>new</code> operator handle allocation failures in different ways.
|
|
||||||
If <code>new T</code> fails for some type <code>T</code>, it throws a <code>std::bad_alloc</code> exception,
|
|
||||||
but <code>new(std::nothrow) T</code> returns a null pointer. If the programmer does not use the corresponding
|
|
||||||
method of error handling, allocation failure may go unhandled and could cause the program to behave in
|
|
||||||
unexpected ways.</p>
|
|
||||||
|
|
||||||
</overview>
|
|
||||||
<recommendation>
|
|
||||||
|
|
||||||
<p>Make sure that exceptions are handled appropriately if <code>new T</code> is used. On the other hand,
|
|
||||||
make sure to handle the possibility of null pointers if <code>new(std::nothrow) T</code> is used.</p>
|
|
||||||
|
|
||||||
</recommendation>
|
|
||||||
<example>
|
|
||||||
<sample src="IncorrectAllocationErrorHandling.cpp" />
|
|
||||||
|
|
||||||
</example>
|
|
||||||
<references>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
CERT C++ Coding Standard:
|
|
||||||
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM52-CPP.+Detect+and+handle+memory+allocation+errors">MEM52-CPP. Detect and handle memory allocation errors</a>.
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</references>
|
|
||||||
</qhelp>
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Incorrect allocation-error handling
|
|
||||||
* @description `operator new` throws an exception on allocation failures, while `operator new(std::nothrow)` returns a null pointer. Mixing up these two failure conditions can result in unexpected behavior.
|
|
||||||
* @kind problem
|
|
||||||
* @id cpp/incorrect-allocation-error-handling
|
|
||||||
* @problem.severity warning
|
|
||||||
* @precision medium
|
|
||||||
* @tags correctness
|
|
||||||
* security
|
|
||||||
* external/cwe/cwe-570
|
|
||||||
*/
|
|
||||||
|
|
||||||
import cpp
|
|
||||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
|
||||||
import semmle.code.cpp.controlflow.Guards
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A C++ `delete` or `delete[]` expression.
|
|
||||||
*/
|
|
||||||
class DeleteOrDeleteArrayExpr extends Expr {
|
|
||||||
DeleteOrDeleteArrayExpr() { this instanceof DeleteExpr or this instanceof DeleteArrayExpr }
|
|
||||||
|
|
||||||
DeallocationFunction getDeallocator() {
|
|
||||||
result = [this.(DeleteExpr).getDeallocator(), this.(DeleteArrayExpr).getDeallocator()]
|
|
||||||
}
|
|
||||||
|
|
||||||
Destructor getDestructor() {
|
|
||||||
result = [this.(DeleteExpr).getDestructor(), this.(DeleteArrayExpr).getDestructor()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the `Constructor` invoked when `newExpr` allocates memory. */
|
|
||||||
Constructor getConstructorForAllocation(NewOrNewArrayExpr newExpr) {
|
|
||||||
result.getACallToThisFunction() = newExpr.getInitializer()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the `Destructor` invoked when `deleteExpr` deallocates memory. */
|
|
||||||
Destructor getDestructorForDeallocation(DeleteOrDeleteArrayExpr deleteExpr) {
|
|
||||||
result = deleteExpr.getDestructor()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if the evaluation of `newExpr` may throw an exception. */
|
|
||||||
predicate newMayThrow(NewOrNewArrayExpr newExpr) {
|
|
||||||
functionMayThrow(newExpr.getAllocator()) or
|
|
||||||
functionMayThrow(getConstructorForAllocation(newExpr))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if the evaluation of `deleteExpr` may throw an exception. */
|
|
||||||
predicate deleteMayThrow(DeleteOrDeleteArrayExpr deleteExpr) {
|
|
||||||
functionMayThrow(deleteExpr.getDeallocator()) or
|
|
||||||
functionMayThrow(getDestructorForDeallocation(deleteExpr))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if the function may throw an exception when called. That is, if the body of the function looks
|
|
||||||
* like it might throw an exception, and the function does not have a `noexcept` or `throw()` specifier.
|
|
||||||
*/
|
|
||||||
predicate functionMayThrow(Function f) {
|
|
||||||
(not exists(f.getBlock()) or stmtMayThrow(f.getBlock())) and
|
|
||||||
not f.isNoExcept() and
|
|
||||||
not f.isNoThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if the evaluation of `stmt` may throw an exception. */
|
|
||||||
predicate stmtMayThrow(Stmt stmt) {
|
|
||||||
stmtMayThrow(stmt.(BlockStmt).getAStmt())
|
|
||||||
or
|
|
||||||
convertedExprMayThrow(stmt.(ExprStmt).getExpr())
|
|
||||||
or
|
|
||||||
convertedExprMayThrow(stmt.(DeclStmt).getADeclaration().(Variable).getInitializer().getExpr())
|
|
||||||
or
|
|
||||||
exists(IfStmt ifStmt | ifStmt = stmt |
|
|
||||||
convertedExprMayThrow(ifStmt.getCondition()) or
|
|
||||||
stmtMayThrow([ifStmt.getThen(), ifStmt.getElse()])
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(ConstexprIfStmt constIfStmt | constIfStmt = stmt |
|
|
||||||
stmtMayThrow([constIfStmt.getThen(), constIfStmt.getElse()])
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(Loop loop | loop = stmt |
|
|
||||||
convertedExprMayThrow(loop.getCondition()) or
|
|
||||||
stmtMayThrow(loop.getStmt())
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// The case for `Loop` already checked the condition and the statement.
|
|
||||||
convertedExprMayThrow(stmt.(RangeBasedForStmt).getUpdate())
|
|
||||||
or
|
|
||||||
// The case for `Loop` already checked the condition and the statement.
|
|
||||||
exists(ForStmt forStmt | forStmt = stmt |
|
|
||||||
stmtMayThrow(forStmt.getInitialization())
|
|
||||||
or
|
|
||||||
convertedExprMayThrow(forStmt.getUpdate())
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(SwitchStmt switchStmt | switchStmt = stmt |
|
|
||||||
convertedExprMayThrow(switchStmt.getExpr()) or
|
|
||||||
stmtMayThrow(switchStmt.getStmt())
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// NOTE: We don't include `TryStmt` as those exceptions are not "observable" outside the function.
|
|
||||||
stmtMayThrow(stmt.(Handler).getBlock())
|
|
||||||
or
|
|
||||||
convertedExprMayThrow(stmt.(CoReturnStmt).getExpr())
|
|
||||||
or
|
|
||||||
convertedExprMayThrow(stmt.(ReturnStmt).getExpr())
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if the evaluation of `e` (including conversions) may throw an exception. */
|
|
||||||
predicate convertedExprMayThrow(Expr e) {
|
|
||||||
exprMayThrow(e)
|
|
||||||
or
|
|
||||||
convertedExprMayThrow(e.getConversion())
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if the evaluation of `e` may throw an exception. */
|
|
||||||
predicate exprMayThrow(Expr e) {
|
|
||||||
e instanceof DynamicCast
|
|
||||||
or
|
|
||||||
e instanceof TypeidOperator
|
|
||||||
or
|
|
||||||
e instanceof ThrowExpr
|
|
||||||
or
|
|
||||||
newMayThrow(e)
|
|
||||||
or
|
|
||||||
deleteMayThrow(e)
|
|
||||||
or
|
|
||||||
convertedExprMayThrow(e.(UnaryOperation).getOperand())
|
|
||||||
or
|
|
||||||
exists(BinaryOperation binOp | binOp = e |
|
|
||||||
convertedExprMayThrow([binOp.getLeftOperand(), binOp.getRightOperand()])
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(Assignment assign | assign = e |
|
|
||||||
convertedExprMayThrow([assign.getLValue(), assign.getRValue()])
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(CommaExpr comma | comma = e |
|
|
||||||
convertedExprMayThrow([comma.getLeftOperand(), comma.getRightOperand()])
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(StmtExpr stmtExpr | stmtExpr = e |
|
|
||||||
convertedExprMayThrow(stmtExpr.getResultExpr()) or
|
|
||||||
stmtMayThrow(stmtExpr.getStmt())
|
|
||||||
)
|
|
||||||
or
|
|
||||||
convertedExprMayThrow(e.(Conversion).getExpr())
|
|
||||||
or
|
|
||||||
exists(FunctionCall fc | fc = e |
|
|
||||||
not exists(fc.getTarget()) or
|
|
||||||
functionMayThrow(fc.getTarget()) or
|
|
||||||
convertedExprMayThrow(fc.getAnArgument())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** An allocator that might throw an exception. */
|
|
||||||
class ThrowingAllocator extends Function {
|
|
||||||
ThrowingAllocator() {
|
|
||||||
exists(NewOrNewArrayExpr newExpr |
|
|
||||||
newExpr.getAllocator() = this and
|
|
||||||
functionMayThrow(this)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The `std::bad_alloc` exception and its `bsl` variant. */
|
|
||||||
class BadAllocType extends Class {
|
|
||||||
BadAllocType() { this.hasGlobalOrStdOrBslName("bad_alloc") }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A catch block that catches a `std::bad_alloc` (or any of its superclasses), or a catch
|
|
||||||
* block that catches every exception (i.e., `catch(...)`).
|
|
||||||
*/
|
|
||||||
class BadAllocCatchBlock extends CatchBlock {
|
|
||||||
BadAllocCatchBlock() {
|
|
||||||
this.getParameter().getUnspecifiedType().stripType() =
|
|
||||||
any(BadAllocType badAlloc).getABaseClass*()
|
|
||||||
or
|
|
||||||
not exists(this.getParameter())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `newExpr` is embedded in a `try` statement with a catch block `catchBlock` that
|
|
||||||
* catches a `std::bad_alloc` exception, but nothing in the `try` block (including the `newExpr`)
|
|
||||||
* will throw that exception.
|
|
||||||
*/
|
|
||||||
predicate noThrowInTryBlock(NewOrNewArrayExpr newExpr, BadAllocCatchBlock catchBlock) {
|
|
||||||
exists(TryStmt try |
|
|
||||||
not stmtMayThrow(try.getStmt()) and
|
|
||||||
try.getACatchClause() = catchBlock and
|
|
||||||
newExpr.getEnclosingBlock().getEnclosingBlock*() = try.getStmt()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `newExpr` is handles allocation failures by throwing an exception, yet
|
|
||||||
* the guard condition `guard` compares the result of `newExpr` to a null value.
|
|
||||||
*/
|
|
||||||
predicate nullCheckInThrowingNew(NewOrNewArrayExpr newExpr, GuardCondition guard) {
|
|
||||||
newExpr.getAllocator() instanceof ThrowingAllocator and
|
|
||||||
(
|
|
||||||
// Handles null comparisons.
|
|
||||||
guard.ensuresEq(globalValueNumber(newExpr).getAnExpr(), any(NullValue null), _, _, _)
|
|
||||||
or
|
|
||||||
// Handles `if(ptr)` and `if(!ptr)` cases.
|
|
||||||
guard = globalValueNumber(newExpr).getAnExpr()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
from NewOrNewArrayExpr newExpr, Element element, string msg, string elementString
|
|
||||||
where
|
|
||||||
not newExpr.isFromUninstantiatedTemplate(_) and
|
|
||||||
(
|
|
||||||
noThrowInTryBlock(newExpr, element) and
|
|
||||||
msg = "This allocation cannot throw. $@ is unnecessary." and
|
|
||||||
elementString = "This catch block"
|
|
||||||
or
|
|
||||||
nullCheckInThrowingNew(newExpr, element) and
|
|
||||||
msg = "This allocation cannot return null. $@ is unnecessary." and
|
|
||||||
elementString = "This check"
|
|
||||||
)
|
|
||||||
select newExpr, msg, element, elementString
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// BAD: on memory allocation error, the program terminates.
|
||||||
|
void badFunction(const int *source, std::size_t length) noexcept {
|
||||||
|
int * dest = new int[length];
|
||||||
|
std::memset(dest, 0, length);
|
||||||
|
// ..
|
||||||
|
}
|
||||||
|
// GOOD: memory allocation error will be handled.
|
||||||
|
void goodFunction(const int *source, std::size_t length) noexcept {
|
||||||
|
try {
|
||||||
|
int * dest = new int[length];
|
||||||
|
} catch(std::bad_alloc) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
std::memset(dest, 0, length);
|
||||||
|
// ..
|
||||||
|
}
|
||||||
|
// BAD: memory allocation error will not be handled.
|
||||||
|
void badFunction(const int *source, std::size_t length) noexcept {
|
||||||
|
try {
|
||||||
|
int * dest = new (std::nothrow) int[length];
|
||||||
|
} catch(std::bad_alloc) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
std::memset(dest, 0, length);
|
||||||
|
// ..
|
||||||
|
}
|
||||||
|
// GOOD: memory allocation error will be handled.
|
||||||
|
void goodFunction(const int *source, std::size_t length) noexcept {
|
||||||
|
int * dest = new (std::nothrow) int[length];
|
||||||
|
if (!dest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::memset(dest, 0, length);
|
||||||
|
// ..
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>When using the <code>new</code> operator to allocate memory, you need to pay attention to the different ways of detecting errors. <code>::operator new(std::size_t)</code> throws an exception on error, whereas <code>::operator new(std::size_t, const std::nothrow_t &)</code> returns zero on error. The programmer can get confused and check the error that occurs when allocating memory incorrectly. That can lead to an unhandled program termination or to a violation of the program logic.</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>Use the correct error detection method corresponding with the memory allocation.</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
<p>The following example demonstrates various approaches to detecting memory allocation errors using the <code>new</code> operator.</p>
|
||||||
|
<sample src="WrongInDetectingAndHandlingMemoryAllocationErrors.cpp" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
CERT C++ Coding Standard:
|
||||||
|
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM52-CPP.+Detect+and+handle+memory+allocation+errors">MEM52-CPP. Detect and handle memory allocation errors</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* @name Detect And Handle Memory Allocation Errors
|
||||||
|
* @description --::operator new(std::size_t) throws an exception on error, and ::operator new(std::size_t, const std::nothrow_t &) returns zero on error.
|
||||||
|
* --the programmer can get confused when check the error that occurs when allocating memory incorrectly.
|
||||||
|
* @kind problem
|
||||||
|
* @id cpp/detect-and-handle-memory-allocation-errors
|
||||||
|
* @problem.severity warning
|
||||||
|
* @precision medium
|
||||||
|
* @tags correctness
|
||||||
|
* security
|
||||||
|
* external/cwe/cwe-570
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup if condition compare with 0
|
||||||
|
*/
|
||||||
|
class IfCompareWithZero extends IfStmt {
|
||||||
|
IfCompareWithZero() {
|
||||||
|
this.getCondition().(EQExpr).getAChild().getValue() = "0"
|
||||||
|
or
|
||||||
|
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
|
||||||
|
this.hasElse()
|
||||||
|
or
|
||||||
|
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
|
||||||
|
this.getThen().getAChild*() instanceof ReturnStmt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lookup for calls to `operator new`, with incorrect error handling.
|
||||||
|
*/
|
||||||
|
class WrongCheckErrorOperatorNew extends FunctionCall {
|
||||||
|
Expr exp;
|
||||||
|
|
||||||
|
WrongCheckErrorOperatorNew() {
|
||||||
|
this = exp.(NewOrNewArrayExpr).getAChild().(FunctionCall) and
|
||||||
|
(
|
||||||
|
this.getTarget().hasGlobalOrStdName("operator new")
|
||||||
|
or
|
||||||
|
this.getTarget().hasGlobalOrStdName("operator new[]")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if handler `try ... catch` exists.
|
||||||
|
*/
|
||||||
|
predicate isExistsTryCatchBlock() {
|
||||||
|
exists(TryStmt ts | this.getEnclosingStmt() = ts.getStmt().getAChild*())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if results call `operator new` check in `operator if`.
|
||||||
|
*/
|
||||||
|
predicate isExistsIfCondition() {
|
||||||
|
exists(IfCompareWithZero ifc, AssignExpr aex, Initializer it |
|
||||||
|
// call `operator new` directly from the condition of `operator if`.
|
||||||
|
this = ifc.getCondition().getAChild*()
|
||||||
|
or
|
||||||
|
// check results call `operator new` with variable appropriation
|
||||||
|
postDominates(ifc, this) and
|
||||||
|
aex.getAChild() = exp and
|
||||||
|
ifc.getCondition().getAChild().(VariableAccess).getTarget() =
|
||||||
|
aex.getLValue().(VariableAccess).getTarget()
|
||||||
|
or
|
||||||
|
// check results call `operator new` with declaration variable
|
||||||
|
postDominates(ifc, this) and
|
||||||
|
exp = it.getExpr() and
|
||||||
|
it.getDeclaration() = ifc.getCondition().getAChild().(VariableAccess).getTarget()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `(std::nothrow)` or `(std::noexcept)` exists in call `operator new`.
|
||||||
|
*/
|
||||||
|
predicate isExistsNothrow() { getTarget().isNoExcept() or getTarget().isNoThrow() }
|
||||||
|
}
|
||||||
|
|
||||||
|
from WrongCheckErrorOperatorNew op
|
||||||
|
where
|
||||||
|
// use call `operator new` with `(std::nothrow)` and checking error using `try ... catch` block and not `operator if`
|
||||||
|
op.isExistsNothrow() and not op.isExistsIfCondition() and op.isExistsTryCatchBlock()
|
||||||
|
or
|
||||||
|
// use call `operator new` without `(std::nothrow)` and checking error using `operator if` and not `try ... catch` block
|
||||||
|
not op.isExistsNothrow() and not op.isExistsTryCatchBlock() and op.isExistsIfCondition()
|
||||||
|
select op, "memory allocation error check is incorrect or missing"
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<!DOCTYPE qhelp PUBLIC
|
|
||||||
"-//Semmle//qhelp//EN"
|
|
||||||
"qhelp.dtd">
|
|
||||||
<qhelp>
|
|
||||||
<overview>
|
|
||||||
<p>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.</p>
|
|
||||||
|
|
||||||
|
|
||||||
</overview>
|
|
||||||
<recommendation>
|
|
||||||
|
|
||||||
<p>We recommend that you use more explicit code transformations.</p>
|
|
||||||
|
|
||||||
</recommendation>
|
|
||||||
<example>
|
|
||||||
<p>The following example demonstrates the erroneous and corrected sections of the code.</p>
|
|
||||||
<sample src="InsufficientControlFlowManagementAfterRefactoringTheCode.c" />
|
|
||||||
|
|
||||||
</example>
|
|
||||||
<references>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
CWE Common Weakness Enumeration:
|
|
||||||
<a href="https://cwe.mitre.org/data/definitions/691.html"> CWE-691: Insufficient Control Flow Management</a>.
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</references>
|
|
||||||
</qhelp>
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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"
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
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.
|
|
||||||
...
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<!DOCTYPE qhelp PUBLIC
|
|
||||||
"-//Semmle//qhelp//EN"
|
|
||||||
"qhelp.dtd">
|
|
||||||
<qhelp>
|
|
||||||
<overview>
|
|
||||||
<p>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.</p>
|
|
||||||
|
|
||||||
|
|
||||||
</overview>
|
|
||||||
<recommendation>
|
|
||||||
|
|
||||||
<p>We recommend that you evaluate the correctness of using the specified bit operations.</p>
|
|
||||||
|
|
||||||
</recommendation>
|
|
||||||
<example>
|
|
||||||
<p>The following example demonstrates the erroneous and fixed use of bit and logical operations.</p>
|
|
||||||
<sample src="InsufficientControlFlowManagementWhenUsingBitOperations.c" />
|
|
||||||
|
|
||||||
</example>
|
|
||||||
<references>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
CWE Common Weakness Enumeration:
|
|
||||||
<a href="https://cwe.mitre.org/data/definitions/691.html"> CWE-691: Insufficient Control Flow Management</a>.
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</references>
|
|
||||||
</qhelp>
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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."
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
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`
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<!DOCTYPE qhelp PUBLIC
|
|
||||||
"-//Semmle//qhelp//EN"
|
|
||||||
"qhelp.dtd">
|
|
||||||
<qhelp>
|
|
||||||
<overview>
|
|
||||||
<p>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.</p>
|
|
||||||
|
|
||||||
|
|
||||||
</overview>
|
|
||||||
<recommendation>
|
|
||||||
|
|
||||||
<p>we recommend making the code simpler.</p>
|
|
||||||
|
|
||||||
</recommendation>
|
|
||||||
<example>
|
|
||||||
<p>The following example demonstrates erroneous and fixed methods for using a boolean data type.</p>
|
|
||||||
<sample src="OperatorPrecedenceLogicErrorWhenUseBoolType.c" />
|
|
||||||
|
|
||||||
</example>
|
|
||||||
<references>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
CERT C Coding Standard:
|
|
||||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP00-C.+Use+parentheses+for+precedence+of+operation">EXP00-C. Use parentheses for precedence of operation</a>.
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</references>
|
|
||||||
</qhelp>
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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"
|
|
||||||
@@ -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.
|
* @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.
|
* If terminal zero is present, then the specified expression is meaningless.
|
||||||
* @kind problem
|
* @kind problem
|
||||||
* @id cpp/access-memory-location-after-end-buffer-strlen
|
* @id cpp/access-memory-location-after-end-buffer
|
||||||
* @problem.severity warning
|
* @problem.severity warning
|
||||||
* @precision medium
|
* @precision medium
|
||||||
* @tags correctness
|
* @tags correctness
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @name Access Of Memory Location After The End Of A Buffer Using Strncat
|
* @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.
|
* @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
|
* @kind problem
|
||||||
* @id cpp/access-memory-location-after-end-buffer-strncat
|
* @id cpp/access-memory-location-after-end-buffer
|
||||||
* @problem.severity warning
|
* @problem.severity warning
|
||||||
* @precision medium
|
* @precision medium
|
||||||
* @tags correctness
|
* @tags correctness
|
||||||
@@ -11,32 +11,54 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.models.implementations.Strcat
|
|
||||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `call` is a call to `strncat` such that `sizeArg` and `destArg` are the size and
|
* A call to `strncat` of the form `strncat(buff, str, someExpr - strlen(buf))`, for some expression `someExpr` equal to `sizeof(buff)`.
|
||||||
* destination arguments, respectively.
|
|
||||||
*/
|
*/
|
||||||
predicate interestringCallWithArgs(Call call, Expr sizeArg, Expr destArg) {
|
class WrongCallStrncat extends FunctionCall {
|
||||||
exists(StrcatFunction strcat |
|
Expr leftsomeExpr;
|
||||||
strcat = call.getTarget() and
|
|
||||||
sizeArg = call.getArgument(strcat.getParamSize()) and
|
WrongCallStrncat() {
|
||||||
destArg = call.getArgument(strcat.getParamDest())
|
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()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
from FunctionCall call, Expr sizeArg, Expr destArg, SubExpr sub, int n
|
from WrongCallStrncat sc
|
||||||
where
|
where
|
||||||
interestringCallWithArgs(call, sizeArg, destArg) and
|
sc.isExpressionEqualSizeof() or
|
||||||
// The destination buffer is an array of size n
|
sc.isVariableEqualValueSizegBuffer()
|
||||||
destArg.getUnspecifiedType().(ArrayType).getSize() = n and
|
select sc, "if the used buffer is full, writing out of the buffer is possible"
|
||||||
// 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."
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ the break statement only exits from one level of the loop.</p>
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
||||||
Chapter 4: Control Flow, Rule 4.6 (<a href="https://web.archive.org/web/20190919025638/https://mongers.org/industrial-c++/">PDF</a>).
|
Chapter 4: Control Flow, Rule 4.6 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="http://www.cplusplus.com/doc/tutorial/control/">www.cplusplus.com Control Structures</a>
|
<a href="http://www.cplusplus.com/doc/tutorial/control/">www.cplusplus.com Control Structures</a>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ loop if the loop requires more complicated variable iteration.
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
||||||
Chapter 4: Control Flow, Rule 4.1 (<a href="https://web.archive.org/web/20190919025638/https://mongers.org/industrial-c++/">PDF</a>).
|
Chapter 4: Control Flow, Rule 4.1 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ class Type extends Locatable, @type {
|
|||||||
*
|
*
|
||||||
* For example, starting with `const i64* const` in the context of `typedef long long i64;`, this predicate will return `long long*`.
|
* For example, starting with `const i64* const` in the context of `typedef long long i64;`, this predicate will return `long long*`.
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
|
||||||
Type getUnspecifiedType() { unspecifiedtype(underlyingElement(this), unresolveElement(result)) }
|
Type getUnspecifiedType() { unspecifiedtype(underlyingElement(this), unresolveElement(result)) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import cpp
|
|||||||
* In rare cases, the same node is used in multiple control-flow scopes. This
|
* In rare cases, the same node is used in multiple control-flow scopes. This
|
||||||
* confuses the dominance analysis, so this predicate is used to exclude them.
|
* confuses the dominance analysis, so this predicate is used to exclude them.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
|
||||||
private predicate hasMultiScopeNode(Function f) {
|
private predicate hasMultiScopeNode(Function f) {
|
||||||
exists(ControlFlowNode node |
|
exists(ControlFlowNode node |
|
||||||
node.getControlFlowScope() = f and
|
node.getControlFlowScope() = f and
|
||||||
|
|||||||
@@ -1307,8 +1307,7 @@ private predicate conditionJumps(Expr test, boolean truth, Node n2, Pos p2) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pulled out for performance. See
|
// Factored out for performance. See QL-796.
|
||||||
// https://github.com/github/codeql-coreql-team/issues/1044.
|
|
||||||
private predicate normalGroupMemberBaseCase(Node memberNode, Pos memberPos, Node atNode) {
|
private predicate normalGroupMemberBaseCase(Node memberNode, Pos memberPos, Node atNode) {
|
||||||
memberNode = atNode and
|
memberNode = atNode and
|
||||||
memberPos.isAt() and
|
memberPos.isAt() and
|
||||||
|
|||||||
@@ -104,43 +104,9 @@ 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. */
|
/** A function call that *may* return; if in doubt, we assume it may. */
|
||||||
private predicate potentiallyReturningFunctionCall(FunctionCall fc) {
|
private predicate potentiallyReturningFunctionCall(FunctionCall fc) {
|
||||||
potentiallyReturningFunctionCall_base(fc)
|
potentiallyReturningFunction(fc.getTarget()) or fc.isVirtual()
|
||||||
or
|
|
||||||
potentiallyReturningFunction(fc.getTarget())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A function that *may* return; if in doubt, we assume it may. */
|
/** A function that *may* return; if in doubt, we assume it may. */
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import cpp
|
private import cpp
|
||||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `f` is an instantiation of the `std::move` or `std::forward`
|
* Holds if `f` is an instantiation of the `std::move` or `std::forward`
|
||||||
@@ -95,12 +94,6 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
|
|||||||
|
|
||||||
private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
|
private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
|
||||||
lvalueIn.getConversion() = referenceOut.(ReferenceToExpr)
|
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) {
|
private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
|
||||||
@@ -113,13 +106,6 @@ private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
|
|||||||
stdAddressOf(call.getTarget()) and
|
stdAddressOf(call.getTarget()) and
|
||||||
referenceIn = call.getArgument(0).getFullyConverted()
|
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) {
|
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
|
||||||
@@ -204,19 +190,6 @@ private predicate pointerToUpdate(Expr pointer, Expr outer, ControlFlowNode node
|
|||||||
// See the `lvalueToUpdate` case for an explanation of this conjunct.
|
// See the `lvalueToUpdate` case for an explanation of this conjunct.
|
||||||
call.getType().isDeeplyConstBelow()
|
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
|
or
|
||||||
exists(PointerFieldAccess fa |
|
exists(PointerFieldAccess fa |
|
||||||
@@ -245,9 +218,7 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode
|
|||||||
not stdIdentityFunction(call.getTarget()) and
|
not stdIdentityFunction(call.getTarget()) and
|
||||||
not stdAddressOf(call.getTarget()) and
|
not stdAddressOf(call.getTarget()) and
|
||||||
exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() |
|
exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() |
|
||||||
not rt.getBaseType().isConst() or
|
not rt.getBaseType().isConst()
|
||||||
rt.getBaseType().getUnspecifiedType() =
|
|
||||||
any(PointerWrapper wrapper | not wrapper.pointsToConst())
|
|
||||||
)
|
)
|
||||||
) and
|
) and
|
||||||
reference = outer
|
reference = outer
|
||||||
|
|||||||
@@ -2133,8 +2133,11 @@ private module Stage4 {
|
|||||||
|
|
||||||
bindingset[node, cc, config]
|
bindingset[node, cc, config]
|
||||||
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
||||||
localFlowEntry(node, config) and
|
exists(Cc cc0 |
|
||||||
result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
|
cc = pragma[only_bind_into](cc0) and
|
||||||
|
localFlowEntry(node, config) and
|
||||||
|
result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate localStep(
|
private predicate localStep(
|
||||||
@@ -3129,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
|||||||
conf = mid.getConfiguration() and
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
sc = mid.getSummaryCtx() and
|
sc = mid.getSummaryCtx() and
|
||||||
localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
|
localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
|
||||||
ap0 = mid.getAp()
|
ap0 = mid.getAp()
|
||||||
|
|
|
|
||||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||||
|
|||||||
@@ -2133,8 +2133,11 @@ private module Stage4 {
|
|||||||
|
|
||||||
bindingset[node, cc, config]
|
bindingset[node, cc, config]
|
||||||
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
||||||
localFlowEntry(node, config) and
|
exists(Cc cc0 |
|
||||||
result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
|
cc = pragma[only_bind_into](cc0) and
|
||||||
|
localFlowEntry(node, config) and
|
||||||
|
result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate localStep(
|
private predicate localStep(
|
||||||
@@ -3129,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
|||||||
conf = mid.getConfiguration() and
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
sc = mid.getSummaryCtx() and
|
sc = mid.getSummaryCtx() and
|
||||||
localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
|
localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
|
||||||
ap0 = mid.getAp()
|
ap0 = mid.getAp()
|
||||||
|
|
|
|
||||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||||
|
|||||||
@@ -2133,8 +2133,11 @@ private module Stage4 {
|
|||||||
|
|
||||||
bindingset[node, cc, config]
|
bindingset[node, cc, config]
|
||||||
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
||||||
localFlowEntry(node, config) and
|
exists(Cc cc0 |
|
||||||
result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
|
cc = pragma[only_bind_into](cc0) and
|
||||||
|
localFlowEntry(node, config) and
|
||||||
|
result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate localStep(
|
private predicate localStep(
|
||||||
@@ -3129,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
|||||||
conf = mid.getConfiguration() and
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
sc = mid.getSummaryCtx() and
|
sc = mid.getSummaryCtx() and
|
||||||
localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
|
localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
|
||||||
ap0 = mid.getAp()
|
ap0 = mid.getAp()
|
||||||
|
|
|
|
||||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||||
|
|||||||
@@ -2133,8 +2133,11 @@ private module Stage4 {
|
|||||||
|
|
||||||
bindingset[node, cc, config]
|
bindingset[node, cc, config]
|
||||||
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
||||||
localFlowEntry(node, config) and
|
exists(Cc cc0 |
|
||||||
result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
|
cc = pragma[only_bind_into](cc0) and
|
||||||
|
localFlowEntry(node, config) and
|
||||||
|
result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate localStep(
|
private predicate localStep(
|
||||||
@@ -3129,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
|||||||
conf = mid.getConfiguration() and
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
sc = mid.getSummaryCtx() and
|
sc = mid.getSummaryCtx() and
|
||||||
localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
|
localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
|
||||||
ap0 = mid.getAp()
|
ap0 = mid.getAp()
|
||||||
|
|
|
|
||||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
|
|||||||
* currently excludes read-steps, store-steps, and flow-through.
|
* currently excludes read-steps, store-steps, and flow-through.
|
||||||
*
|
*
|
||||||
* The analysis uses non-linear recursion: When computing a flow path in or out
|
* The analysis uses non-linear recursion: When computing a flow path in or out
|
||||||
* of a call, we use the results of the analysis recursively to resolve lambda
|
* of a call, we use the results of the analysis recursively to resolve lamba
|
||||||
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
|
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
|
||||||
*/
|
*/
|
||||||
private module LambdaFlow {
|
private module LambdaFlow {
|
||||||
|
|||||||
@@ -2133,8 +2133,11 @@ private module Stage4 {
|
|||||||
|
|
||||||
bindingset[node, cc, config]
|
bindingset[node, cc, config]
|
||||||
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
||||||
localFlowEntry(node, config) and
|
exists(Cc cc0 |
|
||||||
result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
|
cc = pragma[only_bind_into](cc0) and
|
||||||
|
localFlowEntry(node, config) and
|
||||||
|
result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate localStep(
|
private predicate localStep(
|
||||||
@@ -3129,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
|||||||
conf = mid.getConfiguration() and
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
sc = mid.getSummaryCtx() and
|
sc = mid.getSummaryCtx() and
|
||||||
localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
|
localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
|
||||||
ap0 = mid.getAp()
|
ap0 = mid.getAp()
|
||||||
|
|
|
|
||||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||||
|
|||||||
@@ -321,5 +321,5 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
|
|||||||
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
||||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
||||||
|
|
||||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
/** Extra data-flow steps needed for lamba flow analysis. */
|
||||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class Node extends TNode {
|
|||||||
/**
|
/**
|
||||||
* INTERNAL: Do not use. Alternative name for `getFunction`.
|
* INTERNAL: Do not use. Alternative name for `getFunction`.
|
||||||
*/
|
*/
|
||||||
final Function getEnclosingCallable() { result = this.getFunction() }
|
final Function getEnclosingCallable() { result = unique(Function f | f = this.getFunction() | f) }
|
||||||
|
|
||||||
/** Gets the type of this node. */
|
/** Gets the type of this node. */
|
||||||
Type getType() { none() } // overridden in subclasses
|
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
|
* A synthetic data flow node used for flow into a collection when an iterator
|
||||||
* write occurs in a callee.
|
* write occurs in a callee.
|
||||||
*/
|
*/
|
||||||
private class IteratorPartialDefinitionNode extends PartialDefinitionNode {
|
class IteratorPartialDefinitionNode extends PartialDefinitionNode {
|
||||||
override IteratorPartialDefinition pd;
|
override IteratorPartialDefinition pd;
|
||||||
|
|
||||||
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
|
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
|
||||||
@@ -694,12 +694,7 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
|||||||
fromExpr = call.getQualifier()
|
fromExpr = call.getQualifier()
|
||||||
) and
|
) and
|
||||||
call.getTarget() = f and
|
call.getTarget() = f and
|
||||||
// AST dataflow treats a reference as if it were the referred-to object, while the dataflow
|
outModel.isReturnValue()
|
||||||
// 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()
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -720,7 +715,6 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private module FieldFlow {
|
private module FieldFlow {
|
||||||
private import DataFlowImplCommon
|
|
||||||
private import DataFlowImplLocal
|
private import DataFlowImplLocal
|
||||||
private import DataFlowPrivate
|
private import DataFlowPrivate
|
||||||
|
|
||||||
@@ -753,7 +747,7 @@ private module FieldFlow {
|
|||||||
exists(FieldConfiguration cfg | cfg.hasFlow(node1, node2)) and
|
exists(FieldConfiguration cfg | cfg.hasFlow(node1, node2)) and
|
||||||
// This configuration should not be able to cross function boundaries, but
|
// This configuration should not be able to cross function boundaries, but
|
||||||
// we double-check here just to be sure.
|
// we double-check here just to be sure.
|
||||||
getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2)
|
node1.getEnclosingCallable() = node2.getEnclosingCallable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ private import semmle.code.cpp.controlflow.SSA
|
|||||||
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
|
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
|
||||||
private import semmle.code.cpp.dataflow.internal.AddressFlow
|
private import semmle.code.cpp.dataflow.internal.AddressFlow
|
||||||
private import semmle.code.cpp.models.implementations.Iterator
|
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
|
* A conceptual variable that is assigned only once, like an SSA variable. This
|
||||||
@@ -159,14 +158,18 @@ private module PartialDefinitions {
|
|||||||
Expr innerDefinedExpr;
|
Expr innerDefinedExpr;
|
||||||
|
|
||||||
IteratorPartialDefinition() {
|
IteratorPartialDefinition() {
|
||||||
innerDefinedExpr = getInnerDefinedExpr(this, node) and
|
exists(Expr convertedInner |
|
||||||
(
|
not this instanceof Conversion and
|
||||||
innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection)
|
valueToUpdate(convertedInner, this.getFullyConverted(), node) and
|
||||||
or
|
innerDefinedExpr = convertedInner.getUnconverted() and
|
||||||
innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and
|
(
|
||||||
collection instanceof IteratorParameter
|
innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection)
|
||||||
) and
|
or
|
||||||
innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator
|
innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and
|
||||||
|
collection instanceof IteratorParameter
|
||||||
|
) and
|
||||||
|
innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator
|
||||||
|
)
|
||||||
or
|
or
|
||||||
// iterators passed by value without a copy constructor
|
// iterators passed by value without a copy constructor
|
||||||
exists(Call call |
|
exists(Call call |
|
||||||
@@ -204,18 +207,16 @@ 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 {
|
class VariablePartialDefinition extends PartialDefinition {
|
||||||
Expr innerDefinedExpr;
|
Expr innerDefinedExpr;
|
||||||
|
|
||||||
VariablePartialDefinition() { innerDefinedExpr = getInnerDefinedExpr(this, node) }
|
VariablePartialDefinition() {
|
||||||
|
not this instanceof Conversion and
|
||||||
|
exists(Expr convertedInner |
|
||||||
|
valueToUpdate(convertedInner, this.getFullyConverted(), node) and
|
||||||
|
innerDefinedExpr = convertedInner.getUnconverted()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
deprecated override predicate partiallyDefines(Variable v) {
|
deprecated override predicate partiallyDefines(Variable v) {
|
||||||
innerDefinedExpr = v.getAnAccess()
|
innerDefinedExpr = v.getAnAccess()
|
||||||
@@ -295,8 +296,7 @@ module FlowVar_internal {
|
|||||||
// treating them as immutable, but for data flow it gives better results in
|
// treating them as immutable, but for data flow it gives better results in
|
||||||
// practice to make the variable synonymous with its contents.
|
// practice to make the variable synonymous with its contents.
|
||||||
not v.getUnspecifiedType() instanceof ReferenceType and
|
not v.getUnspecifiedType() instanceof ReferenceType and
|
||||||
not v instanceof IteratorParameter and
|
not v instanceof IteratorParameter
|
||||||
not v instanceof PointerWrapperParameter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -644,19 +644,10 @@ module FlowVar_internal {
|
|||||||
predicate parameterIsNonConstReference(Parameter p) {
|
predicate parameterIsNonConstReference(Parameter p) {
|
||||||
exists(ReferenceType refType |
|
exists(ReferenceType refType |
|
||||||
refType = p.getUnderlyingType() and
|
refType = p.getUnderlyingType() and
|
||||||
(
|
not refType.getBaseType().isConst()
|
||||||
not refType.getBaseType().isConst()
|
|
||||||
or
|
|
||||||
// A field of a parameter of type `const std::shared_ptr<A>& 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
|
or
|
||||||
p instanceof IteratorParameter
|
p instanceof IteratorParameter
|
||||||
or
|
|
||||||
p instanceof PointerWrapperParameter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -845,10 +836,6 @@ module FlowVar_internal {
|
|||||||
IteratorParameter() { this.getUnspecifiedType() instanceof Iterator }
|
IteratorParameter() { this.getUnspecifiedType() instanceof Iterator }
|
||||||
}
|
}
|
||||||
|
|
||||||
class PointerWrapperParameter extends Parameter {
|
|
||||||
PointerWrapperParameter() { this.getUnspecifiedType() instanceof PointerWrapper }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `v` is initialized to have value `assignedExpr`.
|
* Holds if `v` is initialized to have value `assignedExpr`.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||||
private import semmle.code.cpp.models.interfaces.Taint
|
private import semmle.code.cpp.models.interfaces.Taint
|
||||||
private import semmle.code.cpp.models.interfaces.Iterator
|
private import semmle.code.cpp.models.interfaces.Iterator
|
||||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
|
||||||
|
|
||||||
private module DataFlow {
|
private module DataFlow {
|
||||||
import semmle.code.cpp.dataflow.internal.DataFlowUtil
|
import semmle.code.cpp.dataflow.internal.DataFlowUtil
|
||||||
@@ -142,10 +141,7 @@ private predicate noFlowFromChildExpr(Expr e) {
|
|||||||
or
|
or
|
||||||
e instanceof LogicalOrExpr
|
e instanceof LogicalOrExpr
|
||||||
or
|
or
|
||||||
// Allow taint from `operator*` on smart pointers.
|
e instanceof Call
|
||||||
exists(Call call | e = call |
|
|
||||||
not call.getTarget() = any(PointerWrapper wrapper).getAnUnwrapperFunction()
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
e instanceof SizeofOperator
|
e instanceof SizeofOperator
|
||||||
or
|
or
|
||||||
|
|||||||
@@ -314,7 +314,6 @@ class OverloadedPointerDereferenceFunction extends Function {
|
|||||||
* T1 operator*(const T2 &);
|
* T1 operator*(const T2 &);
|
||||||
* T1 a; T2 b;
|
* T1 a; T2 b;
|
||||||
* a = *b;
|
* a = *b;
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
class OverloadedPointerDereferenceExpr extends FunctionCall {
|
class OverloadedPointerDereferenceExpr extends FunctionCall {
|
||||||
OverloadedPointerDereferenceExpr() {
|
OverloadedPointerDereferenceExpr() {
|
||||||
|
|||||||
@@ -850,24 +850,6 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
|
|||||||
this.getAllocatorCall()
|
this.getAllocatorCall()
|
||||||
.getArgument(this.getAllocator().(OperatorNewAllocationFunction).getPlacementArgument())
|
.getArgument(this.getAllocator().(OperatorNewAllocationFunction).getPlacementArgument())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* For `operator new`, this gets the call or expression that initializes the allocated object, if any.
|
|
||||||
*
|
|
||||||
* As examples, for `new int(4)`, this will be `4`, and for `new std::vector(4)`, this will
|
|
||||||
* be a call to the constructor `std::vector::vector(size_t)` with `4` as an argument.
|
|
||||||
*
|
|
||||||
* For `operator new[]`, this gets the call or expression that initializes the first element of the
|
|
||||||
* array, if any.
|
|
||||||
*
|
|
||||||
* This will either be a call to the default constructor for the array's element type (as
|
|
||||||
* in `new std::string[10]`), or a literal zero for arrays of scalars which are zero-initialized
|
|
||||||
* due to extra parentheses (as in `new int[10]()`).
|
|
||||||
*
|
|
||||||
* At runtime, the constructor will be called once for each element in the array, but the
|
|
||||||
* constructor call only exists once in the AST.
|
|
||||||
*/
|
|
||||||
final Expr getInitializer() { result = this.getChild(1) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -889,6 +871,14 @@ class NewExpr extends NewOrNewArrayExpr, @new_expr {
|
|||||||
override Type getAllocatedType() {
|
override Type getAllocatedType() {
|
||||||
new_allocated_type(underlyingElement(this), unresolveElement(result))
|
new_allocated_type(underlyingElement(this), unresolveElement(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the call or expression that initializes the allocated object, if any.
|
||||||
|
*
|
||||||
|
* As examples, for `new int(4)`, this will be `4`, and for `new std::vector(4)`, this will
|
||||||
|
* be a call to the constructor `std::vector::vector(size_t)` with `4` as an argument.
|
||||||
|
*/
|
||||||
|
Expr getInitializer() { result = this.getChild(1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -919,6 +909,18 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
|
|||||||
result = getType().getUnderlyingType().(PointerType).getBaseType()
|
result = getType().getUnderlyingType().(PointerType).getBaseType()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the call or expression that initializes the first element of the array, if any.
|
||||||
|
*
|
||||||
|
* This will either be a call to the default constructor for the array's element type (as
|
||||||
|
* in `new std::string[10]`), or a literal zero for arrays of scalars which are zero-initialized
|
||||||
|
* due to extra parentheses (as in `new int[10]()`).
|
||||||
|
*
|
||||||
|
* At runtime, the constructor will be called once for each element in the array, but the
|
||||||
|
* constructor call only exists once in the AST.
|
||||||
|
*/
|
||||||
|
Expr getInitializer() { result = this.getChild(1) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the extent of the non-constant array dimension, if any.
|
* Gets the extent of the non-constant array dimension, if any.
|
||||||
*
|
*
|
||||||
@@ -1269,8 +1271,7 @@ private predicate convparents(Expr child, int idx, Element parent) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pulled out for performance. See
|
// Pulled out for performance. See QL-796.
|
||||||
// https://github.com/github/codeql-coreql-team/issues/1044.
|
|
||||||
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
|
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2133,8 +2133,11 @@ private module Stage4 {
|
|||||||
|
|
||||||
bindingset[node, cc, config]
|
bindingset[node, cc, config]
|
||||||
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
||||||
localFlowEntry(node, config) and
|
exists(Cc cc0 |
|
||||||
result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
|
cc = pragma[only_bind_into](cc0) and
|
||||||
|
localFlowEntry(node, config) and
|
||||||
|
result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate localStep(
|
private predicate localStep(
|
||||||
@@ -3129,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
|||||||
conf = mid.getConfiguration() and
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
sc = mid.getSummaryCtx() and
|
sc = mid.getSummaryCtx() and
|
||||||
localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
|
localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
|
||||||
ap0 = mid.getAp()
|
ap0 = mid.getAp()
|
||||||
|
|
|
|
||||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||||
|
|||||||
@@ -2133,8 +2133,11 @@ private module Stage4 {
|
|||||||
|
|
||||||
bindingset[node, cc, config]
|
bindingset[node, cc, config]
|
||||||
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
||||||
localFlowEntry(node, config) and
|
exists(Cc cc0 |
|
||||||
result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
|
cc = pragma[only_bind_into](cc0) and
|
||||||
|
localFlowEntry(node, config) and
|
||||||
|
result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate localStep(
|
private predicate localStep(
|
||||||
@@ -3129,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
|||||||
conf = mid.getConfiguration() and
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
sc = mid.getSummaryCtx() and
|
sc = mid.getSummaryCtx() and
|
||||||
localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
|
localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
|
||||||
ap0 = mid.getAp()
|
ap0 = mid.getAp()
|
||||||
|
|
|
|
||||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||||
|
|||||||
@@ -2133,8 +2133,11 @@ private module Stage4 {
|
|||||||
|
|
||||||
bindingset[node, cc, config]
|
bindingset[node, cc, config]
|
||||||
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
||||||
localFlowEntry(node, config) and
|
exists(Cc cc0 |
|
||||||
result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
|
cc = pragma[only_bind_into](cc0) and
|
||||||
|
localFlowEntry(node, config) and
|
||||||
|
result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate localStep(
|
private predicate localStep(
|
||||||
@@ -3129,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
|||||||
conf = mid.getConfiguration() and
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
sc = mid.getSummaryCtx() and
|
sc = mid.getSummaryCtx() and
|
||||||
localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
|
localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
|
||||||
ap0 = mid.getAp()
|
ap0 = mid.getAp()
|
||||||
|
|
|
|
||||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||||
|
|||||||
@@ -2133,8 +2133,11 @@ private module Stage4 {
|
|||||||
|
|
||||||
bindingset[node, cc, config]
|
bindingset[node, cc, config]
|
||||||
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
|
||||||
localFlowEntry(node, config) and
|
exists(Cc cc0 |
|
||||||
result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
|
cc = pragma[only_bind_into](cc0) and
|
||||||
|
localFlowEntry(node, config) and
|
||||||
|
result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate localStep(
|
private predicate localStep(
|
||||||
@@ -3129,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
|||||||
conf = mid.getConfiguration() and
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
sc = mid.getSummaryCtx() and
|
sc = mid.getSummaryCtx() and
|
||||||
localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
|
localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
|
||||||
ap0 = mid.getAp()
|
ap0 = mid.getAp()
|
||||||
|
|
|
|
||||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
|
|||||||
* currently excludes read-steps, store-steps, and flow-through.
|
* currently excludes read-steps, store-steps, and flow-through.
|
||||||
*
|
*
|
||||||
* The analysis uses non-linear recursion: When computing a flow path in or out
|
* The analysis uses non-linear recursion: When computing a flow path in or out
|
||||||
* of a call, we use the results of the analysis recursively to resolve lambda
|
* of a call, we use the results of the analysis recursively to resolve lamba
|
||||||
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
|
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
|
||||||
*/
|
*/
|
||||||
private module LambdaFlow {
|
private module LambdaFlow {
|
||||||
|
|||||||
@@ -557,5 +557,5 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
|
|||||||
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
||||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
||||||
|
|
||||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
/** Extra data-flow steps needed for lamba flow analysis. */
|
||||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||||
|
|||||||
@@ -362,22 +362,15 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to.
|
* 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. Even if the store does
|
* For instance, an update to a field of a struct containing only one field. For these cases we
|
||||||
* have a chi instruction, a subsequent use of the result of the store may be linked directly to the
|
* attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case
|
||||||
* result of the store as an inexact definition if the store totally overlaps the use. For these
|
* (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`.
|
||||||
* 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 {
|
private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
|
||||||
override StoreInstruction instr;
|
override StoreInstruction instr;
|
||||||
|
|
||||||
ExplicitSingleFieldStoreQualifierNode() {
|
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`.
|
// Without this condition any store would create a `PostUpdateNode`.
|
||||||
instr.getDestinationAddress() instanceof FieldAddressInstruction
|
instr.getDestinationAddress() instanceof FieldAddressInstruction
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,34 @@ private import semmle.code.cpp.ir.ValueNumbering
|
|||||||
private import semmle.code.cpp.ir.IR
|
private import semmle.code.cpp.ir.IR
|
||||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
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.DataFlowUtil
|
||||||
private import PrintIRUtilities
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the local dataflow from other nodes in the same function to this node.
|
* Gets the local dataflow from other nodes in the same function to this node.
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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, ", "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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
|
|
||||||
}
|
|
||||||
@@ -297,8 +297,7 @@ class Instruction extends Construction::TStageInstruction {
|
|||||||
/**
|
/**
|
||||||
* Gets the opcode that specifies the operation performed by this instruction.
|
* Gets the opcode that specifies the operation performed by this instruction.
|
||||||
*/
|
*/
|
||||||
pragma[inline]
|
final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) }
|
||||||
final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all direct uses of the result of this instruction. The result can be
|
* Gets all direct uses of the result of this instruction. The result can be
|
||||||
@@ -1646,19 +1645,6 @@ class CallInstruction extends Instruction {
|
|||||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||||
*/
|
*/
|
||||||
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,71 +4,6 @@ private import AliasAnalysisImports
|
|||||||
|
|
||||||
private class IntValue = Ints::IntValue;
|
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
|
* 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
|
* not result in any address held in that operand from escaping beyond the
|
||||||
@@ -99,7 +34,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
|
|||||||
|
|
||||||
private predicate operandEscapesDomain(Operand operand) {
|
private predicate operandEscapesDomain(Operand operand) {
|
||||||
not operandIsConsumedWithoutEscaping(operand) and
|
not operandIsConsumedWithoutEscaping(operand) and
|
||||||
not operandIsPropagated(operand, _, _) and
|
not operandIsPropagated(operand, _) and
|
||||||
not isArgumentForParameter(_, operand, _) and
|
not isArgumentForParameter(_, operand, _) and
|
||||||
not isOnlyEscapesViaReturnArgument(operand) and
|
not isOnlyEscapesViaReturnArgument(operand) and
|
||||||
not operand.getUse() instanceof ReturnValueInstruction and
|
not operand.getUse() instanceof ReturnValueInstruction and
|
||||||
@@ -134,67 +69,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by
|
* Holds if any address held in operand `tag` of instruction `instr` is
|
||||||
* the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to
|
* propagated to the result of `instr`, offset by the number of bits in
|
||||||
* be a constant, then `bitOffset` is `unknown()`.
|
* `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, Instruction instr) {
|
private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||||
// Some functions are known to propagate an argument
|
exists(Instruction instr |
|
||||||
hasAddressFlowThroughCall(operand, instr) and
|
instr = operand.getUse() and
|
||||||
bitOffset = 0
|
(
|
||||||
or
|
// Converting to a non-virtual base class adds the offset of the base class.
|
||||||
instr = operand.getUse() and
|
exists(ConvertToNonVirtualBaseInstruction convert |
|
||||||
(
|
convert = instr and
|
||||||
// Converting to a non-virtual base class adds the offset of the base class.
|
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
||||||
exists(ConvertToNonVirtualBaseInstruction convert |
|
)
|
||||||
convert = instr and
|
or
|
||||||
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
// 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
|
||||||
)
|
)
|
||||||
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) {
|
private predicate operandEscapesNonReturn(Operand operand) {
|
||||||
exists(Instruction instr |
|
// The address is propagated to the result of the instruction, and that result itself is returned
|
||||||
// The address is propagated to the result of the instruction, and that result itself is returned
|
operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse())
|
||||||
operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
// The operand is used in a function call which returns it, and the return value is then returned
|
// The operand is used in a function call which returns it, and the return value is then returned
|
||||||
exists(CallInstruction ci, Instruction init |
|
exists(CallInstruction ci, Instruction init |
|
||||||
@@ -216,11 +151,9 @@ private predicate operandEscapesNonReturn(Operand operand) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private predicate operandMayReachReturn(Operand operand) {
|
private predicate operandMayReachReturn(Operand operand) {
|
||||||
exists(Instruction instr |
|
// The address is propagated to the result of the instruction, and that result itself is returned
|
||||||
// The address is propagated to the result of the instruction, and that result itself is returned
|
operandIsPropagated(operand, _) and
|
||||||
operandIsPropagated(operand, _, instr) and
|
resultMayReachReturn(operand.getUse())
|
||||||
resultMayReachReturn(instr)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
// The operand is used in a function call which returns it, and the return value is then returned
|
// The operand is used in a function call which returns it, and the return value is then returned
|
||||||
exists(CallInstruction ci, Instruction init |
|
exists(CallInstruction ci, Instruction init |
|
||||||
@@ -240,9 +173,9 @@ private predicate operandMayReachReturn(Operand operand) {
|
|||||||
|
|
||||||
private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||||
// The address is propagated to the result of the instruction, and that result itself is returned
|
// The address is propagated to the result of the instruction, and that result itself is returned
|
||||||
exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 |
|
exists(IntValue bitOffset1, IntValue bitOffset2 |
|
||||||
operandIsPropagated(operand, bitOffset1, instr) and
|
operandIsPropagated(operand, bitOffset1) and
|
||||||
resultReturned(instr, bitOffset2) and
|
resultReturned(operand.getUse(), bitOffset2) and
|
||||||
bitOffset = Ints::add(bitOffset1, bitOffset2)
|
bitOffset = Ints::add(bitOffset1, bitOffset2)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -281,6 +214,13 @@ 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) {
|
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
|
||||||
exists(AliasModels::AliasFunction f |
|
exists(AliasModels::AliasFunction f |
|
||||||
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
||||||
@@ -330,15 +270,12 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
|
|||||||
/**
|
/**
|
||||||
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
|
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
|
||||||
*/
|
*/
|
||||||
private predicate operandIsPropagatedIncludingByCall(
|
private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) {
|
||||||
Operand operand, IntValue bitOffset, Instruction instr
|
operandIsPropagated(operand, bitOffset)
|
||||||
) {
|
|
||||||
operandIsPropagated(operand, bitOffset, instr)
|
|
||||||
or
|
or
|
||||||
exists(CallInstruction call, Instruction init |
|
exists(CallInstruction call, Instruction init |
|
||||||
isArgumentForParameter(call, operand, init) and
|
isArgumentForParameter(call, operand, init) and
|
||||||
resultReturned(init, bitOffset) and
|
resultReturned(init, bitOffset)
|
||||||
instr = call
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +292,8 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base,
|
|||||||
// We already have an offset from `middle`.
|
// We already have an offset from `middle`.
|
||||||
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
|
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
|
||||||
// `middle` is propagated from `base`.
|
// `middle` is propagated from `base`.
|
||||||
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and
|
middleOperand = middle.getAnOperand() and
|
||||||
|
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and
|
||||||
base = middleOperand.getDef() and
|
base = middleOperand.getDef() and
|
||||||
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
|
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -105,21 +105,7 @@ class DynamicAllocation extends Allocation, TDynamicAllocation {
|
|||||||
DynamicAllocation() { this = TDynamicAllocation(call) }
|
DynamicAllocation() { this = TDynamicAllocation(call) }
|
||||||
|
|
||||||
final override string toString() {
|
final override string toString() {
|
||||||
// This isn't performant, but it's only used in test/dump code right now.
|
result = call.toString() + " at " + call.getLocation() // 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 }
|
final override CallInstruction getABaseInstruction() { result = call }
|
||||||
|
|||||||
@@ -338,21 +338,15 @@ private module Cached {
|
|||||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
|
|
||||||
*
|
|
||||||
* The parameters are ordered such that they produce a clean join (with no need for reordering)
|
|
||||||
* in the characteristic predicates of the `Instruction` subclasses.
|
|
||||||
*/
|
|
||||||
cached
|
cached
|
||||||
predicate getInstructionOpcode(Opcode opcode, Instruction instr) {
|
Opcode getInstructionOpcode(Instruction instr) {
|
||||||
opcode = getOldInstruction(instr).getOpcode()
|
result = getOldInstruction(instr).getOpcode()
|
||||||
or
|
or
|
||||||
instr = phiInstruction(_, _) and opcode instanceof Opcode::Phi
|
instr = phiInstruction(_, _) and result instanceof Opcode::Phi
|
||||||
or
|
or
|
||||||
instr = chiInstruction(_) and opcode instanceof Opcode::Chi
|
instr = chiInstruction(_) and result instanceof Opcode::Chi
|
||||||
or
|
or
|
||||||
instr = unreachedInstruction(_) and opcode instanceof Opcode::Unreached
|
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||||
}
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
|
|||||||
@@ -297,8 +297,7 @@ class Instruction extends Construction::TStageInstruction {
|
|||||||
/**
|
/**
|
||||||
* Gets the opcode that specifies the operation performed by this instruction.
|
* Gets the opcode that specifies the operation performed by this instruction.
|
||||||
*/
|
*/
|
||||||
pragma[inline]
|
final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) }
|
||||||
final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all direct uses of the result of this instruction. The result can be
|
* Gets all direct uses of the result of this instruction. The result can be
|
||||||
@@ -1646,19 +1645,6 @@ class CallInstruction extends Instruction {
|
|||||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||||
*/
|
*/
|
||||||
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -360,8 +360,8 @@ CppType getInstructionResultType(TStageInstruction instr) {
|
|||||||
getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result)
|
getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate getInstructionOpcode(Opcode opcode, TStageInstruction instr) {
|
Opcode getInstructionOpcode(TStageInstruction instr) {
|
||||||
getInstructionTranslatedElement(instr).hasInstruction(opcode, getInstructionTag(instr), _)
|
getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _)
|
||||||
}
|
}
|
||||||
|
|
||||||
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
|
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
|||||||
private import semmle.code.cpp.ir.internal.CppType
|
private import semmle.code.cpp.ir.internal.CppType
|
||||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||||
private import InstructionTag
|
private import InstructionTag
|
||||||
private import SideEffects
|
|
||||||
private import TranslatedElement
|
private import TranslatedElement
|
||||||
private import TranslatedExpr
|
private import TranslatedExpr
|
||||||
private import TranslatedFunction
|
private import TranslatedFunction
|
||||||
@@ -425,15 +424,12 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TranslatedStructorCallSideEffects extends TranslatedCallSideEffects {
|
class TranslatedStructorCallSideEffects extends TranslatedCallSideEffects {
|
||||||
TranslatedStructorCallSideEffects() {
|
TranslatedStructorCallSideEffects() { getParent().(TranslatedStructorCall).hasQualifier() }
|
||||||
getParent().(TranslatedStructorCall).hasQualifier() and
|
|
||||||
getASideEffectOpcode(expr, -1) instanceof WriteSideEffectOpcode
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) {
|
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) {
|
||||||
|
opcode instanceof Opcode::IndirectMayWriteSideEffect and
|
||||||
tag instanceof OnlyInstructionTag and
|
tag instanceof OnlyInstructionTag and
|
||||||
t = getTypeForPRValue(expr.getTarget().getDeclaringType()) and
|
t = getTypeForPRValue(expr.getTarget().getDeclaringType())
|
||||||
opcode = getASideEffectOpcode(expr, -1).(WriteSideEffectOpcode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||||
@@ -464,11 +460,9 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
|||||||
Call call;
|
Call call;
|
||||||
Expr arg;
|
Expr arg;
|
||||||
int index;
|
int index;
|
||||||
SideEffectOpcode sideEffectOpcode;
|
boolean write;
|
||||||
|
|
||||||
TranslatedSideEffect() {
|
TranslatedSideEffect() { this = TTranslatedArgumentSideEffect(call, arg, index, write) }
|
||||||
this = TTranslatedArgumentSideEffect(call, arg, index, sideEffectOpcode)
|
|
||||||
}
|
|
||||||
|
|
||||||
override Locatable getAST() { result = arg }
|
override Locatable getAST() { result = arg }
|
||||||
|
|
||||||
@@ -478,13 +472,13 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
|||||||
|
|
||||||
int getArgumentIndex() { result = index }
|
int getArgumentIndex() { result = index }
|
||||||
|
|
||||||
predicate isWrite() { sideEffectOpcode instanceof WriteSideEffectOpcode }
|
predicate isWrite() { write = true }
|
||||||
|
|
||||||
override string toString() {
|
override string toString() {
|
||||||
isWrite() and
|
write = true and
|
||||||
result = "(write side effect for " + arg.toString() + ")"
|
result = "(write side effect for " + arg.toString() + ")"
|
||||||
or
|
or
|
||||||
not isWrite() and
|
write = false and
|
||||||
result = "(read side effect for " + arg.toString() + ")"
|
result = "(read side effect for " + arg.toString() + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,29 +489,29 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
|||||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||||
|
|
||||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||||
|
isWrite() and
|
||||||
|
hasSpecificWriteSideEffect(opcode) and
|
||||||
tag = OnlyInstructionTag() and
|
tag = OnlyInstructionTag() and
|
||||||
opcode = sideEffectOpcode and
|
|
||||||
(
|
(
|
||||||
isWrite() and
|
opcode instanceof BufferAccessOpcode and
|
||||||
(
|
type = getUnknownType()
|
||||||
opcode instanceof BufferAccessOpcode and
|
or
|
||||||
type = getUnknownType()
|
not opcode instanceof BufferAccessOpcode and
|
||||||
or
|
exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() |
|
||||||
not opcode instanceof BufferAccessOpcode and
|
if baseType instanceof VoidType
|
||||||
exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() |
|
then type = getUnknownType()
|
||||||
if baseType instanceof VoidType
|
else type = getTypeForPRValueOrUnknown(baseType)
|
||||||
then type = getUnknownType()
|
|
||||||
else type = getTypeForPRValueOrUnknown(baseType)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
index = -1 and
|
|
||||||
not arg.getUnspecifiedType() instanceof DerivedType and
|
|
||||||
type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType())
|
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
not isWrite() and
|
index = -1 and
|
||||||
type = getVoidType()
|
not arg.getUnspecifiedType() instanceof DerivedType and
|
||||||
|
type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType())
|
||||||
)
|
)
|
||||||
|
or
|
||||||
|
not isWrite() and
|
||||||
|
hasSpecificReadSideEffect(opcode) and
|
||||||
|
tag = OnlyInstructionTag() and
|
||||||
|
type = getVoidType()
|
||||||
}
|
}
|
||||||
|
|
||||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||||
@@ -541,7 +535,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
|||||||
|
|
||||||
override CppType getInstructionMemoryOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
override CppType getInstructionMemoryOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||||
not isWrite() and
|
not isWrite() and
|
||||||
if sideEffectOpcode instanceof BufferAccessOpcode
|
if hasSpecificReadSideEffect(any(BufferAccessOpcode op))
|
||||||
then
|
then
|
||||||
result = getUnknownType() and
|
result = getUnknownType() and
|
||||||
tag instanceof OnlyInstructionTag and
|
tag instanceof OnlyInstructionTag and
|
||||||
@@ -563,6 +557,56 @@ 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) {
|
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||||
tag = OnlyInstructionTag() and
|
tag = OnlyInstructionTag() and
|
||||||
result = getTranslatedCallInstruction(call)
|
result = getTranslatedCallInstruction(call)
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ private import TranslatedStmt
|
|||||||
private import TranslatedExpr
|
private import TranslatedExpr
|
||||||
private import IRConstruction
|
private import IRConstruction
|
||||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||||
private import SideEffects
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the "real" parent of `expr`. This predicate treats conversions as if
|
* Gets the "real" parent of `expr`. This predicate treats conversions as if
|
||||||
@@ -42,8 +41,7 @@ IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) {
|
|||||||
*/
|
*/
|
||||||
predicate isIRConstant(Expr expr) { exists(expr.getValue()) }
|
predicate isIRConstant(Expr expr) { exists(expr.getValue()) }
|
||||||
|
|
||||||
// Pulled out for performance. See
|
// Pulled out to work around QL-796
|
||||||
// https://github.com/github/codeql-coreql-team/issues/1044.
|
|
||||||
private predicate isOrphan(Expr expr) { not exists(getRealParent(expr)) }
|
private predicate isOrphan(Expr expr) { not exists(getRealParent(expr)) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -637,15 +635,46 @@ newtype TTranslatedElement =
|
|||||||
// The side effects of an allocation, i.e. `new`, `new[]` or `malloc`
|
// The side effects of an allocation, i.e. `new`, `new[]` or `malloc`
|
||||||
TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or
|
TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or
|
||||||
// A precise side effect of an argument to a `Call`
|
// A precise side effect of an argument to a `Call`
|
||||||
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) {
|
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
|
||||||
not ignoreExpr(expr) and
|
|
||||||
not ignoreExpr(call) and
|
|
||||||
(
|
(
|
||||||
n >= 0 and expr = call.getArgument(n).getFullyConverted()
|
expr = call.getArgument(n).getFullyConverted()
|
||||||
or
|
or
|
||||||
n = -1 and expr = call.getQualifier().getFullyConverted()
|
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
|
) and
|
||||||
opcode = getASideEffectOpcode(call, n)
|
(
|
||||||
|
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(n, _) and
|
||||||
|
isWrite = false
|
||||||
|
or
|
||||||
|
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(n, _, _) and
|
||||||
|
isWrite = true
|
||||||
|
or
|
||||||
|
not call.getTarget() instanceof SideEffectFunction and
|
||||||
|
exists(Type t | t = expr.getUnspecifiedType() |
|
||||||
|
t instanceof ArrayType or
|
||||||
|
t instanceof PointerType or
|
||||||
|
t instanceof ReferenceType
|
||||||
|
) and
|
||||||
|
(
|
||||||
|
isWrite = true and
|
||||||
|
not call.getTarget().getParameter(n).getType().isDeeplyConstBelow()
|
||||||
|
or
|
||||||
|
isWrite = false
|
||||||
|
)
|
||||||
|
or
|
||||||
|
not call.getTarget() instanceof SideEffectFunction and
|
||||||
|
n = -1 and
|
||||||
|
(
|
||||||
|
isWrite = true and
|
||||||
|
not call.getTarget() instanceof ConstMemberFunction
|
||||||
|
or
|
||||||
|
isWrite = false
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
not ignoreExpr(expr) and
|
||||||
|
not ignoreExpr(call)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -297,8 +297,7 @@ class Instruction extends Construction::TStageInstruction {
|
|||||||
/**
|
/**
|
||||||
* Gets the opcode that specifies the operation performed by this instruction.
|
* Gets the opcode that specifies the operation performed by this instruction.
|
||||||
*/
|
*/
|
||||||
pragma[inline]
|
final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) }
|
||||||
final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all direct uses of the result of this instruction. The result can be
|
* Gets all direct uses of the result of this instruction. The result can be
|
||||||
@@ -1646,19 +1645,6 @@ class CallInstruction extends Instruction {
|
|||||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||||
*/
|
*/
|
||||||
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,71 +4,6 @@ private import AliasAnalysisImports
|
|||||||
|
|
||||||
private class IntValue = Ints::IntValue;
|
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
|
* 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
|
* not result in any address held in that operand from escaping beyond the
|
||||||
@@ -99,7 +34,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
|
|||||||
|
|
||||||
private predicate operandEscapesDomain(Operand operand) {
|
private predicate operandEscapesDomain(Operand operand) {
|
||||||
not operandIsConsumedWithoutEscaping(operand) and
|
not operandIsConsumedWithoutEscaping(operand) and
|
||||||
not operandIsPropagated(operand, _, _) and
|
not operandIsPropagated(operand, _) and
|
||||||
not isArgumentForParameter(_, operand, _) and
|
not isArgumentForParameter(_, operand, _) and
|
||||||
not isOnlyEscapesViaReturnArgument(operand) and
|
not isOnlyEscapesViaReturnArgument(operand) and
|
||||||
not operand.getUse() instanceof ReturnValueInstruction and
|
not operand.getUse() instanceof ReturnValueInstruction and
|
||||||
@@ -134,67 +69,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by
|
* Holds if any address held in operand `tag` of instruction `instr` is
|
||||||
* the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to
|
* propagated to the result of `instr`, offset by the number of bits in
|
||||||
* be a constant, then `bitOffset` is `unknown()`.
|
* `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, Instruction instr) {
|
private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||||
// Some functions are known to propagate an argument
|
exists(Instruction instr |
|
||||||
hasAddressFlowThroughCall(operand, instr) and
|
instr = operand.getUse() and
|
||||||
bitOffset = 0
|
(
|
||||||
or
|
// Converting to a non-virtual base class adds the offset of the base class.
|
||||||
instr = operand.getUse() and
|
exists(ConvertToNonVirtualBaseInstruction convert |
|
||||||
(
|
convert = instr and
|
||||||
// Converting to a non-virtual base class adds the offset of the base class.
|
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
||||||
exists(ConvertToNonVirtualBaseInstruction convert |
|
)
|
||||||
convert = instr and
|
or
|
||||||
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
// 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
|
||||||
)
|
)
|
||||||
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) {
|
private predicate operandEscapesNonReturn(Operand operand) {
|
||||||
exists(Instruction instr |
|
// The address is propagated to the result of the instruction, and that result itself is returned
|
||||||
// The address is propagated to the result of the instruction, and that result itself is returned
|
operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse())
|
||||||
operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
// The operand is used in a function call which returns it, and the return value is then returned
|
// The operand is used in a function call which returns it, and the return value is then returned
|
||||||
exists(CallInstruction ci, Instruction init |
|
exists(CallInstruction ci, Instruction init |
|
||||||
@@ -216,11 +151,9 @@ private predicate operandEscapesNonReturn(Operand operand) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private predicate operandMayReachReturn(Operand operand) {
|
private predicate operandMayReachReturn(Operand operand) {
|
||||||
exists(Instruction instr |
|
// The address is propagated to the result of the instruction, and that result itself is returned
|
||||||
// The address is propagated to the result of the instruction, and that result itself is returned
|
operandIsPropagated(operand, _) and
|
||||||
operandIsPropagated(operand, _, instr) and
|
resultMayReachReturn(operand.getUse())
|
||||||
resultMayReachReturn(instr)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
// The operand is used in a function call which returns it, and the return value is then returned
|
// The operand is used in a function call which returns it, and the return value is then returned
|
||||||
exists(CallInstruction ci, Instruction init |
|
exists(CallInstruction ci, Instruction init |
|
||||||
@@ -240,9 +173,9 @@ private predicate operandMayReachReturn(Operand operand) {
|
|||||||
|
|
||||||
private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||||
// The address is propagated to the result of the instruction, and that result itself is returned
|
// The address is propagated to the result of the instruction, and that result itself is returned
|
||||||
exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 |
|
exists(IntValue bitOffset1, IntValue bitOffset2 |
|
||||||
operandIsPropagated(operand, bitOffset1, instr) and
|
operandIsPropagated(operand, bitOffset1) and
|
||||||
resultReturned(instr, bitOffset2) and
|
resultReturned(operand.getUse(), bitOffset2) and
|
||||||
bitOffset = Ints::add(bitOffset1, bitOffset2)
|
bitOffset = Ints::add(bitOffset1, bitOffset2)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -281,6 +214,13 @@ 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) {
|
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
|
||||||
exists(AliasModels::AliasFunction f |
|
exists(AliasModels::AliasFunction f |
|
||||||
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
||||||
@@ -330,15 +270,12 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
|
|||||||
/**
|
/**
|
||||||
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
|
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
|
||||||
*/
|
*/
|
||||||
private predicate operandIsPropagatedIncludingByCall(
|
private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) {
|
||||||
Operand operand, IntValue bitOffset, Instruction instr
|
operandIsPropagated(operand, bitOffset)
|
||||||
) {
|
|
||||||
operandIsPropagated(operand, bitOffset, instr)
|
|
||||||
or
|
or
|
||||||
exists(CallInstruction call, Instruction init |
|
exists(CallInstruction call, Instruction init |
|
||||||
isArgumentForParameter(call, operand, init) and
|
isArgumentForParameter(call, operand, init) and
|
||||||
resultReturned(init, bitOffset) and
|
resultReturned(init, bitOffset)
|
||||||
instr = call
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +292,8 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base,
|
|||||||
// We already have an offset from `middle`.
|
// We already have an offset from `middle`.
|
||||||
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
|
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
|
||||||
// `middle` is propagated from `base`.
|
// `middle` is propagated from `base`.
|
||||||
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and
|
middleOperand = middle.getAnOperand() and
|
||||||
|
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and
|
||||||
base = middleOperand.getDef() and
|
base = middleOperand.getDef() and
|
||||||
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
|
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -338,21 +338,15 @@ private module Cached {
|
|||||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
|
|
||||||
*
|
|
||||||
* The parameters are ordered such that they produce a clean join (with no need for reordering)
|
|
||||||
* in the characteristic predicates of the `Instruction` subclasses.
|
|
||||||
*/
|
|
||||||
cached
|
cached
|
||||||
predicate getInstructionOpcode(Opcode opcode, Instruction instr) {
|
Opcode getInstructionOpcode(Instruction instr) {
|
||||||
opcode = getOldInstruction(instr).getOpcode()
|
result = getOldInstruction(instr).getOpcode()
|
||||||
or
|
or
|
||||||
instr = phiInstruction(_, _) and opcode instanceof Opcode::Phi
|
instr = phiInstruction(_, _) and result instanceof Opcode::Phi
|
||||||
or
|
or
|
||||||
instr = chiInstruction(_) and opcode instanceof Opcode::Chi
|
instr = chiInstruction(_) and result instanceof Opcode::Chi
|
||||||
or
|
or
|
||||||
instr = unreachedInstruction(_) and opcode instanceof Opcode::Unreached
|
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||||
}
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
|
|||||||
@@ -1,63 +1,34 @@
|
|||||||
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.Taint
|
||||||
import semmle.code.cpp.models.interfaces.DataFlow
|
import semmle.code.cpp.models.interfaces.DataFlow
|
||||||
import semmle.code.cpp.models.interfaces.PointerWrapper
|
import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `std::shared_ptr`, `std::weak_ptr`, and `std::unique_ptr` template classes.
|
* The `std::shared_ptr` and `std::unique_ptr` template classes.
|
||||||
*/
|
*/
|
||||||
private class SmartPtr extends Class, PointerWrapper {
|
private class UniqueOrSharedPtr extends Class, PointerWrapper {
|
||||||
SmartPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "weak_ptr", "unique_ptr"]) }
|
UniqueOrSharedPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "unique_ptr"]) }
|
||||||
|
|
||||||
override MemberFunction getAnUnwrapperFunction() {
|
override MemberFunction getAnUnwrapperFunction() {
|
||||||
result.(OverloadedPointerDereferenceFunction).getDeclaringType() = this
|
result.(OverloadedPointerDereferenceFunction).getDeclaringType() = this
|
||||||
or
|
or
|
||||||
result.getClassAndName(["operator->", "get"]) = this
|
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. */
|
||||||
* Any function that returns the address wrapped by a `PointerWrapper`, whether as a pointer or a
|
private class PointerWrapperDataFlow extends DataFlowFunction {
|
||||||
* reference.
|
PointerWrapperDataFlow() {
|
||||||
*
|
this = any(PointerWrapper wrapper).getAnUnwrapperFunction() and
|
||||||
* Examples:
|
not this.getUnspecifiedType() instanceof ReferenceType
|
||||||
* - `std::unique_ptr<T>::get()`
|
|
||||||
* - `std::shared_ptr<T>::operator->()`
|
|
||||||
* - `std::weak_ptr<T>::operator*()`
|
|
||||||
*/
|
|
||||||
private class PointerUnwrapperFunction extends MemberFunction, TaintFunction, DataFlowFunction,
|
|
||||||
SideEffectFunction, AliasFunction {
|
|
||||||
PointerUnwrapperFunction() {
|
|
||||||
exists(PointerWrapper wrapper | wrapper.getAnUnwrapperFunction() = this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
|
||||||
input.isReturnValueDeref() and
|
|
||||||
output.isQualifierObject()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isQualifierObject() and output.isReturnValue()
|
input.isQualifierAddress() and output.isReturnValue()
|
||||||
}
|
or
|
||||||
|
input.isQualifierObject() and output.isReturnValueDeref()
|
||||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
or
|
||||||
|
input.isReturnValueDeref() and
|
||||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
output.isQualifierObject()
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,80 +60,31 @@ private class MakeUniqueOrShared extends TaintFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that sets the value of a smart pointer.
|
* A prefix `operator*` member function for a `shared_ptr` or `unique_ptr` type.
|
||||||
*
|
|
||||||
* This could be a constructor, an assignment operator, or a named member function like `reset()`.
|
|
||||||
*/
|
*/
|
||||||
private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, SideEffectFunction {
|
private class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunction {
|
||||||
SmartPtrSetterFunction() {
|
UniqueOrSharedDereferenceMemberOperator() {
|
||||||
this.getDeclaringType() instanceof SmartPtr and
|
this.hasName("operator*") and
|
||||||
not this.isStatic() and
|
this.getDeclaringType() instanceof UniqueOrSharedPtr
|
||||||
(
|
|
||||||
this instanceof Constructor
|
|
||||||
or
|
|
||||||
this.hasName("operator=")
|
|
||||||
or
|
|
||||||
this.hasName("reset")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasOnlySpecificReadSideEffects() { none() }
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
|
input.isQualifierObject() and
|
||||||
override predicate hasOnlySpecificWriteSideEffects() { none() }
|
output.isReturnValueDeref()
|
||||||
|
}
|
||||||
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
|
* The `std::shared_ptr` or `std::unique_ptr` function `get`.
|
||||||
// When taking ownership of a smart pointer via an rvalue reference, always overwrite the input
|
*/
|
||||||
// smart pointer.
|
private class UniqueOrSharedGet extends TaintFunction {
|
||||||
getPointerInput().isParameterDeref(i) and
|
UniqueOrSharedGet() {
|
||||||
this.getParameter(i).getUnspecifiedType() instanceof RValueReferenceType and
|
this.hasName("get") and
|
||||||
buffer = false and
|
this.getDeclaringType() instanceof UniqueOrSharedPtr
|
||||||
mustWrite = true
|
}
|
||||||
}
|
|
||||||
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
input.isQualifierObject() and
|
||||||
getPointerInput().isParameterDeref(i) and
|
output.isReturnValue()
|
||||||
buffer = false
|
|
||||||
or
|
|
||||||
not this instanceof Constructor and
|
|
||||||
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 = 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<T>` 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)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,16 +50,5 @@ abstract class AliasFunction extends Function {
|
|||||||
/**
|
/**
|
||||||
* Holds if the function always returns the value of the parameter at the specified index.
|
* Holds if the function always returns the value of the parameter at the specified index.
|
||||||
*/
|
*/
|
||||||
predicate parameterIsAlwaysReturned(int index) { none() }
|
abstract predicate parameterIsAlwaysReturned(int index);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,4 @@ abstract class PointerWrapper extends Class {
|
|||||||
* that return a reference to the pointed-to object.
|
* that return a reference to the pointed-to object.
|
||||||
*/
|
*/
|
||||||
abstract MemberFunction getAnUnwrapperFunction();
|
abstract MemberFunction getAnUnwrapperFunction();
|
||||||
|
|
||||||
/** Holds if the type of the data that is pointed to by this pointer wrapper is `const`. */
|
|
||||||
abstract predicate pointsToConst();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1617,20 +1617,6 @@ private module SimpleRangeAnalysisCached {
|
|||||||
defMightOverflowPositively(def, v)
|
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
|
* Holds if the expression might overflow negatively. This predicate
|
||||||
* does not consider the possibility that the expression might overflow
|
* does not consider the possibility that the expression might overflow
|
||||||
@@ -1644,11 +1630,6 @@ private module SimpleRangeAnalysisCached {
|
|||||||
// bound of `x`, so the standard logic (above) does not work for
|
// bound of `x`, so the standard logic (above) does not work for
|
||||||
// detecting whether it might overflow.
|
// detecting whether it might overflow.
|
||||||
getLowerBoundsImpl(expr.(PostfixDecrExpr)) = exprMinVal(expr)
|
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1676,11 +1657,6 @@ private module SimpleRangeAnalysisCached {
|
|||||||
// bound of `x`, so the standard logic (above) does not work for
|
// bound of `x`, so the standard logic (above) does not work for
|
||||||
// detecting whether it might overflow.
|
// detecting whether it might overflow.
|
||||||
getUpperBoundsImpl(expr.(PostfixIncrExpr)) = exprMaxVal(expr)
|
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.controlflow.Dominance
|
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`.
|
* Holds if the value of `use` is guarded using `abs`.
|
||||||
@@ -96,14 +94,9 @@ predicate guardedGreater(Operation e, Expr use) {
|
|||||||
VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() }
|
VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `e` potentially overflows and `use` is an operand of `e` that is not guarded.
|
* Holds if `e` is not guarded against overflow by `use`.
|
||||||
*/
|
*/
|
||||||
predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
|
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
|
use = e.getAnOperand() and
|
||||||
exists(LocalScopeVariable v | use.getTarget() = v |
|
exists(LocalScopeVariable v | use.getTarget() = v |
|
||||||
// overflow possible if large
|
// overflow possible if large
|
||||||
@@ -122,14 +115,9 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `e` potentially underflows and `use` is an operand of `e` that is not guarded.
|
* Holds if `e` is not guarded against underflow by `use`.
|
||||||
*/
|
*/
|
||||||
predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) {
|
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
|
use = e.getAnOperand() and
|
||||||
exists(LocalScopeVariable v | use.getTarget() = v |
|
exists(LocalScopeVariable v | use.getTarget() = v |
|
||||||
// underflow possible if use is left operand and small
|
// underflow possible if use is left operand and small
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
| test.cpp:21:9:21:15 | new | This allocation cannot return null. $@ is unnecessary. | test.cpp:21:9:21:15 | new | This check |
|
|
||||||
| test.cpp:29:13:29:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:30:7:30:13 | ... == ... | This check |
|
|
||||||
| test.cpp:33:13:33:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:34:8:34:9 | p2 | This check |
|
|
||||||
| test.cpp:37:13:37:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:38:7:38:16 | ... == ... | This check |
|
|
||||||
| test.cpp:41:13:41:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:42:7:42:19 | ... == ... | This check |
|
|
||||||
| test.cpp:45:13:45:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:46:7:46:8 | p5 | This check |
|
|
||||||
| test.cpp:49:8:49:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:50:7:50:13 | ... == ... | This check |
|
|
||||||
| test.cpp:53:8:53:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:54:8:54:9 | p7 | This check |
|
|
||||||
| test.cpp:58:8:58:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:59:7:59:16 | ... == ... | This check |
|
|
||||||
| test.cpp:63:8:63:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:64:7:64:19 | ... != ... | This check |
|
|
||||||
| test.cpp:69:9:69:20 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:70:7:70:14 | ... != ... | This check |
|
|
||||||
| test.cpp:75:11:75:22 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:76:13:76:15 | p11 | This check |
|
|
||||||
| test.cpp:92:5:92:31 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block |
|
|
||||||
| test.cpp:93:15:93:41 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block |
|
|
||||||
| test.cpp:96:10:96:36 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block |
|
|
||||||
| test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block |
|
|
||||||
| test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block |
|
|
||||||
| test.cpp:212:14:212:34 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:213:34:213:36 | { ... } | This catch block |
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
| test.cpp:30:15:30:26 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||||
|
| test.cpp:38:9:38:20 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||||
|
| test.cpp:50:13:50:38 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||||
|
| test.cpp:51:22:51:47 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||||
|
| test.cpp:53:18:53:43 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql
|
||||||
@@ -1,227 +1,97 @@
|
|||||||
#define NULL ((void *)0)
|
#define NULL ((void*)0)
|
||||||
|
|
||||||
namespace std {
|
|
||||||
struct nothrow_t {};
|
|
||||||
typedef unsigned long size_t;
|
|
||||||
|
|
||||||
class exception {};
|
class exception {};
|
||||||
class bad_alloc : public exception {};
|
|
||||||
|
|
||||||
extern const std::nothrow_t nothrow;
|
namespace std{
|
||||||
} // namespace std
|
struct nothrow_t {};
|
||||||
|
typedef unsigned long size_t;
|
||||||
|
class bad_alloc{
|
||||||
|
const char* what() const throw();
|
||||||
|
};
|
||||||
|
extern const std::nothrow_t nothrow;
|
||||||
|
}
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void *operator new(std::size_t);
|
void* operator new(std::size_t _Size);
|
||||||
void *operator new[](std::size_t);
|
void* operator new[](std::size_t _Size);
|
||||||
void *operator new(std::size_t, const std::nothrow_t &) noexcept;
|
void* operator new( std::size_t count, const std::nothrow_t& tag ) noexcept;
|
||||||
void *operator new[](std::size_t, const std::nothrow_t &) noexcept;
|
void* operator new[]( std::size_t count, const std::nothrow_t& tag ) noexcept;
|
||||||
|
|
||||||
void bad_new_in_condition() {
|
void badNew_0_0()
|
||||||
if (!(new int)) { // BAD
|
{
|
||||||
return;
|
while (true) {
|
||||||
}
|
new int[100]; // BAD [NOT DETECTED]
|
||||||
|
if(!(new int[100])) // BAD [NOT DETECTED]
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void badNew_0_1()
|
||||||
|
{
|
||||||
|
int * i = new int[100]; // BAD
|
||||||
|
if(i == 0)
|
||||||
|
return;
|
||||||
|
if(!i)
|
||||||
|
return;
|
||||||
|
if(i == NULL)
|
||||||
|
return;
|
||||||
|
int * j;
|
||||||
|
j = new int[100]; // BAD
|
||||||
|
if(j == 0)
|
||||||
|
return;
|
||||||
|
if(!j)
|
||||||
|
return;
|
||||||
|
if(j == NULL)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void badNew_1_0()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
new(std::nothrow) int[100]; // BAD
|
||||||
|
int* p = new(std::nothrow) int[100]; // BAD
|
||||||
|
int* p1;
|
||||||
|
p1 = new(std::nothrow) int[100]; // BAD
|
||||||
|
}
|
||||||
|
} catch (const exception &){//const std::bad_alloc& e) {
|
||||||
|
// std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void badNew_1_1()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
int* p = new(std::nothrow) int[100]; // BAD [NOT DETECTED]
|
||||||
|
new(std::nothrow) int[100]; // BAD [NOT DETECTED]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void foo(int**);
|
void goodNew_0_0()
|
||||||
|
{
|
||||||
void bad_new_missing_exception_handling() {
|
try {
|
||||||
int *p1 = new int[100]; // BAD
|
while (true) {
|
||||||
if (p1 == 0)
|
new int[100]; // GOOD
|
||||||
return;
|
}
|
||||||
|
} catch (const exception &){//const std::bad_alloc& e) {
|
||||||
int *p2 = new int[100]; // BAD
|
// std::cout << e.what() << '\n';
|
||||||
if (!p2)
|
}
|
||||||
return;
|
|
||||||
|
|
||||||
int *p3 = new int[100]; // BAD
|
|
||||||
if (p3 == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int *p4 = new int[100]; // BAD
|
|
||||||
if (p4 == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int *p5 = new int[100]; // BAD
|
|
||||||
if (p5) {} else return;
|
|
||||||
|
|
||||||
int *p6;
|
|
||||||
p6 = new int[100]; // BAD
|
|
||||||
if (p6 == 0) return;
|
|
||||||
|
|
||||||
int *p7;
|
|
||||||
p7 = new int[100]; // BAD
|
|
||||||
if (!p7)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int *p8;
|
|
||||||
p8 = new int[100]; // BAD
|
|
||||||
if (p8 == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int *p9;
|
|
||||||
p9 = new int[100]; // BAD
|
|
||||||
if (p9 != nullptr) {
|
|
||||||
} else
|
|
||||||
return;
|
|
||||||
|
|
||||||
int *p10;
|
|
||||||
p10 = new int[100]; // BAD
|
|
||||||
if (p10 != 0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
int *p11;
|
|
||||||
do {
|
|
||||||
p11 = new int[100]; // BAD
|
|
||||||
} while (!p11);
|
|
||||||
|
|
||||||
int* p12 = new int[100];
|
|
||||||
foo(&p12);
|
|
||||||
if(p12) {} else return; // GOOD: p12 is probably modified in foo, so it's
|
|
||||||
// not the return value of the new that's checked.
|
|
||||||
|
|
||||||
int* p13 = new int[100];
|
|
||||||
foo(&p13);
|
|
||||||
if(!p13) {
|
|
||||||
return;
|
|
||||||
} else { }; // GOOD: same as above.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bad_new_nothrow_in_exception_body() {
|
void goodNew_1_0()
|
||||||
try {
|
{
|
||||||
new (std::nothrow) int[100]; // BAD
|
while (true) {
|
||||||
int *p1 = new (std::nothrow) int[100]; // BAD
|
int* p = new(std::nothrow) int[100]; // GOOD
|
||||||
|
if (p == nullptr) {
|
||||||
int *p2;
|
// std::cout << "Allocation returned nullptr\n";
|
||||||
p2 = new (std::nothrow) int[100]; // BAD
|
break;
|
||||||
} catch (const std::bad_alloc &) {
|
}
|
||||||
}
|
int* p1;
|
||||||
|
p1 = new(std::nothrow) int[100]; // GOOD
|
||||||
|
if (p1 == nullptr) {
|
||||||
|
// std::cout << "Allocation returned nullptr\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (new(std::nothrow) int[100] == nullptr) { // GOOD
|
||||||
|
// std::cout << "Allocation returned nullptr\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void good_new_has_exception_handling() {
|
|
||||||
try {
|
|
||||||
int *p1 = new int[100]; // GOOD
|
|
||||||
} catch (...) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void good_new_handles_nullptr() {
|
|
||||||
int *p1 = new (std::nothrow) int[100]; // GOOD
|
|
||||||
if (p1 == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int *p2;
|
|
||||||
p2 = new (std::nothrow) int[100]; // GOOD
|
|
||||||
if (p2 == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int *p3;
|
|
||||||
p3 = new (std::nothrow) int[100]; // GOOD
|
|
||||||
if (p3 != nullptr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
int *p4;
|
|
||||||
p4 = new (std::nothrow) int[100]; // GOOD
|
|
||||||
if (p4) {
|
|
||||||
} else
|
|
||||||
return;
|
|
||||||
|
|
||||||
int *p5;
|
|
||||||
p5 = new (std::nothrow) int[100]; // GOOD
|
|
||||||
if (p5 != nullptr) {
|
|
||||||
} else
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (new (std::nothrow) int[100] == nullptr)
|
|
||||||
return; // GOOD
|
|
||||||
}
|
|
||||||
|
|
||||||
void* operator new(std::size_t count, void*) noexcept;
|
|
||||||
void* operator new[](std::size_t count, void*) noexcept;
|
|
||||||
|
|
||||||
struct Foo {
|
|
||||||
Foo() noexcept;
|
|
||||||
Foo(int);
|
|
||||||
|
|
||||||
operator bool();
|
|
||||||
};
|
|
||||||
|
|
||||||
void bad_placement_new_with_exception_handling() {
|
|
||||||
char buffer[1024];
|
|
||||||
try { new (buffer) Foo; } // BAD
|
|
||||||
catch (...) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
void good_placement_new_with_exception_handling() {
|
|
||||||
char buffer[1024];
|
|
||||||
try { new (buffer) Foo(42); } // GOOD: Foo constructor might throw
|
|
||||||
catch (...) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
int unknown_value_without_exceptions() noexcept;
|
|
||||||
|
|
||||||
void may_throw() {
|
|
||||||
if(unknown_value_without_exceptions()) {
|
|
||||||
throw "bad luck exception!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void unknown_code_that_may_throw(int*);
|
|
||||||
void unknown_code_that_will_not_throw(int*) noexcept;
|
|
||||||
|
|
||||||
void calls_throwing_code(int* p) {
|
|
||||||
if(unknown_value_without_exceptions()) unknown_code_that_may_throw(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void calls_non_throwing(int* p) {
|
|
||||||
if (unknown_value_without_exceptions()) unknown_code_that_will_not_throw(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void good_new_with_throwing_call() {
|
|
||||||
try {
|
|
||||||
int* p1 = new(std::nothrow) int; // GOOD
|
|
||||||
may_throw();
|
|
||||||
} catch(...) { }
|
|
||||||
|
|
||||||
try {
|
|
||||||
int* p2 = new(std::nothrow) int; // GOOD
|
|
||||||
Foo f(10);
|
|
||||||
} catch(...) { }
|
|
||||||
|
|
||||||
try {
|
|
||||||
int* p3 = new(std::nothrow) int; // GOOD
|
|
||||||
calls_throwing_code(p3);
|
|
||||||
} catch(...) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
void bad_new_with_nonthrowing_call() {
|
|
||||||
try {
|
|
||||||
int* p1 = new(std::nothrow) int; // BAD
|
|
||||||
calls_non_throwing(p1);
|
|
||||||
} catch(...) { }
|
|
||||||
|
|
||||||
try {
|
|
||||||
int* p2 = new(std::nothrow) int; // GOOD: boolean conversion constructor might throw
|
|
||||||
Foo f;
|
|
||||||
if(f) { }
|
|
||||||
} catch(...) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
void bad_new_catch_baseclass_of_bad_alloc() {
|
|
||||||
try {
|
|
||||||
int* p = new(std::nothrow) int; // BAD
|
|
||||||
} catch(const std::exception&) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
void good_new_catch_exception_in_assignment() {
|
|
||||||
int* p;
|
|
||||||
try {
|
|
||||||
p = new int; // GOOD
|
|
||||||
} catch(const std::bad_alloc&) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
void good_new_catch_exception_in_conversion() {
|
|
||||||
try {
|
|
||||||
long* p = (long*) new int; // GOOD
|
|
||||||
} catch(const std::bad_alloc&) { }
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
| 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 |
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
| 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. |
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user