mirror of
https://github.com/github/codeql.git
synced 2026-05-17 04:37:07 +02:00
Compare commits
2 Commits
remove-jav
...
max-schaef
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
152b290bdf | ||
|
|
044c759682 |
@@ -1,5 +1,4 @@
|
||||
{ "provide": [ "*/ql/src/qlpack.yml",
|
||||
"*/ql/lib/qlpack.yml",
|
||||
"*/ql/test/qlpack.yml",
|
||||
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
|
||||
"*/ql/examples/qlpack.yml",
|
||||
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -48,6 +48,3 @@
|
||||
*.gif -text
|
||||
*.dll -text
|
||||
*.pdb -text
|
||||
|
||||
java/ql/test/stubs/**/*.java linguist-generated=true
|
||||
java/ql/test/experimental/stubs/**/*.java linguist-generated=true
|
||||
11
.github/workflows/codeql-analysis.yml
vendored
11
.github/workflows/codeql-analysis.yml
vendored
@@ -11,8 +11,6 @@ on:
|
||||
- 'rc/*'
|
||||
paths:
|
||||
- 'csharp/**'
|
||||
- '.github/codeql/**'
|
||||
- '.github/workflows/codeql-analysis.yml'
|
||||
schedule:
|
||||
- cron: '0 9 * * 1'
|
||||
|
||||
@@ -40,8 +38,8 @@ jobs:
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
#- name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@main
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@main
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -50,8 +48,9 @@ jobs:
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
- run: |
|
||||
dotnet build csharp
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@main
|
||||
|
||||
2
.github/workflows/csv-coverage-update.yml
vendored
2
.github/workflows/csv-coverage-update.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
jobs:
|
||||
update:
|
||||
name: Update framework coverage report
|
||||
if: github.repository == 'github/codeql'
|
||||
if: github.event.repository.fork == false
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
@@ -17,9 +17,3 @@
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
|
||||
|
||||
# CodeQL tools and associated docs
|
||||
/docs/codeql-cli/ @github/codeql-cli-reviewers
|
||||
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
|
||||
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
|
||||
@@ -4,8 +4,8 @@ This open source repository contains the standard CodeQL libraries and queries t
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension to try out your queries on any open source project that's currently being analyzed.
|
||||
There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing CodeQL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode.html) extension to try out your queries on any open source project that's currently being analyzed.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -1,333 +1,332 @@
|
||||
{
|
||||
"DataFlow Java/C++/C#/Python": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll"
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.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/DataFlowImpl5.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Python Common": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll"
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll"
|
||||
],
|
||||
"TaintTracking::Configuration Java/C++/C#/Python": [
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Python Consistency checks": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll"
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll"
|
||||
],
|
||||
"DataFlow Java/C# Flow Summaries": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll"
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll"
|
||||
],
|
||||
"SsaReadPosition Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/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"
|
||||
],
|
||||
"Sign Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
|
||||
],
|
||||
"SignAnalysis Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll"
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll"
|
||||
],
|
||||
"Bound Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/Bound.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll"
|
||||
"java/ql/src/semmle/code/java/dataflow/Bound.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll"
|
||||
],
|
||||
"ModulusAnalysis Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/ModulusAnalysis.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/ModulusAnalysis.qll"
|
||||
"java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/ModulusAnalysis.qll"
|
||||
],
|
||||
"C++ SubBasicBlocks": [
|
||||
"cpp/ql/lib/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
|
||||
],
|
||||
"IR Instruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||
],
|
||||
"IR IRBlock": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||
],
|
||||
"IR IRVariable": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||
],
|
||||
"IR IRFunction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||
],
|
||||
"IR Operand": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
|
||||
],
|
||||
"IR IRType": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/IRType.qll"
|
||||
],
|
||||
"IR IRConfiguration": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll"
|
||||
],
|
||||
"IR UseSoundEscapeAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
|
||||
],
|
||||
"IR IRFunctionBase": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
|
||||
],
|
||||
"IR TInstruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
|
||||
],
|
||||
"IR TIRVariable": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
|
||||
],
|
||||
"IR IR": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll"
|
||||
],
|
||||
"IR IRConsistency": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll"
|
||||
],
|
||||
"IR PrintIR": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||
],
|
||||
"IR IntegerConstant": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerConstant.qll"
|
||||
],
|
||||
"IR IntegerInteval": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerInterval.qll"
|
||||
],
|
||||
"IR IntegerPartial": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerPartial.qll"
|
||||
],
|
||||
"IR Overlap": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/Overlap.qll"
|
||||
],
|
||||
"IR EdgeKind": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/EdgeKind.qll"
|
||||
],
|
||||
"IR MemoryAccessKind": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll"
|
||||
],
|
||||
"IR TempVariableTag": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll"
|
||||
],
|
||||
"IR Opcode": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/Opcode.qll"
|
||||
],
|
||||
"IR SSAConsistency": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
|
||||
],
|
||||
"C++ IR InstructionImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll"
|
||||
],
|
||||
"C++ IR IRImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll"
|
||||
],
|
||||
"C++ IR IRBlockImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C++ IR IRFunctionImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C++ IR IRVariableImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll"
|
||||
],
|
||||
"C++ IR OperandImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll"
|
||||
],
|
||||
"C++ IR PrintIRImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll"
|
||||
],
|
||||
"C++ SSA SSAConstructionImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll"
|
||||
],
|
||||
"SSA AliasAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
||||
],
|
||||
"SSA PrintAliasAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll"
|
||||
],
|
||||
"C++ SSA AliasAnalysisImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll"
|
||||
],
|
||||
"C++ IR ValueNumberingImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"IR SSA SimpleSSA": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
],
|
||||
"IR AliasConfiguration (unaliased_ssa)": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"IR SSA PrintSSA": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
],
|
||||
"IR ValueNumberInternal": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
],
|
||||
"C++ IR PrintValueNumbering": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
],
|
||||
"C++ IR ConstantAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll"
|
||||
],
|
||||
"C++ IR PrintConstantAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll"
|
||||
],
|
||||
"C++ IR ReachableBlock": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll"
|
||||
],
|
||||
"C++ IR PrintReachableBlock": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll"
|
||||
],
|
||||
"C++ IR Dominance": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll"
|
||||
],
|
||||
"C++ IR PrintDominance": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"C# IR InstructionImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
@@ -362,8 +361,8 @@
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"C# ControlFlowReachability": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
],
|
||||
"Inline Test Expectations": [
|
||||
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
@@ -379,11 +378,11 @@
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/SafeExternalAPIFunction.qll"
|
||||
],
|
||||
"XML": [
|
||||
"cpp/ql/lib/semmle/code/cpp/XML.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/XML.qll",
|
||||
"java/ql/lib/semmle/code/xml/XML.qll",
|
||||
"javascript/ql/lib/semmle/javascript/XML.qll",
|
||||
"python/ql/lib/semmle/python/xml/XML.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/XML.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/XML.qll",
|
||||
"java/ql/src/semmle/code/xml/XML.qll",
|
||||
"javascript/ql/src/semmle/javascript/XML.qll",
|
||||
"python/ql/src/semmle/python/xml/XML.qll"
|
||||
],
|
||||
"DuplicationProblems.inc.qhelp": [
|
||||
"cpp/ql/src/Metrics/Files/DuplicationProblems.inc.qhelp",
|
||||
@@ -437,29 +436,17 @@
|
||||
"python/ql/src/analysis/IDEContextual.qll"
|
||||
],
|
||||
"SSA C#": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll"
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll"
|
||||
],
|
||||
"CryptoAlgorithms Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll"
|
||||
"javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||
"python/ql/src/semmle/python/concepts/CryptoAlgorithms.qll"
|
||||
],
|
||||
"SensitiveDataHeuristics Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
||||
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll"
|
||||
],
|
||||
"ReDoS Util Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll",
|
||||
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll"
|
||||
],
|
||||
"ReDoS Exponential Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/performance/ExponentialBackTracking.qll",
|
||||
"python/ql/lib/semmle/python/security/performance/ExponentialBackTracking.qll"
|
||||
],
|
||||
"ReDoS Polynomial Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
|
||||
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll"
|
||||
"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 'Uncontrolled data in SQL query' (cpp/sql-injection) query now supports the `libpqxx` library.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm
|
||||
* The 'Uncontrolled data in arithmetic expression' (cpp/uncontrolled-arithmetic) query now recognizes more sources of randomness.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The 'Wrong type of arguments to formatting function' (cpp/wrong-type-format-argument) query is now more accepting of the string and character formatting differences between Microsoft and non-Microsoft platforms. There are now fewer false positive results.
|
||||
@@ -1,3 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file) query now uses dataflow to produce additional results.
|
||||
* Heuristics in the SensitiveExprs.qll library have been improved, making the "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file), "Cleartext storage of sensitive information in buffer" (cpp/cleartext-storage-buffer) and "Cleartext storage of sensitive information in an SQLite" (cpp/cleartext-storage-database) queries more accurate.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Improvements have been made to the `cpp/toctou-race-condition` query, both to find more correct results and fewer false positive results.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm
|
||||
* Improvements made to the (`cpp/uncontrolled-arithmetic`) query, reducing the frequency of false positive results.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Virtual function specifiers are now accessible via the new predicates on `Function` (`.isDeclaredVirtual`, `.isOverride`, and `.isFinal`).
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Added `Function.hasTrailingReturnType` predicate to check whether a function was declared with a trailing return type.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Added `RoutineType.hasCLinkage` predicate to check whether a function type has "C" language linkage.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Lowered the precision of `cpp/potentially-dangerous-function` so it is run but not displayed on LGTM by default and so it's only run and displayed on Code Scanning if a broader suite like `cpp-security-extended` is opted into.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Added `Element.getPrimaryQlClasses()` predicate, which gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The query `cpp/implicit-bitfield-downcast` now accounts for C++ reference types, which leads to more true positive results.
|
||||
@@ -1,4 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The `SimpleRangeAnalysis` library includes information from the
|
||||
immediate guard for determining the upper bound of a stack
|
||||
variable for improved accuracy.
|
||||
@@ -1,4 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The `memberMayBeVarSize` predicate considers more fields to be variable size.
|
||||
As a result, the "Static buffer overflow" query (cpp/static-buffer-overflow)
|
||||
produces fewer false positives.
|
||||
@@ -1,3 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Increase precision to high for the "Static buffer overflow" query
|
||||
(`cpp/static-buffer-overflow`). This means the query is run and displayed by default on Code Scanning and LGTM.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
dependencies: {}
|
||||
compiled: false
|
||||
lockVersion: 1.0.0
|
||||
@@ -1,4 +1,3 @@
|
||||
name: codeql/cpp-examples
|
||||
version: 0.0.2
|
||||
dependencies:
|
||||
codeql/cpp-all: "*"
|
||||
name: codeql-cpp-examples
|
||||
version: 0.0.0
|
||||
libraryPathDependencies: codeql-cpp
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
dependencies: {}
|
||||
compiled: false
|
||||
lockVersion: 1.0.0
|
||||
@@ -1,7 +0,0 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.0.2
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
library: true
|
||||
dependencies:
|
||||
codeql/cpp-upgrades: 0.0.2
|
||||
@@ -1,297 +0,0 @@
|
||||
/**
|
||||
* Provides the `Element` class, which is the base class for all classes representing C or C++
|
||||
* program elements.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
private import semmle.code.cpp.Enclosing
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* Get the `Element` that represents this `@element`.
|
||||
* Normally this will simply be a cast of `e`, but sometimes it is not.
|
||||
* For example, for an incomplete struct `e` the result may be a
|
||||
* complete struct with the same name.
|
||||
*/
|
||||
pragma[inline]
|
||||
Element mkElement(@element e) { unresolveElement(result) = e }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets an `@element` that resolves to the `Element`. This should
|
||||
* normally only be called from member predicates, where `e` is not
|
||||
* `this` and you need the result for an argument to a database
|
||||
* extensional.
|
||||
* See `underlyingElement` for when `e` is `this`.
|
||||
*/
|
||||
pragma[inline]
|
||||
@element unresolveElement(Element e) {
|
||||
not result instanceof @usertype and
|
||||
result = e
|
||||
or
|
||||
e = resolveClass(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the `@element` that this `Element` extends. This should normally
|
||||
* only be called from member predicates, where `e` is `this` and you
|
||||
* need the result for an argument to a database extensional.
|
||||
* See `unresolveElement` for when `e` is not `this`.
|
||||
*/
|
||||
@element underlyingElement(Element e) { result = e }
|
||||
|
||||
/**
|
||||
* A C/C++ element with no member predicates other than `toString`. Not for
|
||||
* general use. This class does not define a location, so classes wanting to
|
||||
* change their location without affecting other classes can extend
|
||||
* `ElementBase` instead of `Element` to create a new rootdef for `getURL`,
|
||||
* `getLocation`, or `hasLocationInfo`.
|
||||
*/
|
||||
class ElementBase extends @element {
|
||||
/** Gets a textual representation of this element. */
|
||||
cached
|
||||
string toString() { none() }
|
||||
|
||||
/** DEPRECATED: use `getAPrimaryQlClass` instead. */
|
||||
deprecated string getCanonicalQLClass() { result = this.getAPrimaryQlClass() }
|
||||
|
||||
/**
|
||||
* Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.
|
||||
*/
|
||||
final string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") }
|
||||
|
||||
/**
|
||||
* Gets the name of a primary CodeQL class to which this element belongs.
|
||||
*
|
||||
* For most elements, this is simply the most precise syntactic category to
|
||||
* which they belong; for example, `AddExpr` is a primary class, but
|
||||
* `BinaryOperation` is not.
|
||||
*
|
||||
* This predicate can have multiple results if multiple primary classes match.
|
||||
* For some elements, this predicate may not have a result.
|
||||
*/
|
||||
string getAPrimaryQlClass() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ element. This class is the base class for all C/C++
|
||||
* elements, such as functions, classes, expressions, and so on.
|
||||
*/
|
||||
class Element extends ElementBase {
|
||||
/** Gets the primary file where this element occurs. */
|
||||
File getFile() { result = this.getLocation().getFile() }
|
||||
|
||||
/**
|
||||
* Holds if this element may be from source. This predicate holds for all
|
||||
* elements, except for those in the dummy file, whose name is the empty string.
|
||||
* The dummy file contains declarations that are built directly into the compiler.
|
||||
*/
|
||||
predicate fromSource() { this.getFile().fromSource() }
|
||||
|
||||
/**
|
||||
* Holds if this element may be from a library.
|
||||
*
|
||||
* DEPRECATED: always true.
|
||||
*/
|
||||
deprecated predicate fromLibrary() { this.getFile().fromLibrary() }
|
||||
|
||||
/** Gets the primary location of this element. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/**
|
||||
* Gets the source of this element: either itself or a macro that expanded
|
||||
* to this element.
|
||||
*
|
||||
* If the element is not in a macro expansion, then the "root" is just
|
||||
* the element itself. Otherwise, it is the definition of the innermost
|
||||
* macro whose expansion the element is in.
|
||||
*
|
||||
* This method is useful for filtering macro results in checks: simply
|
||||
* blame `e.findRootCause` rather than `e`. This will report only bugs
|
||||
* that are not in macros, and in addition report macros that (somewhere)
|
||||
* expand to a bug.
|
||||
*/
|
||||
Element findRootCause() {
|
||||
if exists(MacroInvocation mi | this = mi.getAGeneratedElement())
|
||||
then
|
||||
exists(MacroInvocation mi |
|
||||
this = mi.getAGeneratedElement() and
|
||||
not exists(MacroInvocation closer |
|
||||
this = closer.getAGeneratedElement() and
|
||||
mi = closer.getParentInvocation+()
|
||||
) and
|
||||
result = mi.getMacro()
|
||||
)
|
||||
else result = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent scope of this `Element`, if any.
|
||||
* A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `BlockStmt`, a `Function`,
|
||||
* or certain kinds of `Statement`.
|
||||
*/
|
||||
Element getParentScope() {
|
||||
// result instanceof class
|
||||
exists(Declaration m |
|
||||
m = this and
|
||||
result = m.getDeclaringType() and
|
||||
not this instanceof EnumConstant
|
||||
)
|
||||
or
|
||||
exists(TemplateClass tc | this = tc.getATemplateArgument() and result = tc)
|
||||
or
|
||||
// result instanceof namespace
|
||||
exists(Namespace n | result = n and n.getADeclaration() = this)
|
||||
or
|
||||
exists(FriendDecl d, Namespace n | this = d and n.getADeclaration() = d and result = n)
|
||||
or
|
||||
exists(Namespace n | this = n and result = n.getParentNamespace())
|
||||
or
|
||||
// result instanceof stmt
|
||||
exists(LocalVariable v |
|
||||
this = v and
|
||||
exists(DeclStmt ds | ds.getADeclaration() = v and result = ds.getParent())
|
||||
)
|
||||
or
|
||||
exists(Parameter p | this = p and result = p.getFunction())
|
||||
or
|
||||
exists(GlobalVariable g, Namespace n | this = g and n.getADeclaration() = g and result = n)
|
||||
or
|
||||
exists(EnumConstant e | this = e and result = e.getDeclaringEnum())
|
||||
or
|
||||
// result instanceof block|function
|
||||
exists(BlockStmt b | this = b and blockscope(unresolveElement(b), unresolveElement(result)))
|
||||
or
|
||||
exists(TemplateFunction tf | this = tf.getATemplateArgument() and result = tf)
|
||||
or
|
||||
// result instanceof stmt
|
||||
exists(ControlStructure s | this = s and result = s.getParent())
|
||||
or
|
||||
using_container(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element comes from a macro expansion. Only elements that
|
||||
* are entirely generated by a macro are included - for elements that
|
||||
* partially come from a macro, see `isAffectedByMacro`.
|
||||
*/
|
||||
predicate isInMacroExpansion() { inMacroExpansion(this) }
|
||||
|
||||
/**
|
||||
* Holds if this element is affected in any way by a macro. All elements
|
||||
* that are totally or partially generated by a macro are included, so
|
||||
* this is a super-set of `isInMacroExpansion`.
|
||||
*/
|
||||
predicate isAffectedByMacro() { affectedByMacro(this) }
|
||||
|
||||
private Element getEnclosingElementPref() {
|
||||
enclosingfunction(underlyingElement(this), unresolveElement(result)) or
|
||||
result.(Function) = stmtEnclosingElement(this) or
|
||||
this.(LocalScopeVariable).getFunction() = result or
|
||||
enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _) or
|
||||
derivations(underlyingElement(this), unresolveElement(result), _, _, _) or
|
||||
stmtparents(underlyingElement(this), _, unresolveElement(result)) or
|
||||
exprparents(underlyingElement(this), _, unresolveElement(result)) or
|
||||
namequalifiers(underlyingElement(this), unresolveElement(result), _, _) or
|
||||
initialisers(underlyingElement(this), unresolveElement(result), _, _) or
|
||||
exprconv(unresolveElement(result), underlyingElement(this)) or
|
||||
param_decl_bind(underlyingElement(this), _, unresolveElement(result)) or
|
||||
using_container(unresolveElement(result), underlyingElement(this)) or
|
||||
static_asserts(unresolveElement(this), _, _, _, underlyingElement(result))
|
||||
}
|
||||
|
||||
/** Gets the closest `Element` enclosing this one. */
|
||||
cached
|
||||
Element getEnclosingElement() {
|
||||
result = getEnclosingElementPref()
|
||||
or
|
||||
not exists(getEnclosingElementPref()) and
|
||||
(
|
||||
this = result.(Class).getAMember()
|
||||
or
|
||||
result = exprEnclosingElement(this)
|
||||
or
|
||||
var_decls(underlyingElement(this), unresolveElement(result), _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this `Element` is a part of a template instantiation (but not
|
||||
* the template itself).
|
||||
*/
|
||||
predicate isFromTemplateInstantiation(Element instantiation) {
|
||||
exists(Element e | isFromTemplateInstantiationRec(e, instantiation) |
|
||||
this = e or
|
||||
this.(DeclarationEntry).getDeclaration() = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this `Element` is part of a template `template` (not if it is
|
||||
* part of an instantiation of `template`). This means it is represented in
|
||||
* the database purely as syntax and without guarantees on the presence or
|
||||
* correctness of type-based operations such as implicit conversions.
|
||||
*
|
||||
* If an element is nested within several templates, this predicate holds with
|
||||
* a value of `template` for each containing template.
|
||||
*/
|
||||
predicate isFromUninstantiatedTemplate(Element template) {
|
||||
exists(Element e | isFromUninstantiatedTemplateRec(e, template) |
|
||||
this = e or
|
||||
this.(DeclarationEntry).getDeclaration() = e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate isFromTemplateInstantiationRec(Element e, Element instantiation) {
|
||||
instantiation.(Function).isConstructedFrom(_) and
|
||||
e = instantiation
|
||||
or
|
||||
instantiation.(Class).isConstructedFrom(_) and
|
||||
e = instantiation
|
||||
or
|
||||
instantiation.(Variable).isConstructedFrom(_) and
|
||||
e = instantiation
|
||||
or
|
||||
isFromTemplateInstantiationRec(e.getEnclosingElement(), instantiation)
|
||||
}
|
||||
|
||||
private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
|
||||
is_class_template(unresolveElement(template)) and
|
||||
e = template
|
||||
or
|
||||
is_function_template(unresolveElement(template)) and
|
||||
e = template
|
||||
or
|
||||
is_variable_template(unresolveElement(template)) and
|
||||
e = template
|
||||
or
|
||||
isFromUninstantiatedTemplateRec(e.getEnclosingElement(), template)
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++11 `static_assert` or C11 `_Static_assert` construct. For example each
|
||||
* line in the following example contains a static assert:
|
||||
* ```
|
||||
* static_assert(sizeof(MyStruct) <= 4096);
|
||||
* static_assert(sizeof(MyStruct) <= 4096, "MyStruct is too big!");
|
||||
* ```
|
||||
*/
|
||||
class StaticAssert extends Locatable, @static_assert {
|
||||
override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" }
|
||||
|
||||
/**
|
||||
* Gets the expression which this static assertion ensures is true.
|
||||
*/
|
||||
Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the message which will be reported by the compiler if this static assertion fails.
|
||||
*/
|
||||
string getMessage() { static_asserts(underlyingElement(this), _, result, _, _) }
|
||||
|
||||
override Location getLocation() { static_asserts(underlyingElement(this), _, _, result, _) }
|
||||
}
|
||||
@@ -1,448 +0,0 @@
|
||||
/**
|
||||
* Provides classes representing files and folders.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Declaration
|
||||
import semmle.code.cpp.metrics.MetricFile
|
||||
|
||||
/** A file or folder. */
|
||||
class Container extends Locatable, @container {
|
||||
/**
|
||||
* Gets the absolute, canonical path of this container, using forward slashes
|
||||
* as path separator.
|
||||
*
|
||||
* The path starts with a _root prefix_ followed by zero or more _path
|
||||
* segments_ separated by forward slashes.
|
||||
*
|
||||
* The root prefix is of one of the following forms:
|
||||
*
|
||||
* 1. A single forward slash `/` (Unix-style)
|
||||
* 2. An upper-case drive letter followed by a colon and a forward slash,
|
||||
* such as `C:/` (Windows-style)
|
||||
* 3. Two forward slashes, a computer name, and then another forward slash,
|
||||
* such as `//FileServer/` (UNC-style)
|
||||
*
|
||||
* Path segments are never empty (that is, absolute paths never contain two
|
||||
* contiguous slashes, except as part of a UNC-style root prefix). Also, path
|
||||
* segments never contain forward slashes, and no path segment is of the
|
||||
* form `.` (one dot) or `..` (two dots).
|
||||
*
|
||||
* Note that an absolute path never ends with a forward slash, except if it is
|
||||
* a bare root prefix, that is, the path has no path segments. A container
|
||||
* whose absolute path has no segments is always a `Folder`, not a `File`.
|
||||
*/
|
||||
string getAbsolutePath() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets a URL representing the location of this container.
|
||||
*
|
||||
* For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls).
|
||||
*/
|
||||
deprecated string getURL() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* Gets the relative path of this file or folder from the root folder of the
|
||||
* analyzed source location. The relative path of the root folder itself is
|
||||
* the empty string.
|
||||
*
|
||||
* This has no result if the container is outside the source root, that is,
|
||||
* if the root folder is not a reflexive, transitive parent of this container.
|
||||
*/
|
||||
string getRelativePath() {
|
||||
exists(string absPath, string pref |
|
||||
absPath = getAbsolutePath() and sourceLocationPrefix(pref)
|
||||
|
|
||||
absPath = pref and result = ""
|
||||
or
|
||||
absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
|
||||
not result.matches("/%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base name of this container including extension, that is, the last
|
||||
* segment of its absolute path, or the empty string if it has no segments.
|
||||
*
|
||||
* Here are some examples of absolute paths and the corresponding base names
|
||||
* (surrounded with quotes to avoid ambiguity):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Base name</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"tst.js"</td></tr>
|
||||
* <tr><td>"C:/Program Files (x86)"</td><td>"Program Files (x86)"</td></tr>
|
||||
* <tr><td>"/"</td><td>""</td></tr>
|
||||
* <tr><td>"C:/"</td><td>""</td></tr>
|
||||
* <tr><td>"D:/"</td><td>""</td></tr>
|
||||
* <tr><td>"//FileServer/"</td><td>""</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getBaseName() {
|
||||
result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extension of this container, that is, the suffix of its base name
|
||||
* after the last dot character, if any.
|
||||
*
|
||||
* In particular,
|
||||
*
|
||||
* - if the name does not include a dot, there is no extension, so this
|
||||
* predicate has no result;
|
||||
* - if the name ends in a dot, the extension is the empty string;
|
||||
* - if the name contains multiple dots, the extension follows the last dot.
|
||||
*
|
||||
* Here are some examples of absolute paths and the corresponding extensions
|
||||
* (surrounded with quotes to avoid ambiguity):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Extension</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"js"</td></tr>
|
||||
* <tr><td>"/tmp/.classpath"</td><td>"classpath"</td></tr>
|
||||
* <tr><td>"/bin/bash"</td><td>not defined</td></tr>
|
||||
* <tr><td>"/tmp/tst2."</td><td>""</td></tr>
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) }
|
||||
|
||||
/**
|
||||
* Gets the stem of this container, that is, the prefix of its base name up to
|
||||
* (but not including) the last dot character if there is one, or the entire
|
||||
* base name if there is not.
|
||||
*
|
||||
* Here are some examples of absolute paths and the corresponding stems
|
||||
* (surrounded with quotes to avoid ambiguity):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Stem</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"tst"</td></tr>
|
||||
* <tr><td>"/tmp/.classpath"</td><td>""</td></tr>
|
||||
* <tr><td>"/bin/bash"</td><td>"bash"</td></tr>
|
||||
* <tr><td>"/tmp/tst2."</td><td>"tst2"</td></tr>
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) }
|
||||
|
||||
/** Gets the parent container of this file or folder, if any. */
|
||||
Container getParentContainer() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
/** Gets a file or sub-folder in this container. */
|
||||
Container getAChildContainer() { this = result.getParentContainer() }
|
||||
|
||||
/** Gets a file in this container. */
|
||||
File getAFile() { result = getAChildContainer() }
|
||||
|
||||
/** Gets the file in this container that has the given `baseName`, if any. */
|
||||
File getFile(string baseName) {
|
||||
result = getAFile() and
|
||||
result.getBaseName() = baseName
|
||||
}
|
||||
|
||||
/** Gets a sub-folder in this container. */
|
||||
Folder getAFolder() { result = getAChildContainer() }
|
||||
|
||||
/** Gets the sub-folder in this container that has the given `baseName`, if any. */
|
||||
Folder getFolder(string baseName) {
|
||||
result = getAFolder() and
|
||||
result.getBaseName() = baseName
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a textual representation of the path of this container.
|
||||
*
|
||||
* This is the absolute path of the container.
|
||||
*/
|
||||
override string toString() { result = getAbsolutePath() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A folder that was observed on disk during the build process.
|
||||
*
|
||||
* For the example folder name of "/usr/home/me", the path decomposes to:
|
||||
*
|
||||
* 1. "/usr/home" - see `getParentContainer`.
|
||||
* 2. "me" - see `getBaseName`.
|
||||
*
|
||||
* To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class Folder extends Container, @folder {
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result) }
|
||||
|
||||
override Location getLocation() {
|
||||
result.getContainer() = this and
|
||||
result.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Folder" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets the URL of this folder.
|
||||
*/
|
||||
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath` instead.
|
||||
* Gets the name of this folder.
|
||||
*/
|
||||
deprecated string getName() { folders(underlyingElement(this), result) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath` instead.
|
||||
* Holds if this element is named `name`.
|
||||
*/
|
||||
deprecated predicate hasName(string name) { name = this.getName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath` instead.
|
||||
* Gets the full name of this folder.
|
||||
*/
|
||||
deprecated string getFullName() { result = this.getName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getBaseName` instead.
|
||||
* Gets the last part of the folder name.
|
||||
*/
|
||||
deprecated string getShortName() { result = this.getBaseName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getParentContainer` instead.
|
||||
* Gets the parent folder.
|
||||
*/
|
||||
deprecated Folder getParent() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A file that was observed on disk during the build process.
|
||||
*
|
||||
* For the example filename of "/usr/home/me/myprogram.c", the filename
|
||||
* decomposes to:
|
||||
*
|
||||
* 1. "/usr/home/me" - see `getParentContainer`.
|
||||
* 2. "myprogram.c" - see `getBaseName`.
|
||||
*
|
||||
* The base name further decomposes into the _stem_ and _extension_ -- see
|
||||
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class File extends Container, @file {
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result) }
|
||||
|
||||
override string toString() { result = Container.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "File" }
|
||||
|
||||
override Location getLocation() {
|
||||
result.getContainer() = this and
|
||||
result.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets the URL of this file.
|
||||
*/
|
||||
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
/** Holds if this file was compiled as C (at any point). */
|
||||
predicate compiledAsC() { fileannotations(underlyingElement(this), 1, "compiled as c", "1") }
|
||||
|
||||
/** Holds if this file was compiled as C++ (at any point). */
|
||||
predicate compiledAsCpp() { fileannotations(underlyingElement(this), 1, "compiled as c++", "1") }
|
||||
|
||||
/**
|
||||
* Holds if this file was compiled by a Microsoft compiler (at any point).
|
||||
*
|
||||
* Note: currently unreliable - on some projects only some of the files that
|
||||
* are compiled by a Microsoft compiler are detected by this predicate.
|
||||
*/
|
||||
predicate compiledAsMicrosoft() {
|
||||
exists(File f, Compilation c |
|
||||
c.getAFileCompiled() = f and
|
||||
(
|
||||
c.getAnArgument() = "--microsoft" or
|
||||
c.getAnArgument()
|
||||
.toLowerCase()
|
||||
.replaceAll("\\", "/")
|
||||
.matches(["%/cl.exe", "%/clang-cl.exe"])
|
||||
) and
|
||||
f.getAnIncludedFile*() = this
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a top-level element declared in this file. */
|
||||
Declaration getATopLevelDeclaration() { result.getAFile() = this and result.isTopLevel() }
|
||||
|
||||
/** Gets a declaration in this file. */
|
||||
Declaration getADeclaration() { result.getAFile() = this }
|
||||
|
||||
/** Holds if this file uses the given macro. */
|
||||
predicate usesMacro(Macro m) {
|
||||
exists(MacroInvocation mi |
|
||||
mi.getFile() = this and
|
||||
mi.getMacro() = m
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a file that is directly included from this file (using a
|
||||
* pre-processor directive like `#include`).
|
||||
*/
|
||||
File getAnIncludedFile() {
|
||||
exists(Include i | i.getFile() = this and i.getIncludedFile() = result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this file may be from source. This predicate holds for all files
|
||||
* except the dummy file, whose name is the empty string, which contains
|
||||
* declarations that are built into the compiler.
|
||||
*/
|
||||
override predicate fromSource() { numlines(underlyingElement(this), _, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this file may be from a library.
|
||||
*
|
||||
* DEPRECATED: For historical reasons this is true for any file.
|
||||
*/
|
||||
deprecated override predicate fromLibrary() { any() }
|
||||
|
||||
/** Gets the metric file. */
|
||||
MetricFile getMetrics() { result = this }
|
||||
|
||||
/**
|
||||
* Gets the remainder of the base name after the first dot character. Note
|
||||
* that the name of this predicate is in plural form, unlike `getExtension`,
|
||||
* which gets the remainder of the base name after the _last_ dot character.
|
||||
*
|
||||
* Predicates `getStem` and `getExtension` should be preferred over
|
||||
* `getShortName` and `getExtensions` since the former pair is compatible
|
||||
* with the file libraries of other languages.
|
||||
* Note the slight difference between this predicate and `getStem`:
|
||||
* for example, for "file.tar.gz", this predicate will have the result
|
||||
* "tar.gz", while `getExtension` will have the result "gz".
|
||||
*/
|
||||
string getExtensions() {
|
||||
exists(string name, int firstDotPos |
|
||||
name = this.getBaseName() and
|
||||
firstDotPos = min([name.indexOf("."), name.length() - 1]) and
|
||||
result = name.suffix(firstDotPos + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the short name of this file, that is, the prefix of its base name up
|
||||
* to (but not including) the first dot character if there is one, or the
|
||||
* entire base name if there is not. For example, if the full name is
|
||||
* "/path/to/filename.a.bcd" then the short name is "filename".
|
||||
*
|
||||
* Predicates `getStem` and `getExtension` should be preferred over
|
||||
* `getShortName` and `getExtensions` since the former pair is compatible
|
||||
* with the file libraries of other languages.
|
||||
* Note the slight difference between this predicate and `getStem`:
|
||||
* for example, for "file.tar.gz", this predicate will have the result
|
||||
* "file", while `getStem` will have the result "file.tar".
|
||||
*/
|
||||
string getShortName() {
|
||||
exists(string name, int firstDotPos |
|
||||
name = this.getBaseName() and
|
||||
firstDotPos = min([name.indexOf("."), name.length()]) and
|
||||
result = name.prefix(firstDotPos)
|
||||
)
|
||||
or
|
||||
this.getAbsolutePath() = "" and
|
||||
result = ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if any file was compiled by a Microsoft compiler.
|
||||
*/
|
||||
predicate anyFileCompiledAsMicrosoft() { any(File f).compiledAsMicrosoft() }
|
||||
|
||||
/**
|
||||
* A C/C++ header file, as determined (mainly) by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is included anywhere (using a
|
||||
* pre-processor directive like `#include`), use `Include.getIncludedFile`.
|
||||
*/
|
||||
class HeaderFile extends File {
|
||||
HeaderFile() {
|
||||
this.getExtension().toLowerCase() =
|
||||
["h", "r", "hpp", "hxx", "h++", "hh", "hp", "tcc", "tpp", "txx", "t++"]
|
||||
or
|
||||
not exists(this.getExtension()) and
|
||||
exists(Include i | i.getIncludedFile() = this)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "HeaderFile" }
|
||||
|
||||
/**
|
||||
* Holds if this header file does not contain any declaration entries or top level
|
||||
* declarations. For example it might be:
|
||||
* - a file containing only preprocessor directives and/or comments
|
||||
* - an empty file
|
||||
* - a file that contains non-top level code or data that's included in an
|
||||
* unusual way
|
||||
*/
|
||||
predicate noTopLevelCode() {
|
||||
not exists(DeclarationEntry de | de.getFile() = this) and
|
||||
not exists(Declaration d | d.getFile() = this and d.isTopLevel()) and
|
||||
not exists(UsingEntry ue | ue.getFile() = this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C source file, as determined by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is compiled as C code, use
|
||||
* `File.compiledAsC`.
|
||||
*/
|
||||
class CFile extends File {
|
||||
CFile() { this.getExtension().toLowerCase() = ["c", "i"] }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CFile" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ source file, as determined by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is compiled as C++ code, use
|
||||
* `File.compiledAsCpp`.
|
||||
*/
|
||||
class CppFile extends File {
|
||||
CppFile() {
|
||||
this.getExtension().toLowerCase() =
|
||||
["cpp", "cxx", "c++", "cc", "cp", "icc", "ipp", "ixx", "i++", "ii"]
|
||||
// Note: .C files are indistinguishable from .c files on some
|
||||
// file systems, so we just treat them as CFile's.
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CppFile" }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Objective-C is no longer supported.
|
||||
* An Objective C source file, as determined by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is compiled as Objective C
|
||||
* code, use `File.compiledAsObjC`.
|
||||
*/
|
||||
deprecated class ObjCFile extends File {
|
||||
ObjCFile() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Objective-C is no longer supported.
|
||||
* An Objective C++ source file, as determined by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is compiled as Objective C++
|
||||
* code, use `File.compiledAsObjCpp`.
|
||||
*/
|
||||
deprecated class ObjCppFile extends File {
|
||||
ObjCppFile() { none() }
|
||||
}
|
||||
@@ -1,876 +0,0 @@
|
||||
/**
|
||||
* Provides classes for working with functions, including template functions.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
import semmle.code.cpp.Class
|
||||
import semmle.code.cpp.Parameter
|
||||
import semmle.code.cpp.exprs.Call
|
||||
import semmle.code.cpp.metrics.MetricFunction
|
||||
import semmle.code.cpp.Linkage
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* A C/C++ function [N4140 8.3.5]. Both member functions and non-member
|
||||
* functions are included. For example the function `MyFunction` in:
|
||||
* ```
|
||||
* void MyFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Function has a one-to-many relationship with FunctionDeclarationEntry,
|
||||
* because the same function can be declared in multiple locations. This
|
||||
* relationship between `Declaration` and `DeclarationEntry` is explained
|
||||
* in more detail in `Declaration.qll`.
|
||||
*/
|
||||
class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
override string getName() { functions(underlyingElement(this), result, _) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getIdentityString(Declaration)` from `semmle.code.cpp.Print` instead.
|
||||
* Gets the full signature of this function, including return type, parameter
|
||||
* types, and template arguments.
|
||||
*
|
||||
* For example, in the following code:
|
||||
* ```
|
||||
* template<typename T> T min(T x, T y);
|
||||
* int z = min(5, 7);
|
||||
* ```
|
||||
* The full signature of the function called on the last line would be
|
||||
* "min<int>(int, int) -> int", and the full signature of the uninstantiated
|
||||
* template on the first line would be "min<T>(T, T) -> T".
|
||||
*/
|
||||
string getFullSignature() {
|
||||
exists(string name, string templateArgs, string args |
|
||||
result = name + templateArgs + args + " -> " + getType().toString() and
|
||||
name = getQualifiedName() and
|
||||
(
|
||||
if exists(getATemplateArgument())
|
||||
then
|
||||
templateArgs =
|
||||
"<" +
|
||||
concat(int i |
|
||||
exists(getTemplateArgument(i))
|
||||
|
|
||||
getTemplateArgument(i).toString(), ", " order by i
|
||||
) + ">"
|
||||
else templateArgs = ""
|
||||
) and
|
||||
args =
|
||||
"(" +
|
||||
concat(int i |
|
||||
exists(getParameter(i))
|
||||
|
|
||||
getParameter(i).getType().toString(), ", " order by i
|
||||
) + ")"
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a specifier of this function. */
|
||||
override Specifier getASpecifier() {
|
||||
funspecifiers(underlyingElement(this), unresolveElement(result)) or
|
||||
result.hasName(getADeclarationEntry().getASpecifier())
|
||||
}
|
||||
|
||||
/** Gets an attribute of this function. */
|
||||
Attribute getAnAttribute() { funcattributes(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/** Holds if this function is generated by the compiler. */
|
||||
predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) }
|
||||
|
||||
/** Holds if this function is inline. */
|
||||
predicate isInline() { this.hasSpecifier("inline") }
|
||||
|
||||
/**
|
||||
* Holds if this function is virtual.
|
||||
*
|
||||
* Unlike `isDeclaredVirtual()`, `isVirtual()` holds even if the function
|
||||
* is not explicitly declared with the `virtual` specifier.
|
||||
*/
|
||||
predicate isVirtual() { this.hasSpecifier("virtual") }
|
||||
|
||||
/** Holds if this function is declared with the `virtual` specifier. */
|
||||
predicate isDeclaredVirtual() { this.hasSpecifier("declared_virtual") }
|
||||
|
||||
/** Holds if this function is declared with the `override` specifier. */
|
||||
predicate isOverride() { this.hasSpecifier("override") }
|
||||
|
||||
/** Holds if this function is declared with the `final` specifier. */
|
||||
predicate isFinal() { this.hasSpecifier("final") }
|
||||
|
||||
/**
|
||||
* Holds if this function is deleted.
|
||||
* This may be because it was explicitly deleted with an `= delete`
|
||||
* definition, or because the compiler was unable to auto-generate a
|
||||
* definition for it.
|
||||
*
|
||||
* Most implicitly deleted functions are omitted from the database.
|
||||
* `Class.implicitCopyConstructorDeleted` and
|
||||
* `Class.implicitCopyAssignmentOperatorDeleted` can be used to find
|
||||
* whether a class would have had those members implicitly deleted.
|
||||
*/
|
||||
predicate isDeleted() { function_deleted(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this function is explicitly defaulted with the `= default`
|
||||
* specifier.
|
||||
*/
|
||||
predicate isDefaulted() { function_defaulted(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this function is declared to be `constexpr`.
|
||||
*
|
||||
* Note that this does not hold if the function has been declared
|
||||
* `consteval`.
|
||||
*/
|
||||
predicate isDeclaredConstexpr() { this.hasSpecifier("declared_constexpr") }
|
||||
|
||||
/**
|
||||
* Holds if this function is `constexpr`. Normally, this holds if and
|
||||
* only if `isDeclaredConstexpr()` holds, but in some circumstances
|
||||
* they differ. For example, with
|
||||
* ```
|
||||
* int f(int i) { return 6; }
|
||||
* template <typename T> constexpr int g(T x) { return f(x); }
|
||||
* ```
|
||||
* `g<int>` is declared constexpr, but is not constexpr.
|
||||
*
|
||||
* Will also hold if this function is `consteval`.
|
||||
*/
|
||||
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
|
||||
|
||||
/**
|
||||
* Holds if this function is declared to be `consteval`.
|
||||
*/
|
||||
predicate isConsteval() { this.hasSpecifier("is_consteval") }
|
||||
|
||||
/**
|
||||
* Holds if this function is declared with `__attribute__((naked))` or
|
||||
* `__declspec(naked)`.
|
||||
*/
|
||||
predicate isNaked() { getAnAttribute().hasName("naked") }
|
||||
|
||||
/**
|
||||
* Holds if this function has a trailing return type.
|
||||
*
|
||||
* Note that this is true whether or not deduction took place. For example,
|
||||
* this holds for both `e` and `f`, but not `g` or `h`:
|
||||
* ```
|
||||
* auto e() -> int { return 0; }
|
||||
* auto f() -> auto { return 0; }
|
||||
* auto g() { return 0; }
|
||||
* int h() { return 0; }
|
||||
* ```
|
||||
*/
|
||||
predicate hasTrailingReturnType() { this.hasSpecifier("has_trailing_return_type") }
|
||||
|
||||
/** Gets the return type of this function. */
|
||||
Type getType() { function_return_type(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Gets the return type of this function after specifiers have been deeply
|
||||
* stripped and typedefs have been resolved.
|
||||
*/
|
||||
Type getUnspecifiedType() { result = getType().getUnspecifiedType() }
|
||||
|
||||
/**
|
||||
* Gets the nth parameter of this function. There is no result for the
|
||||
* implicit `this` parameter, and there is no `...` varargs pseudo-parameter.
|
||||
*/
|
||||
Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) }
|
||||
|
||||
/**
|
||||
* Gets a parameter of this function. There is no result for the implicit
|
||||
* `this` parameter, and there is no `...` varargs pseudo-parameter.
|
||||
*/
|
||||
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
|
||||
|
||||
/**
|
||||
* Gets an access of this function.
|
||||
*
|
||||
* To get calls to this function, use `getACallToThisFunction` instead.
|
||||
*/
|
||||
FunctionAccess getAnAccess() { result.getTarget() = this }
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of this function, _not_ including any
|
||||
* implicit `this` parameter or any `...` varargs pseudo-parameter.
|
||||
*/
|
||||
int getNumberOfParameters() { result = count(this.getAParameter()) }
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of this function, _including_ any implicit
|
||||
* `this` parameter but _not_ including any `...` varargs pseudo-parameter.
|
||||
*/
|
||||
int getEffectiveNumberOfParameters() {
|
||||
// This method is overridden in `MemberFunction`, where the result is
|
||||
// adjusted to account for the implicit `this` parameter.
|
||||
result = getNumberOfParameters()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string representing the parameters of this function.
|
||||
*
|
||||
* For example: for a function `int Foo(int p1, int p2)` this would
|
||||
* return `int p1, int p2`.
|
||||
*/
|
||||
string getParameterString() {
|
||||
result = concat(int i | | min(getParameter(i).getTypedName()), ", " order by i)
|
||||
}
|
||||
|
||||
/** Gets a call to this function. */
|
||||
FunctionCall getACallToThisFunction() { result.getTarget() = this }
|
||||
|
||||
/**
|
||||
* Gets a declaration entry corresponding to this declaration. The
|
||||
* relationship between `Declaration` and `DeclarationEntry` is explained
|
||||
* in `Declaration.qll`.
|
||||
*/
|
||||
override FunctionDeclarationEntry getADeclarationEntry() {
|
||||
if fun_decls(_, underlyingElement(this), _, _, _)
|
||||
then declEntry(result)
|
||||
else
|
||||
exists(Function f |
|
||||
this.isConstructedFrom(f) and
|
||||
fun_decls(unresolveElement(result), unresolveElement(f), _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate declEntry(FunctionDeclarationEntry fde) {
|
||||
fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and
|
||||
// If one .cpp file specializes a function, and another calls the
|
||||
// specialized function, then when extracting the second we only see an
|
||||
// instantiation, not the specialization. We Therefore need to ignore
|
||||
// any non-specialized declarations if there are any specialized ones.
|
||||
(this.isSpecialization() implies fde.isSpecialization())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location of a `FunctionDeclarationEntry` corresponding to this
|
||||
* declaration.
|
||||
*/
|
||||
override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() }
|
||||
|
||||
/** Holds if this Function is a Template specialization. */
|
||||
predicate isSpecialization() {
|
||||
exists(FunctionDeclarationEntry fde |
|
||||
fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and
|
||||
fde.isSpecialization()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declaration entry corresponding to this declaration that is a
|
||||
* definition, if any.
|
||||
*/
|
||||
override FunctionDeclarationEntry getDefinition() {
|
||||
result = getADeclarationEntry() and
|
||||
result.isDefinition()
|
||||
}
|
||||
|
||||
/** Gets the location of the definition, if any. */
|
||||
override Location getDefinitionLocation() {
|
||||
if exists(getDefinition())
|
||||
then result = getDefinition().getLocation()
|
||||
else exists(Function f | this.isConstructedFrom(f) and result = f.getDefinition().getLocation())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the preferred location of this declaration. (The location of the
|
||||
* definition, if possible.)
|
||||
*/
|
||||
override Location getLocation() {
|
||||
if exists(getDefinition())
|
||||
then result = this.getDefinitionLocation()
|
||||
else result = this.getADeclarationLocation()
|
||||
}
|
||||
|
||||
/** Gets a child declaration of this function. */
|
||||
Declaration getADeclaration() { result = this.getAParameter() }
|
||||
|
||||
/**
|
||||
* Gets the block that is the function body.
|
||||
*
|
||||
* For C++ functions whose body is a function try statement rather than a
|
||||
* block, this gives the block guarded by the try statement. See
|
||||
* `FunctionTryStmt` for further information.
|
||||
*/
|
||||
BlockStmt getBlock() { result.getParentScope() = this }
|
||||
|
||||
/** Holds if this function has an entry point. */
|
||||
predicate hasEntryPoint() { exists(getEntryPoint()) }
|
||||
|
||||
/**
|
||||
* Gets the first node in this function's control flow graph.
|
||||
*
|
||||
* For most functions, this first node will be the `BlockStmt` returned by
|
||||
* `getBlock`. However in C++, the first node can also be a
|
||||
* `FunctionTryStmt`.
|
||||
*/
|
||||
Stmt getEntryPoint() { function_entry_point(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Gets the metric class. `MetricFunction` has methods for computing
|
||||
* various metrics, such as "number of lines of code" and "number of
|
||||
* function calls".
|
||||
*/
|
||||
MetricFunction getMetrics() { result = this }
|
||||
|
||||
/** Holds if this function calls the function `f`. */
|
||||
predicate calls(Function f) { exists(Locatable l | this.calls(f, l)) }
|
||||
|
||||
/**
|
||||
* Holds if this function calls the function `f` in the `FunctionCall`
|
||||
* expression `l`.
|
||||
*/
|
||||
predicate calls(Function f, Locatable l) {
|
||||
exists(FunctionCall call |
|
||||
call.getEnclosingFunction() = this and call.getTarget() = f and call = l
|
||||
)
|
||||
or
|
||||
exists(DestructorCall call |
|
||||
call.getEnclosingFunction() = this and call.getTarget() = f and call = l
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this function accesses a function or variable or enumerator `a`. */
|
||||
predicate accesses(Declaration a) { exists(Locatable l | this.accesses(a, l)) }
|
||||
|
||||
/**
|
||||
* Holds if this function accesses a function or variable or enumerator `a`
|
||||
* in the `Access` expression `l`.
|
||||
*/
|
||||
predicate accesses(Declaration a, Locatable l) {
|
||||
exists(Access access |
|
||||
access.getEnclosingFunction() = this and
|
||||
a = access.getTarget() and
|
||||
access = l
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a variable that is written-to in this function. */
|
||||
Variable getAWrittenVariable() {
|
||||
exists(ConstructorFieldInit cfi |
|
||||
cfi.getEnclosingFunction() = this and result = cfi.getTarget()
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va |
|
||||
va = result.getAnAccess() and
|
||||
va.isUsedAsLValue() and
|
||||
va.getEnclosingFunction() = this
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class of which this function, called `memberName`, is a member.
|
||||
*
|
||||
* Prefer to use `getDeclaringType()` or `getName()` directly if you do not
|
||||
* need to reason about both.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Class getClassAndName(string memberName) {
|
||||
this.hasName(memberName) and
|
||||
this.getDeclaringType() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements `ControlFlowNode.getControlFlowScope`. The `Function` is
|
||||
* used to represent the exit node of the control flow graph, so it is
|
||||
* its own scope.
|
||||
*/
|
||||
override Function getControlFlowScope() { result = this }
|
||||
|
||||
/**
|
||||
* Implements `ControlFlowNode.getEnclosingStmt`. The `Function` is
|
||||
* used to represent the exit node of the control flow graph, so it
|
||||
* has no enclosing statement.
|
||||
*/
|
||||
override Stmt getEnclosingStmt() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this function has C linkage, as specified by one of its
|
||||
* declaration entries. For example: `extern "C" void foo();`.
|
||||
*/
|
||||
predicate hasCLinkage() { getADeclarationEntry().hasCLinkage() }
|
||||
|
||||
/**
|
||||
* Holds if this function is constructed from `f` as a result
|
||||
* of template instantiation. If so, it originates either from a template
|
||||
* function or from a function nested in a template class.
|
||||
*/
|
||||
predicate isConstructedFrom(Function f) {
|
||||
function_instantiation(underlyingElement(this), unresolveElement(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function is defined in several files. This is illegal in
|
||||
* C (though possible in some C++ compilers), and likely indicates that
|
||||
* several functions that are not linked together have been compiled. An
|
||||
* example would be a project with many 'main' functions.
|
||||
*/
|
||||
predicate isMultiplyDefined() { strictcount(getFile()) > 1 }
|
||||
|
||||
/** Holds if this function is a varargs function. */
|
||||
predicate isVarargs() { hasSpecifier("varargs") }
|
||||
|
||||
/** Gets a type that is specified to be thrown by the function. */
|
||||
Type getAThrownType() { result = getADeclarationEntry().getAThrownType() }
|
||||
|
||||
/**
|
||||
* Gets the `i`th type specified to be thrown by the function.
|
||||
*/
|
||||
Type getThrownType(int i) { result = getADeclarationEntry().getThrownType(i) }
|
||||
|
||||
/** Holds if the function has an exception specification. */
|
||||
predicate hasExceptionSpecification() { getADeclarationEntry().hasExceptionSpecification() }
|
||||
|
||||
/** Holds if this function has a `throw()` exception specification. */
|
||||
predicate isNoThrow() { getADeclarationEntry().isNoThrow() }
|
||||
|
||||
/** Holds if this function has a `noexcept` exception specification. */
|
||||
predicate isNoExcept() { getADeclarationEntry().isNoExcept() }
|
||||
|
||||
/**
|
||||
* Gets a function that overloads this one.
|
||||
*
|
||||
* Note: if _overrides_ are wanted rather than _overloads_ then
|
||||
* `MemberFunction::getAnOverridingFunction` should be used instead.
|
||||
*/
|
||||
Function getAnOverload() {
|
||||
(
|
||||
// If this function is declared in a class, only consider other
|
||||
// functions from the same class.
|
||||
exists(string name, Class declaringType |
|
||||
candGetAnOverloadMember(name, declaringType, this) and
|
||||
candGetAnOverloadMember(name, declaringType, result)
|
||||
)
|
||||
or
|
||||
// Conversely, if this function is not
|
||||
// declared in a class, only consider other functions not declared in a
|
||||
// class.
|
||||
exists(string name, Namespace namespace |
|
||||
candGetAnOverloadNonMember(name, namespace, this) and
|
||||
candGetAnOverloadNonMember(name, namespace, result)
|
||||
)
|
||||
) and
|
||||
result != this and
|
||||
// Instantiations and specializations don't participate in overload
|
||||
// resolution.
|
||||
not (
|
||||
this instanceof FunctionTemplateInstantiation or
|
||||
result instanceof FunctionTemplateInstantiation
|
||||
) and
|
||||
not (
|
||||
this instanceof FunctionTemplateSpecialization or
|
||||
result instanceof FunctionTemplateSpecialization
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a link target which compiled or referenced this function. */
|
||||
LinkTarget getALinkTarget() { this = result.getAFunction() }
|
||||
|
||||
/**
|
||||
* Holds if this function is side-effect free (conservative
|
||||
* approximation).
|
||||
*/
|
||||
predicate isSideEffectFree() { not this.mayHaveSideEffects() }
|
||||
|
||||
/**
|
||||
* Holds if this function may have side-effects; if in doubt, we assume it
|
||||
* may.
|
||||
*/
|
||||
predicate mayHaveSideEffects() {
|
||||
// If we cannot see the definition then we assume that it may have
|
||||
// side-effects.
|
||||
if exists(this.getEntryPoint())
|
||||
then
|
||||
// If it might be globally impure (we don't care about it modifying
|
||||
// temporaries) then it may have side-effects.
|
||||
this.getEntryPoint().mayBeGloballyImpure()
|
||||
or
|
||||
// Constructor initializers are separate from the entry point ...
|
||||
this.(Constructor).getAnInitializer().mayBeGloballyImpure()
|
||||
or
|
||||
// ... and likewise for destructors.
|
||||
this.(Destructor).getADestruction().mayBeGloballyImpure()
|
||||
else
|
||||
// Unless it's a function that we know is side-effect free, it may
|
||||
// have side-effects.
|
||||
not this.hasGlobalOrStdName([
|
||||
"strcmp", "wcscmp", "_mbscmp", "strlen", "wcslen", "_mbslen", "_mbslen_l", "_mbstrlen",
|
||||
"_mbstrlen_l", "strnlen", "strnlen_s", "wcsnlen", "wcsnlen_s", "_mbsnlen", "_mbsnlen_l",
|
||||
"_mbstrnlen", "_mbstrnlen_l", "strncmp", "wcsncmp", "_mbsncmp", "_mbsncmp_l", "strchr",
|
||||
"memchr", "wmemchr", "memcmp", "wmemcmp", "_memicmp", "_memicmp_l", "feof", "isdigit",
|
||||
"isxdigit", "abs", "fabs", "labs", "floor", "ceil", "atoi", "atol", "atoll", "atof"
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest enclosing AccessHolder.
|
||||
*/
|
||||
override AccessHolder getEnclosingAccessHolder() { result = this.getDeclaringType() }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate candGetAnOverloadMember(string name, Class declaringType, Function f) {
|
||||
f.getName() = name and
|
||||
f.getDeclaringType() = declaringType
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate candGetAnOverloadNonMember(string name, Namespace namespace, Function f) {
|
||||
f.getName() = name and
|
||||
f.getNamespace() = namespace and
|
||||
not exists(f.getDeclaringType())
|
||||
}
|
||||
|
||||
/**
|
||||
* A particular declaration or definition of a C/C++ function. For example the
|
||||
* declaration and definition of `MyFunction` in the following code are each a
|
||||
* `FunctionDeclarationEntry`:
|
||||
* ```
|
||||
* void MyFunction();
|
||||
*
|
||||
* void MyFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
/** Gets the function which is being declared or defined. */
|
||||
override Function getDeclaration() { result = getFunction() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" }
|
||||
|
||||
/** Gets the function which is being declared or defined. */
|
||||
Function getFunction() { fun_decls(underlyingElement(this), unresolveElement(result), _, _, _) }
|
||||
|
||||
/** Gets the name of the function. */
|
||||
override string getName() { fun_decls(underlyingElement(this), _, _, result, _) }
|
||||
|
||||
/**
|
||||
* Gets the return type of the function which is being declared or
|
||||
* defined.
|
||||
*/
|
||||
override Type getType() { fun_decls(underlyingElement(this), _, unresolveElement(result), _, _) }
|
||||
|
||||
/** Gets the location of this declaration entry. */
|
||||
override Location getLocation() { fun_decls(underlyingElement(this), _, _, _, result) }
|
||||
|
||||
/** Gets a specifier associated with this declaration entry. */
|
||||
override string getASpecifier() { fun_decl_specifiers(underlyingElement(this), result) }
|
||||
|
||||
/**
|
||||
* Implements `Element.getEnclosingElement`. A function declaration does
|
||||
* not have an enclosing element.
|
||||
*/
|
||||
override Element getEnclosingElement() { none() }
|
||||
|
||||
/**
|
||||
* Gets the typedef type (if any) used for this function declaration. As
|
||||
* an example, the typedef type in the declaration of function foo in the
|
||||
* following is Foo:
|
||||
*
|
||||
* typedef int Foo();
|
||||
* static Foo foo;
|
||||
*/
|
||||
TypedefType getTypedefType() {
|
||||
fun_decl_typedef_type(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cyclomatic complexity of this function:
|
||||
*
|
||||
* The number of branching statements (if, while, do, for, switch,
|
||||
* case, catch) plus the number of branching expressions (`?`, `&&`,
|
||||
* `||`) plus one.
|
||||
*/
|
||||
int getCyclomaticComplexity() { result = 1 + cyclomaticComplexityBranches(getBlock()) }
|
||||
|
||||
/**
|
||||
* If this is a function definition, get the block containing the
|
||||
* function body.
|
||||
*/
|
||||
BlockStmt getBlock() {
|
||||
this.isDefinition() and
|
||||
result = getFunction().getBlock() and
|
||||
result.getFile() = this.getFile()
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a function definition, get the number of lines of code
|
||||
* associated with it.
|
||||
*/
|
||||
pragma[noopt]
|
||||
int getNumberOfLines() {
|
||||
exists(BlockStmt b, Location l, int start, int end, int diff | b = getBlock() |
|
||||
l = b.getLocation() and
|
||||
start = l.getStartLine() and
|
||||
end = l.getEndLine() and
|
||||
diff = end - start and
|
||||
result = diff + 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declaration entry for a parameter of this function
|
||||
* declaration.
|
||||
*/
|
||||
ParameterDeclarationEntry getAParameterDeclarationEntry() {
|
||||
result = getParameterDeclarationEntry(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declaration entry for the nth parameter of this function
|
||||
* declaration.
|
||||
*/
|
||||
ParameterDeclarationEntry getParameterDeclarationEntry(int n) {
|
||||
param_decl_bind(unresolveElement(result), n, underlyingElement(this))
|
||||
}
|
||||
|
||||
/** Gets the number of parameters of this function declaration. */
|
||||
int getNumberOfParameters() { result = count(this.getAParameterDeclarationEntry()) }
|
||||
|
||||
/**
|
||||
* Gets a string representing the parameters of this function declaration.
|
||||
*
|
||||
* For example: for a function 'int Foo(int p1, int p2)' this would
|
||||
* return 'int p1, int p2'.
|
||||
*/
|
||||
string getParameterString() {
|
||||
result = concat(int i | | min(getParameterDeclarationEntry(i).getTypedName()), ", " order by i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this declaration entry specifies C linkage:
|
||||
*
|
||||
* `extern "C" void foo();`
|
||||
*/
|
||||
predicate hasCLinkage() { getASpecifier() = "c_linkage" }
|
||||
|
||||
/** Holds if this declaration entry has a void parameter list. */
|
||||
predicate hasVoidParamList() { getASpecifier() = "void_param_list" }
|
||||
|
||||
/** Holds if this declaration is also a definition of its function. */
|
||||
override predicate isDefinition() { fun_def(underlyingElement(this)) }
|
||||
|
||||
/** Holds if this declaration is a Template specialization. */
|
||||
predicate isSpecialization() { fun_specialized(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this declaration is an implicit function declaration, that is,
|
||||
* where a function is used before it is declared (under older C standards).
|
||||
*/
|
||||
predicate isImplicit() { fun_implicit(underlyingElement(this)) }
|
||||
|
||||
/** Gets a type that is specified to be thrown by the declared function. */
|
||||
Type getAThrownType() { result = getThrownType(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th type specified to be thrown by the declared function
|
||||
* (where `i` is indexed from 0). For example, if a function is declared
|
||||
* to `throw(int,float)`, then the thrown type with index 0 would be
|
||||
* `int`, and that with index 1 would be `float`.
|
||||
*/
|
||||
Type getThrownType(int i) {
|
||||
fun_decl_throws(underlyingElement(this), i, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* If this declaration has a noexcept-specification [N4140 15.4], then
|
||||
* this predicate returns the argument to `noexcept` if one was given.
|
||||
*/
|
||||
Expr getNoExceptExpr() { fun_decl_noexcept(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Holds if the declared function has an exception specification [N4140
|
||||
* 15.4].
|
||||
*/
|
||||
predicate hasExceptionSpecification() {
|
||||
fun_decl_throws(underlyingElement(this), _, _) or
|
||||
fun_decl_noexcept(underlyingElement(this), _) or
|
||||
isNoThrow() or
|
||||
isNoExcept()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the declared function has a `throw()` exception specification.
|
||||
*/
|
||||
predicate isNoThrow() { fun_decl_empty_throws(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if the declared function has an empty `noexcept` exception
|
||||
* specification.
|
||||
*/
|
||||
predicate isNoExcept() { fun_decl_empty_noexcept(underlyingElement(this)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ non-member function (a function that is not a member of any
|
||||
* class). For example, in the following code, `MyFunction` is a
|
||||
* `TopLevelFunction` but `MyMemberFunction` is not:
|
||||
* ```
|
||||
* void MyFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
*
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void MyMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class TopLevelFunction extends Function {
|
||||
TopLevelFunction() { not this.isMember() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TopLevelFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ user-defined operator [N4140 13.5].
|
||||
*/
|
||||
class Operator extends Function {
|
||||
Operator() { functions(underlyingElement(this), _, 5) }
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
not this instanceof MemberFunction and result = "Operator"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ function which has a non-empty template argument list. For example
|
||||
* the function `myTemplateFunction` in the following code:
|
||||
* ```
|
||||
* template<class T>
|
||||
* void myTemplateFunction(T t) {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This comprises function declarations which are immediately preceded by
|
||||
* `template <...>`, where the "..." part is not empty, and therefore it does
|
||||
* not include:
|
||||
*
|
||||
* 1. Full specializations of template functions, as they have an empty
|
||||
* template argument list.
|
||||
* 2. Instantiations of template functions, as they don't have an
|
||||
* explicit template argument list.
|
||||
* 3. Member functions of template classes - unless they have their own
|
||||
* (non-empty) template argument list.
|
||||
*/
|
||||
class TemplateFunction extends Function {
|
||||
TemplateFunction() {
|
||||
is_function_template(underlyingElement(this)) and exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TemplateFunction" }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated instantiation of this function template.
|
||||
*/
|
||||
Function getAnInstantiation() {
|
||||
result.isConstructedFrom(this) and
|
||||
not result.isSpecialization()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a full specialization of this function template.
|
||||
*
|
||||
* Note that unlike classes, functions overload rather than specialize
|
||||
* partially. Therefore this does not include things which "look like"
|
||||
* partial specializations, nor does it include full specializations of
|
||||
* such things -- see FunctionTemplateSpecialization for further details.
|
||||
*/
|
||||
FunctionTemplateSpecialization getASpecialization() { result.getPrimaryTemplate() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that is an instantiation of a template. For example
|
||||
* the instantiation `myTemplateFunction<int>` in the following code:
|
||||
* ```
|
||||
* template<class T>
|
||||
* void myTemplateFunction(T t) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* void caller(int i) {
|
||||
* myTemplateFunction<int>(i);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class FunctionTemplateInstantiation extends Function {
|
||||
TemplateFunction tf;
|
||||
|
||||
FunctionTemplateInstantiation() { tf.getAnInstantiation() = this }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionTemplateInstantiation" }
|
||||
|
||||
/**
|
||||
* Gets the function template from which this instantiation was instantiated.
|
||||
*
|
||||
* Example: For `int const& std::min<int>(int const&, int const&)`, returns `T const& min<T>(T const&, T const&)`.
|
||||
*/
|
||||
TemplateFunction getTemplate() { result = tf }
|
||||
}
|
||||
|
||||
/**
|
||||
* An explicit specialization of a C++ function template. For example the
|
||||
* function `myTemplateFunction<int>` in the following code:
|
||||
* ```
|
||||
* template<class T>
|
||||
* void myTemplateFunction(T t) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* template<>
|
||||
* void myTemplateFunction<int>(int i) {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note that unlike classes, functions overload rather than specialize
|
||||
* partially. Therefore this only includes the last two of the following
|
||||
* four definitions, and in particular does not include the second one:
|
||||
*
|
||||
* ```
|
||||
* template <typename T> void f(T) {...}
|
||||
* template <typename T> void f(T*) {...}
|
||||
* template <> void f<int>(int *) {...}
|
||||
* template <> void f<int*>(int *) {...}
|
||||
* ```
|
||||
*
|
||||
* Furthermore, this does not include compiler-generated instantiations of
|
||||
* function templates.
|
||||
*
|
||||
* For further reference on function template specializations, see:
|
||||
* http://www.gotw.ca/publications/mill17.htm
|
||||
*/
|
||||
class FunctionTemplateSpecialization extends Function {
|
||||
FunctionTemplateSpecialization() { this.isSpecialization() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionTemplateSpecialization" }
|
||||
|
||||
/**
|
||||
* Gets the primary template for the specialization (the function template
|
||||
* this specializes).
|
||||
*/
|
||||
TemplateFunction getPrimaryTemplate() { this.isConstructedFrom(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A GCC built-in function. For example: `__builtin___memcpy_chk`.
|
||||
*/
|
||||
class BuiltInFunction extends Function {
|
||||
BuiltInFunction() { functions(underlyingElement(this), _, 6) }
|
||||
|
||||
/** Gets a dummy location for the built-in function. */
|
||||
override Location getLocation() {
|
||||
suppressUnusedThis(this) and
|
||||
result instanceof UnknownDefaultLocation
|
||||
}
|
||||
}
|
||||
|
||||
private predicate suppressUnusedThis(Function f) { any() }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Holds if `v` is a member variable of `c` that looks like it might be variable sized
|
||||
* in practice. For example:
|
||||
* ```
|
||||
* struct myStruct { // c
|
||||
* int amount;
|
||||
* char data[1]; // v
|
||||
* };
|
||||
* ```
|
||||
* This requires that `v` is an array of size 0 or 1.
|
||||
*/
|
||||
predicate memberMayBeVarSize(Class c, MemberVariable v) {
|
||||
c = v.getDeclaringType() and
|
||||
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size in bytes of the buffer pointed to by an expression (if this can be determined).
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
int getBufferSize(Expr bufferExpr, Element why) {
|
||||
exists(Variable bufferVar | bufferVar = bufferExpr.(VariableAccess).getTarget() |
|
||||
// buffer is a fixed size array
|
||||
result = bufferVar.getUnspecifiedType().(ArrayType).getSize() and
|
||||
why = bufferVar and
|
||||
not memberMayBeVarSize(_, bufferVar) and
|
||||
not result = 0 // zero sized arrays are likely to have special usage, for example
|
||||
or
|
||||
// behaving a bit like a 'union' overlapping other fields.
|
||||
// buffer is an initialized array
|
||||
// e.g. int buffer[] = {1, 2, 3};
|
||||
why = bufferVar.getInitializer().getExpr() and
|
||||
(
|
||||
why instanceof AggregateLiteral or
|
||||
why instanceof StringLiteral
|
||||
) and
|
||||
result = why.(Expr).getType().(ArrayType).getSize() and
|
||||
not exists(bufferVar.getUnspecifiedType().(ArrayType).getSize())
|
||||
or
|
||||
exists(Class parentClass, VariableAccess parentPtr |
|
||||
// buffer is the parentPtr->bufferVar of a 'variable size struct'
|
||||
memberMayBeVarSize(parentClass, bufferVar) and
|
||||
why = bufferVar and
|
||||
parentPtr = bufferExpr.(VariableAccess).getQualifier() and
|
||||
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
|
||||
result = getBufferSize(parentPtr, _) + bufferVar.getType().getSize() - parentClass.getSize()
|
||||
)
|
||||
)
|
||||
or
|
||||
// buffer is a fixed size dynamic allocation
|
||||
result = bufferExpr.(AllocationExpr).getSizeBytes() and
|
||||
why = bufferExpr
|
||||
or
|
||||
exists(DataFlow::ExprNode bufferExprNode |
|
||||
// dataflow (all sources must be the same size)
|
||||
bufferExprNode = DataFlow::exprNode(bufferExpr) and
|
||||
result =
|
||||
unique(Expr def |
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
|
||||
|
|
||||
getBufferSize(def, _)
|
||||
) and
|
||||
// find reason
|
||||
exists(Expr def | DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode) |
|
||||
why = def or
|
||||
exists(getBufferSize(def, why))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Type bufferType |
|
||||
// buffer is the address of a variable
|
||||
why = bufferExpr.(AddressOfExpr).getAddressable() and
|
||||
bufferType = why.(Variable).getType() and
|
||||
result = bufferType.getSize() and
|
||||
not bufferType instanceof ReferenceType and
|
||||
not any(Union u).getAMemberVariable() = why
|
||||
)
|
||||
or
|
||||
exists(Union bufferType |
|
||||
// buffer is the address of a union member; in this case, we
|
||||
// take the size of the union itself rather the union member, since
|
||||
// it's usually OK to access that amount (e.g. clearing with memset).
|
||||
why = bufferExpr.(AddressOfExpr).getAddressable() and
|
||||
bufferType.getAMemberVariable() = why and
|
||||
result = bufferType.getSize()
|
||||
)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,841 +0,0 @@
|
||||
/**
|
||||
* Provides C++-specific definitions for use in the data flow library.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
private import semmle.code.cpp.dataflow.internal.AddressFlow
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) or
|
||||
TPartialDefinitionNode(PartialDefinition pd) or
|
||||
TPreObjectInitializerNode(Expr e) {
|
||||
e instanceof ConstructorCall
|
||||
or
|
||||
e instanceof ClassAggregateLiteral
|
||||
} or
|
||||
TExplicitParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
|
||||
TInstanceParameterNode(MemberFunction f) { exists(f.getBlock()) and not f.isStatic() } or
|
||||
TPreConstructorInitThis(ConstructorFieldInit cfi) or
|
||||
TPostConstructorInitThis(ConstructorFieldInit cfi) or
|
||||
TInnerPartialDefinitionNode(Expr e) {
|
||||
exists(PartialDefinition def, Expr outer |
|
||||
def.definesExpressions(e, outer) and
|
||||
// This condition ensures that we don't get two post-update nodes sharing
|
||||
// the same pre-update node.
|
||||
e != outer
|
||||
)
|
||||
} or
|
||||
TUninitializedNode(LocalVariable v) { not v.hasInitializer() } or
|
||||
TRefParameterFinalValueNode(Parameter p) { exists(FlowVar var | var.reachesRefParameter(p)) }
|
||||
|
||||
/**
|
||||
* A node in a data flow graph.
|
||||
*
|
||||
* A node can be either an expression, a parameter, or an uninitialized local
|
||||
* variable. Such nodes are created with `DataFlow::exprNode`,
|
||||
* `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively.
|
||||
*/
|
||||
class Node extends TNode {
|
||||
/** Gets the function to which this node belongs. */
|
||||
Function getFunction() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getFunction`.
|
||||
*/
|
||||
final Function getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. This predicate
|
||||
* only has a result on nodes that represent the value of evaluating the
|
||||
* expression. For data flowing _out of_ an expression, like when an
|
||||
* argument is passed by reference, use `asDefiningArgument` instead of
|
||||
* `asExpr`.
|
||||
*/
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
|
||||
|
||||
/**
|
||||
* Gets the argument that defines this `DefinitionByReferenceNode`, if any.
|
||||
* This predicate should be used instead of `asExpr` when referring to the
|
||||
* value of a reference argument _after_ the call has returned. For example,
|
||||
* in `f(&x)`, this predicate will have `&x` as its result for the `Node`
|
||||
* that represents the new value of `x`.
|
||||
*/
|
||||
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
|
||||
|
||||
/**
|
||||
* Gets the expression that is partially defined by this node, if any.
|
||||
*
|
||||
* Partial definitions are created for field stores (`x.y = taint();` is a partial
|
||||
* definition of `x`), and for calls that may change the value of an object (so
|
||||
* `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is
|
||||
* a partial definition of `&x`).
|
||||
*/
|
||||
Expr asPartialDefinition() {
|
||||
this.(PartialDefinitionNode).getPartialDefinition().definesExpressions(_, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the uninitialized local variable corresponding to this node, if
|
||||
* any.
|
||||
*/
|
||||
LocalVariable asUninitialized() { result = this.(UninitializedNode).getLocalVariable() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() } // overridden by subclasses
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
*/
|
||||
Type getTypeBound() { result = getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr expr;
|
||||
|
||||
ExprNode() { this = TExprNode(expr) }
|
||||
|
||||
override Function getFunction() { result = expr.getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = expr.getType() }
|
||||
|
||||
override string toString() { result = expr.toString() }
|
||||
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
|
||||
/** Gets the expression corresponding to this node. */
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
abstract class ParameterNode extends Node, TNode {
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
abstract predicate isParameterOf(Function f, int i);
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class ExplicitParameterNode extends ParameterNode, TExplicitParameterNode {
|
||||
Parameter param;
|
||||
|
||||
ExplicitParameterNode() { this = TExplicitParameterNode(param) }
|
||||
|
||||
override Function getFunction() { result = param.getFunction() }
|
||||
|
||||
override Type getType() { result = param.getType() }
|
||||
|
||||
override string toString() { result = param.toString() }
|
||||
|
||||
override Location getLocation() { result = param.getLocation() }
|
||||
|
||||
/** Gets the parameter corresponding to this node. */
|
||||
Parameter getParameter() { result = param }
|
||||
|
||||
override predicate isParameterOf(Function f, int i) { f.getParameter(i) = param }
|
||||
}
|
||||
|
||||
class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode {
|
||||
MemberFunction f;
|
||||
|
||||
ImplicitParameterNode() { this = TInstanceParameterNode(f) }
|
||||
|
||||
override Function getFunction() { result = f }
|
||||
|
||||
override Type getType() { result = f.getDeclaringType() }
|
||||
|
||||
override string toString() { result = "this" }
|
||||
|
||||
override Location getLocation() { result = f.getLocation() }
|
||||
|
||||
override predicate isParameterOf(Function fun, int i) { f = fun and i = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A node that represents the value of a variable after a function call that
|
||||
* may have changed the variable because it's passed by reference or because an
|
||||
* iterator for it was passed by value or by reference.
|
||||
*/
|
||||
class DefinitionByReferenceOrIteratorNode extends PartialDefinitionNode {
|
||||
Expr inner;
|
||||
Expr argument;
|
||||
|
||||
DefinitionByReferenceOrIteratorNode() {
|
||||
this.getPartialDefinition().definesExpressions(inner, argument) and
|
||||
(
|
||||
this.getPartialDefinition() instanceof DefinitionByReference
|
||||
or
|
||||
this.getPartialDefinition() instanceof DefinitionByIterator
|
||||
)
|
||||
}
|
||||
|
||||
override Function getFunction() { result = inner.getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = inner.getType() }
|
||||
|
||||
override Location getLocation() { result = argument.getLocation() }
|
||||
|
||||
override ExprNode getPreUpdateNode() { result.getExpr() = argument }
|
||||
|
||||
/** Gets the argument corresponding to this node. */
|
||||
Expr getArgument() { result = argument }
|
||||
|
||||
/** Gets the parameter through which this value is assigned. */
|
||||
Parameter getParameter() {
|
||||
exists(FunctionCall call, int i |
|
||||
argument = call.getArgument(i) and
|
||||
result = call.getTarget().getParameter(i)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that represents the value of a variable after a function call that
|
||||
* may have changed the variable because it's passed by reference.
|
||||
*
|
||||
* A typical example would be a call `f(&x)`. Firstly, there will be flow into
|
||||
* `x` from previous definitions of `x`. Secondly, there will be a
|
||||
* `DefinitionByReferenceNode` to represent the value of `x` after the call has
|
||||
* returned. This node will have its `getArgument()` equal to `&x`.
|
||||
*/
|
||||
class DefinitionByReferenceNode extends DefinitionByReferenceOrIteratorNode {
|
||||
override VariablePartialDefinition pd;
|
||||
|
||||
override string toString() { result = "ref arg " + argument.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of an uninitialized local variable, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class UninitializedNode extends Node, TUninitializedNode {
|
||||
LocalVariable v;
|
||||
|
||||
UninitializedNode() { this = TUninitializedNode(v) }
|
||||
|
||||
override Function getFunction() { result = v.getFunction() }
|
||||
|
||||
override Type getType() { result = v.getType() }
|
||||
|
||||
override string toString() { result = v.toString() }
|
||||
|
||||
override Location getLocation() { result = v.getLocation() }
|
||||
|
||||
/** Gets the uninitialized local variable corresponding to this node. */
|
||||
LocalVariable getLocalVariable() { result = v }
|
||||
}
|
||||
|
||||
/** INTERNAL: do not use. The final value of a non-const ref parameter. */
|
||||
class RefParameterFinalValueNode extends Node, TRefParameterFinalValueNode {
|
||||
Parameter p;
|
||||
|
||||
RefParameterFinalValueNode() { this = TRefParameterFinalValueNode(p) }
|
||||
|
||||
override Function getFunction() { result = p.getFunction() }
|
||||
|
||||
override Type getType() { result = p.getType() }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
|
||||
override Location getLocation() { result = p.getLocation() }
|
||||
|
||||
Parameter getParameter() { result = p }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
* changed its state.
|
||||
*
|
||||
* This can be either the argument to a callable after the callable returns
|
||||
* (which might have mutated the argument), or the qualifier of a field after
|
||||
* an update to the field.
|
||||
*
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update with the exception of `ClassInstanceExpr`,
|
||||
* which represents the value after the constructor has run.
|
||||
*/
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
abstract Node getPreUpdateNode();
|
||||
|
||||
override Function getFunction() { result = getPreUpdateNode().getFunction() }
|
||||
|
||||
override Type getType() { result = getPreUpdateNode().getType() }
|
||||
|
||||
override Location getLocation() { result = getPreUpdateNode().getLocation() }
|
||||
}
|
||||
|
||||
abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
|
||||
PartialDefinition pd;
|
||||
|
||||
PartialDefinitionNode() { this = TPartialDefinitionNode(pd) }
|
||||
|
||||
override Location getLocation() { result = pd.getActualLocation() }
|
||||
|
||||
PartialDefinition getPartialDefinition() { result = pd }
|
||||
|
||||
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
|
||||
}
|
||||
|
||||
private class VariablePartialDefinitionNode extends PartialDefinitionNode {
|
||||
override VariablePartialDefinition pd;
|
||||
|
||||
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A synthetic data flow node used for flow into a collection when an iterator
|
||||
* write occurs in a callee.
|
||||
*/
|
||||
private class IteratorPartialDefinitionNode extends PartialDefinitionNode {
|
||||
override IteratorPartialDefinition pd;
|
||||
|
||||
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A post-update node on the `e->f` in `f(&e->f)` (and other forms).
|
||||
*/
|
||||
private class InnerPartialDefinitionNode extends TInnerPartialDefinitionNode, PostUpdateNode {
|
||||
Expr e;
|
||||
|
||||
InnerPartialDefinitionNode() { this = TInnerPartialDefinitionNode(e) }
|
||||
|
||||
override ExprNode getPreUpdateNode() { result.getExpr() = e }
|
||||
|
||||
override Function getFunction() { result = e.getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = e.getType() }
|
||||
|
||||
override string toString() { result = e.toString() + " [inner post update]" }
|
||||
|
||||
override Location getLocation() { result = e.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the temporary value of an object that was just
|
||||
* constructed by a constructor call or an aggregate initializer. This is only
|
||||
* for objects, not for pointers to objects.
|
||||
*
|
||||
* These expressions are their own post-update nodes but instead have synthetic
|
||||
* pre-update nodes.
|
||||
*/
|
||||
private class ObjectInitializerNode extends PostUpdateNode, TExprNode {
|
||||
PreObjectInitializerNode pre;
|
||||
|
||||
ObjectInitializerNode() {
|
||||
// If a `Node` is associated with a `PreObjectInitializerNode`, then it's
|
||||
// an `ObjectInitializerNode`.
|
||||
pre.getExpr() = this.asExpr()
|
||||
}
|
||||
|
||||
override PreObjectInitializerNode getPreUpdateNode() { result = pre }
|
||||
// No override of `toString` since these nodes already have a `toString` from
|
||||
// their overlap with `ExprNode`.
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A synthetic data-flow node that plays the role of a temporary object that
|
||||
* has not yet been initialized.
|
||||
*/
|
||||
class PreObjectInitializerNode extends Node, TPreObjectInitializerNode {
|
||||
Expr getExpr() { this = TPreObjectInitializerNode(result) }
|
||||
|
||||
override Function getFunction() { result = getExpr().getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = getExpr().getType() }
|
||||
|
||||
override Location getLocation() { result = getExpr().getLocation() }
|
||||
|
||||
override string toString() { result = getExpr().toString() + " [pre init]" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A synthetic data-flow node that plays the role of the post-update `this`
|
||||
* pointer in a `ConstructorFieldInit`. For example, the `x(1)` in
|
||||
* `C() : x(1) { }` is roughly equivalent to `this.x = 1`, and this node is
|
||||
* equivalent to the `this` _after_ the field has been assigned.
|
||||
*/
|
||||
private class PostConstructorInitThis extends PostUpdateNode, TPostConstructorInitThis {
|
||||
override PreConstructorInitThis getPreUpdateNode() {
|
||||
this = TPostConstructorInitThis(result.getConstructorFieldInit())
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = getPreUpdateNode().getConstructorFieldInit().toString() + " [post-this]"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A synthetic data-flow node that plays the role of the pre-update `this`
|
||||
* pointer in a `ConstructorFieldInit`. For example, the `x(1)` in
|
||||
* `C() : x(1) { }` is roughly equivalent to `this.x = 1`, and this node is
|
||||
* equivalent to the `this` _before_ the field has been assigned.
|
||||
*/
|
||||
class PreConstructorInitThis extends Node, TPreConstructorInitThis {
|
||||
ConstructorFieldInit getConstructorFieldInit() { this = TPreConstructorInitThis(result) }
|
||||
|
||||
override Constructor getFunction() { result = getConstructorFieldInit().getEnclosingFunction() }
|
||||
|
||||
override PointerType getType() {
|
||||
result.getBaseType() = getConstructorFieldInit().getEnclosingFunction().getDeclaringType()
|
||||
}
|
||||
|
||||
override Location getLocation() { result = getConstructorFieldInit().getLocation() }
|
||||
|
||||
override string toString() { result = getConstructorFieldInit().toString() + " [pre-this]" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of evaluating `e`. For data
|
||||
* flowing _out of_ an expression, like when an argument is passed by
|
||||
* reference, use `definitionByReferenceNodeFromArgument` instead.
|
||||
*/
|
||||
ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of `p` at function entry.
|
||||
*/
|
||||
ParameterNode parameterNode(Parameter p) { result.(ExplicitParameterNode).getParameter() = p }
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to a definition by reference of the variable
|
||||
* that is passed as `argument` of a call.
|
||||
*/
|
||||
DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) {
|
||||
result.getArgument() = argument
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of an uninitialized local
|
||||
* variable `v`.
|
||||
*/
|
||||
UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable() = v }
|
||||
|
||||
private module ThisFlow {
|
||||
/**
|
||||
* Gets the 0-based index of `thisNode` in `b`, where `thisNode` is an access
|
||||
* to `this` that may or may not have an associated `PostUpdateNode`. To make
|
||||
* room for synthetic nodes that access `this`, the index may not correspond
|
||||
* to an actual `ControlFlowNode`.
|
||||
*/
|
||||
private int basicBlockThisIndex(BasicBlock b, Node thisNode) {
|
||||
// The implicit `this` parameter node is given a very negative offset to
|
||||
// make space for any `ConstructorFieldInit`s there may be between it and
|
||||
// the block contents.
|
||||
thisNode.(ImplicitParameterNode).getFunction().getBlock() = b and
|
||||
result = -2147483648
|
||||
or
|
||||
// Place the synthetic `this` node for a `ConstructorFieldInit` at a
|
||||
// negative offset in the first basic block, between the
|
||||
// `ImplicitParameterNode` and the first statement.
|
||||
exists(Constructor constructor, int i |
|
||||
thisNode.(PreConstructorInitThis).getConstructorFieldInit() = constructor.getInitializer(i) and
|
||||
result = -2147483648 + 1 + i and
|
||||
b = thisNode.getFunction().getBlock()
|
||||
)
|
||||
or
|
||||
b.getNode(result) = thisNode.asExpr().(ThisExpr)
|
||||
}
|
||||
|
||||
private int thisRank(BasicBlock b, Node thisNode) {
|
||||
thisNode = rank[result](Node n, int i | i = basicBlockThisIndex(b, n) | n order by i)
|
||||
}
|
||||
|
||||
private int lastThisRank(BasicBlock b) { result = max(thisRank(b, _)) }
|
||||
|
||||
private predicate thisAccessBlockReaches(BasicBlock b1, BasicBlock b2) {
|
||||
exists(basicBlockThisIndex(b1, _)) and b2 = b1.getASuccessor()
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
thisAccessBlockReaches(b1, mid) and
|
||||
b2 = mid.getASuccessor() and
|
||||
not exists(basicBlockThisIndex(mid, _))
|
||||
)
|
||||
}
|
||||
|
||||
predicate adjacentThisRefs(Node n1, Node n2) {
|
||||
exists(BasicBlock b | thisRank(b, n1) + 1 = thisRank(b, n2))
|
||||
or
|
||||
exists(BasicBlock b1, BasicBlock b2 |
|
||||
lastThisRank(b1) = thisRank(b1, n1) and
|
||||
thisAccessBlockReaches(b1, b2) and
|
||||
thisRank(b2, n2) = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Field flow is not strictly a "step" but covers the whole function
|
||||
// transitively. There's no way to get a step-like relation out of the global
|
||||
// data flow library, so we just have to accept some big steps here.
|
||||
FieldFlow::fieldFlow(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* This is the local flow predicate that's used as a building block in global
|
||||
* data flow. It may have less flow than the `localFlowStep` predicate.
|
||||
*/
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Expr -> Expr
|
||||
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
|
||||
or
|
||||
// Assignment -> LValue post-update node
|
||||
//
|
||||
// This is used for assignments whose left-hand side is not a variable
|
||||
// assignment or a storeStep but is still modeled by other means. It could be
|
||||
// a call to `operator*` or `operator[]` where taint should flow to the
|
||||
// post-update node of the qualifier.
|
||||
exists(AssignExpr assign |
|
||||
nodeFrom.asExpr() = assign and
|
||||
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
|
||||
)
|
||||
or
|
||||
// Node -> FlowVar -> VariableAccess
|
||||
exists(FlowVar var |
|
||||
(
|
||||
exprToVarStep(nodeFrom.asExpr(), var)
|
||||
or
|
||||
varSourceBaseCase(var, nodeFrom.asParameter())
|
||||
or
|
||||
varSourceBaseCase(var, nodeFrom.asUninitialized())
|
||||
or
|
||||
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
|
||||
) and
|
||||
varToNodeStep(var, nodeTo)
|
||||
)
|
||||
or
|
||||
// Expr -> DefinitionByReferenceNode
|
||||
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
|
||||
or
|
||||
// `this` -> adjacent-`this`
|
||||
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
|
||||
or
|
||||
// post-update-`this` -> following-`this`-ref
|
||||
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
|
||||
or
|
||||
// In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`,
|
||||
// from which there is field flow to `x` via reverse read.
|
||||
exists(PartialDefinition def, Expr inner, Expr outer |
|
||||
def.definesExpressions(inner, outer) and
|
||||
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
|
||||
outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
|
||||
)
|
||||
or
|
||||
// Reverse flow: data that flows from the post-update node of a reference
|
||||
// returned by a function call, back into the qualifier of that function.
|
||||
// This allows data to flow 'in' through references returned by a modeled
|
||||
// function such as `operator[]`.
|
||||
exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
|
||||
call.getTarget() = f and
|
||||
inModel.isReturnValueDeref() and
|
||||
outModel.isQualifierObject() and
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and
|
||||
nodeTo.asDefiningArgument() = call.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
|
||||
|
||||
/**
|
||||
* Holds if the initial value of `v`, if it is a source, flows to `var`.
|
||||
*/
|
||||
private predicate varSourceBaseCase(FlowVar var, Variable v) { var.definedByInitialValue(v) }
|
||||
|
||||
/**
|
||||
* Holds if `var` is defined by an assignment-like operation that causes flow
|
||||
* directly from `assignedExpr` to `var`, _and_ `assignedExpr` evaluates to
|
||||
* the same value as what is assigned to `var`.
|
||||
*/
|
||||
private predicate exprToVarStep(Expr assignedExpr, FlowVar var) {
|
||||
exists(ControlFlowNode operation |
|
||||
var.definedByExpr(assignedExpr, operation) and
|
||||
not operation instanceof PostfixCrementOperation
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is an access of the variable `var`.
|
||||
*/
|
||||
private predicate varToNodeStep(FlowVar var, Node n) {
|
||||
n.asExpr() = var.getAnAccess()
|
||||
or
|
||||
var.reachesRefParameter(n.(RefParameterFinalValueNode).getParameter())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `fromExpr` to `toExpr` directly, in the case
|
||||
* where `toExpr` is the immediate AST parent of `fromExpr`. For example,
|
||||
* data flows from `x` and `y` to `b ? x : y`.
|
||||
*/
|
||||
private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
||||
toExpr = any(ConditionalExpr cond | fromExpr = cond.getThen() or fromExpr = cond.getElse())
|
||||
or
|
||||
toExpr = any(AssignExpr assign | fromExpr = assign.getRValue())
|
||||
or
|
||||
toExpr = any(CommaExpr comma | fromExpr = comma.getRightOperand())
|
||||
or
|
||||
toExpr = any(PostfixCrementOperation op | fromExpr = op.getOperand())
|
||||
or
|
||||
toExpr = any(StmtExpr stmtExpr | fromExpr = stmtExpr.getResultExpr())
|
||||
or
|
||||
toExpr.(AddressOfExpr).getOperand() = fromExpr
|
||||
or
|
||||
// This rule enables flow from an array to its elements. Example: `a` to
|
||||
// `a[i]` or `*a`, where `a` is an array type. It does not enable flow from a
|
||||
// pointer to its indirection as in `p[i]` where `p` is a pointer type.
|
||||
exists(Expr toConverted |
|
||||
variablePartiallyAccessed(fromExpr, toConverted) and
|
||||
toExpr = toConverted.getUnconverted() and
|
||||
not toExpr = fromExpr
|
||||
)
|
||||
or
|
||||
toExpr.(BuiltInOperationBuiltInAddressOf).getOperand() = fromExpr
|
||||
or
|
||||
// The following case is needed to track the qualifier object for flow
|
||||
// through fields. It gives flow from `T(x)` to `new T(x)`. That's not
|
||||
// strictly _data_ flow but _taint_ flow because the type of `fromExpr` is
|
||||
// `T` while the type of `toExpr` is `T*`.
|
||||
//
|
||||
// This discrepancy is an artifact of how `new`-expressions are represented
|
||||
// in the database in a way that slightly varies from what the standard
|
||||
// specifies. In the C++ standard, there is no constructor call expression
|
||||
// `T(x)` after `new`. Instead there is a type `T` and an optional
|
||||
// initializer `(x)`.
|
||||
toExpr.(NewExpr).getInitializer() = fromExpr
|
||||
or
|
||||
// A lambda expression (`[captures](params){body}`) is just a thin wrapper
|
||||
// around the desugared closure creation in the form of a
|
||||
// `ClassAggregateLiteral` (`{ capture1, ..., captureN }`).
|
||||
toExpr.(LambdaExpression).getInitializer() = fromExpr
|
||||
or
|
||||
// Data flow through a function model.
|
||||
toExpr =
|
||||
any(Call call |
|
||||
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel |
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
(
|
||||
exists(int iIn |
|
||||
inModel.isParameterDeref(iIn) and
|
||||
call.passesByReference(iIn, fromExpr)
|
||||
)
|
||||
or
|
||||
exists(int iIn |
|
||||
inModel.isParameter(iIn) and
|
||||
fromExpr = call.getArgument(iIn)
|
||||
)
|
||||
or
|
||||
inModel.isQualifierObject() and
|
||||
fromExpr = call.getQualifier()
|
||||
or
|
||||
inModel.isQualifierAddress() and
|
||||
fromExpr = call.getQualifier()
|
||||
) and
|
||||
call.getTarget() = f and
|
||||
// AST dataflow treats a reference as if it were the referred-to object, while the dataflow
|
||||
// models treat references as pointers. If the return type of the call is a reference, then
|
||||
// look for data flow the the referred-to object, rather than the reference itself.
|
||||
if call.getType().getUnspecifiedType() instanceof ReferenceType
|
||||
then outModel.isReturnValueDeref()
|
||||
else outModel.isReturnValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isParameterDeref(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
||||
inModel.isParameterDeref(argInIndex) and
|
||||
call.passesByReference(argInIndex, exprIn)
|
||||
or
|
||||
inModel.isParameter(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private module FieldFlow {
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplLocal
|
||||
private import DataFlowPrivate
|
||||
|
||||
/**
|
||||
* A configuration for finding local-only flow through fields. This uses the
|
||||
* `Configuration` class in the dedicated `DataFlowImplLocal` copy of the
|
||||
* shared library that's not user-exposed directly.
|
||||
*
|
||||
* To keep the flow local to a single function, we put barriers on parameters
|
||||
* and return statements. Sources and sinks are the values that go into and
|
||||
* out of fields, respectively.
|
||||
*/
|
||||
private class FieldConfiguration extends Configuration {
|
||||
FieldConfiguration() { this = "FieldConfiguration" }
|
||||
|
||||
override predicate isSource(Node source) {
|
||||
storeStep(source, _, _)
|
||||
or
|
||||
// Also mark `foo(a.b);` as a source when `a.b` may be overwritten by `foo`.
|
||||
readStep(_, _, any(Node node | node.asExpr() = source.asDefiningArgument()))
|
||||
}
|
||||
|
||||
override predicate isSink(Node sink) { readStep(_, _, sink) }
|
||||
|
||||
override predicate isBarrier(Node node) { node instanceof ParameterNode }
|
||||
|
||||
override predicate isBarrierOut(Node node) {
|
||||
node.asExpr().getParent() instanceof ReturnStmt
|
||||
or
|
||||
node.asExpr().getParent() instanceof ThrowExpr
|
||||
}
|
||||
}
|
||||
|
||||
predicate fieldFlow(Node node1, Node node2) {
|
||||
exists(FieldConfiguration cfg | cfg.hasFlow(node1, node2)) and
|
||||
// This configuration should not be able to cross function boundaries, but
|
||||
// we double-check here just to be sure.
|
||||
getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2)
|
||||
}
|
||||
}
|
||||
|
||||
VariableAccess getAnAccessToAssignedVariable(Expr assign) {
|
||||
(
|
||||
assign instanceof Assignment
|
||||
or
|
||||
assign instanceof CrementOperation
|
||||
) and
|
||||
exists(FlowVar var |
|
||||
var.definedByExpr(_, assign) and
|
||||
result = var.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TContent =
|
||||
TFieldContent(Field f) or
|
||||
TCollectionContent() or
|
||||
TArrayContent()
|
||||
|
||||
/**
|
||||
* A description of the way data may be stored inside an object. Examples
|
||||
* include instance fields, the contents of a collection object, or the contents
|
||||
* of an array.
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
}
|
||||
|
||||
/** A reference through an instance field. */
|
||||
class FieldContent extends Content, TFieldContent {
|
||||
Field f;
|
||||
|
||||
FieldContent() { this = TFieldContent(f) }
|
||||
|
||||
Field getField() { result = f }
|
||||
|
||||
override string toString() { result = f.toString() }
|
||||
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
}
|
||||
|
||||
/** A reference through an array. */
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "[]" }
|
||||
}
|
||||
|
||||
/** A reference through the contents of some collection-like container. */
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "<element>" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A guard that validates some expression.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
* characteristic predicate precisely specifying the guard, and override
|
||||
* `checks` to specify what is being validated and in which branch.
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends GuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract predicate checks(Expr e, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final ExprNode getAGuardedNode() {
|
||||
exists(SsaDefinition def, Variable v, boolean branch |
|
||||
result.getExpr() = def.getAUse(v) and
|
||||
this.checks(def.getAUse(v), branch) and
|
||||
this.controls(result.getExpr().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*/
|
||||
cached
|
||||
Function viableCallable(CallInstruction call) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
result = call.getStaticCallTarget()
|
||||
or
|
||||
// If the target of the call does not have a body in the snapshot, it might
|
||||
// be because the target is just a header declaration, and the real target
|
||||
// will be determined at run time when the caller and callee are linked
|
||||
// together by the operating system's dynamic linker. In case a _unique_
|
||||
// function with the right signature is present in the database, we return
|
||||
// that as a potential callee.
|
||||
exists(string qualifiedName, int nparams |
|
||||
callSignatureWithoutBody(qualifiedName, nparams, call) and
|
||||
functionSignatureWithBody(qualifiedName, nparams, result) and
|
||||
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
|
||||
)
|
||||
or
|
||||
// Virtual dispatch
|
||||
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides virtual dispatch support compatible with the original
|
||||
* implementation of `semmle.code.cpp.security.TaintTracking`.
|
||||
*/
|
||||
private module VirtualDispatch {
|
||||
/** A call that may dispatch differently depending on the qualifier value. */
|
||||
abstract class DataSensitiveCall extends DataFlowCall {
|
||||
/**
|
||||
* Gets the node whose value determines the target of this call. This node
|
||||
* could be the qualifier of a virtual dispatch or the function-pointer
|
||||
* expression in a call to a function pointer. What they have in common is
|
||||
* that we need to find out which data flows there, and then it's up to the
|
||||
* `resolve` predicate to stitch that information together and resolve the
|
||||
* call.
|
||||
*/
|
||||
abstract DataFlow::Node getDispatchValue();
|
||||
|
||||
/** Gets a candidate target for this call. */
|
||||
abstract Function resolve();
|
||||
|
||||
/**
|
||||
* Whether `src` can flow to this call.
|
||||
*
|
||||
* Searches backwards from `getDispatchValue()` to `src`. The `allowFromArg`
|
||||
* parameter is true when the search is allowed to continue backwards into
|
||||
* a parameter; non-recursive callers should pass `_` for `allowFromArg`.
|
||||
*/
|
||||
predicate flowsFrom(DataFlow::Node src, boolean allowFromArg) {
|
||||
src = this.getDispatchValue() and allowFromArg = true
|
||||
or
|
||||
exists(DataFlow::Node other, boolean allowOtherFromArg |
|
||||
this.flowsFrom(other, allowOtherFromArg)
|
||||
|
|
||||
// Call argument
|
||||
exists(DataFlowCall call, int i |
|
||||
other.(DataFlow::ParameterNode).isParameterOf(call.getStaticCallTarget(), i) and
|
||||
src.(ArgumentNode).argumentOf(call, i)
|
||||
) and
|
||||
allowOtherFromArg = true and
|
||||
allowFromArg = true
|
||||
or
|
||||
// Call return
|
||||
exists(DataFlowCall call, ReturnKind returnKind |
|
||||
other = getAnOutNode(call, returnKind) and
|
||||
returnNodeWithKindAndEnclosingCallable(src, returnKind, call.getStaticCallTarget())
|
||||
) and
|
||||
allowFromArg = false
|
||||
or
|
||||
// Local flow
|
||||
DataFlow::localFlowStep(src, other) and
|
||||
allowFromArg = allowOtherFromArg
|
||||
or
|
||||
// Flow from global variable to load.
|
||||
exists(LoadInstruction load, GlobalOrNamespaceVariable var |
|
||||
var = src.asVariable() and
|
||||
other.asInstruction() = load and
|
||||
addressOfGlobal(load.getSourceAddress(), var) and
|
||||
// The `allowFromArg` concept doesn't play a role when `src` is a
|
||||
// global variable, so we just set it to a single arbitrary value for
|
||||
// performance.
|
||||
allowFromArg = true
|
||||
)
|
||||
or
|
||||
// Flow from store to global variable.
|
||||
exists(StoreInstruction store, GlobalOrNamespaceVariable var |
|
||||
var = other.asVariable() and
|
||||
store = src.asInstruction() and
|
||||
storeIntoGlobal(store, var) and
|
||||
// Setting `allowFromArg` to `true` like in the base case means we
|
||||
// treat a store to a global variable like the dispatch itself: flow
|
||||
// may come from anywhere.
|
||||
allowFromArg = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate storeIntoGlobal(StoreInstruction store, GlobalOrNamespaceVariable var) {
|
||||
addressOfGlobal(store.getDestinationAddress(), var)
|
||||
}
|
||||
|
||||
/** Holds if `addressInstr` is an instruction that produces the address of `var`. */
|
||||
private predicate addressOfGlobal(Instruction addressInstr, GlobalOrNamespaceVariable var) {
|
||||
// Access directly to the global variable
|
||||
addressInstr.(VariableAddressInstruction).getASTVariable() = var
|
||||
or
|
||||
// Access to a field on a global union
|
||||
exists(FieldAddressInstruction fa |
|
||||
fa = addressInstr and
|
||||
fa.getObjectAddress().(VariableAddressInstruction).getASTVariable() = var and
|
||||
fa.getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A ReturnNode with its ReturnKind and its enclosing callable.
|
||||
*
|
||||
* Used to fix a join ordering issue in flowsFrom.
|
||||
*/
|
||||
private predicate returnNodeWithKindAndEnclosingCallable(
|
||||
ReturnNode node, ReturnKind kind, DataFlowCallable callable
|
||||
) {
|
||||
node.getKind() = kind and
|
||||
node.getEnclosingCallable() = callable
|
||||
}
|
||||
|
||||
/** Call through a function pointer. */
|
||||
private class DataSensitiveExprCall extends DataSensitiveCall {
|
||||
DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) }
|
||||
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getCallTarget() }
|
||||
|
||||
override Function resolve() {
|
||||
exists(FunctionInstruction fi |
|
||||
this.flowsFrom(DataFlow::instructionNode(fi), _) and
|
||||
result = fi.getFunctionSymbol()
|
||||
) and
|
||||
(
|
||||
this.getNumberOfArguments() <= result.getEffectiveNumberOfParameters() and
|
||||
this.getNumberOfArguments() >= result.getEffectiveNumberOfParameters()
|
||||
or
|
||||
result.isVarargs()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Call to a virtual function. */
|
||||
private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
|
||||
DataSensitiveOverriddenFunctionCall() {
|
||||
exists(this.getStaticCallTarget().(VirtualFunction).getAnOverridingFunction())
|
||||
}
|
||||
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getThisArgument() }
|
||||
|
||||
override MemberFunction resolve() {
|
||||
exists(Class overridingClass |
|
||||
this.overrideMayAffectCall(overridingClass, result) and
|
||||
this.hasFlowFromCastFrom(overridingClass)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `this` is a virtual function call whose static target is
|
||||
* overridden by `overridingFunction` in `overridingClass`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate overrideMayAffectCall(Class overridingClass, MemberFunction overridingFunction) {
|
||||
overridingFunction.getAnOverriddenFunction+() = this.getStaticCallTarget().(VirtualFunction) and
|
||||
overridingFunction.getDeclaringType() = overridingClass
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the qualifier of `this` has flow from an upcast from
|
||||
* `derivedClass`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate hasFlowFromCastFrom(Class derivedClass) {
|
||||
exists(ConvertToBaseInstruction toBase |
|
||||
this.flowsFrom(DataFlow::instructionNode(toBase), _) and
|
||||
derivedClass = toBase.getDerivedClass()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is a function with a body that has name `qualifiedName` and
|
||||
* `nparams` parameter count. See `functionSignature`.
|
||||
*/
|
||||
private predicate functionSignatureWithBody(string qualifiedName, int nparams, Function f) {
|
||||
functionSignature(f, qualifiedName, nparams) and
|
||||
exists(f.getBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the target of `call` is a function _with no definition_ that has
|
||||
* name `qualifiedName` and `nparams` parameter count. See `functionSignature`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate callSignatureWithoutBody(string qualifiedName, int nparams, CallInstruction call) {
|
||||
exists(Function target |
|
||||
target = call.getStaticCallTarget() and
|
||||
not exists(target.getBlock()) and
|
||||
functionSignature(target, qualifiedName, nparams)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is
|
||||
* an approximation of its signature for the purpose of matching functions that
|
||||
* might be the same across link targets.
|
||||
*/
|
||||
private predicate functionSignature(Function f, string qualifiedName, int nparams) {
|
||||
qualifiedName = f.getQualifiedName() and
|
||||
nparams = f.getNumberOfParameters() and
|
||||
not f.isStatic()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the set of viable implementations that can be called by `call`
|
||||
* might be improved by knowing the call context.
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(CallInstruction call, Function f) {
|
||||
mayBenefitFromCallContext(call, f, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is a call through a function pointer, and the pointer
|
||||
* value is given as the `arg`'th argument to `f`.
|
||||
*/
|
||||
private predicate mayBenefitFromCallContext(
|
||||
VirtualDispatch::DataSensitiveCall call, Function f, int arg
|
||||
) {
|
||||
f = pragma[only_bind_out](call).getEnclosingCallable() and
|
||||
exists(InitializeParameterInstruction init |
|
||||
not exists(call.getStaticCallTarget()) and
|
||||
init.getEnclosingFunction() = f and
|
||||
call.flowsFrom(DataFlow::instructionNode(init), _) and
|
||||
init.getParameter().getIndex() = arg
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
|
||||
result = viableCallable(call) and
|
||||
exists(int i, Function f |
|
||||
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and
|
||||
f = ctx.getStaticCallTarget() and
|
||||
result = ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
|
||||
)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,527 +0,0 @@
|
||||
private import IR
|
||||
import InstructionConsistency // module is below
|
||||
import IRTypeConsistency // module is in IRType.qll
|
||||
|
||||
module InstructionConsistency {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
private newtype TOptionalIRFunction =
|
||||
TPresentIRFunction(IRFunction irFunc) or
|
||||
TMissingIRFunction()
|
||||
|
||||
/**
|
||||
* An `IRFunction` that might not exist. This is used so that we can produce consistency failures
|
||||
* for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
|
||||
*/
|
||||
abstract private class OptionalIRFunction extends TOptionalIRFunction {
|
||||
abstract string toString();
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
}
|
||||
|
||||
private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
|
||||
private IRFunction irFunc;
|
||||
|
||||
PresentIRFunction() { this = TPresentIRFunction(irFunc) }
|
||||
|
||||
override string toString() {
|
||||
result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
|
||||
}
|
||||
|
||||
override Language::Location getLocation() {
|
||||
// To avoid an overwhelming number of results when the extractor merges functions with the
|
||||
// same name, just pick a single location.
|
||||
result =
|
||||
min(Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
|
||||
override string toString() { result = "<Missing IRFunction>" }
|
||||
|
||||
override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
|
||||
result = TPresentIRFunction(instr.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
|
||||
result = getInstructionIRFunction(instr) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand) {
|
||||
result = TPresentIRFunction(operand.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
|
||||
result = getOperandIRFunction(operand) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getBlockIRFunction(IRBlock block) {
|
||||
result = TPresentIRFunction(block.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
|
||||
"' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock pred |
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
|
||||
pred.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(
|
||||
Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' is missing a type in function '$@'." and
|
||||
irFunc = getOperandIRFunction(operand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(chi, irFuncText)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message =
|
||||
"Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction and
|
||||
message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple edges of the same kind from `source`.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(
|
||||
Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(EdgeKind kind, int n |
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
message =
|
||||
"Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
|
||||
kind.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(source, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Language::Function f |
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int n |
|
||||
n = count(instr.getBlock().getAPredecessor()) and
|
||||
n < 2 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
|
||||
" predecessors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a memory operand is connected to a definition with an unmodeled result.
|
||||
*/
|
||||
query predicate memoryOperandDefinitionIsUnmodeled(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(MemoryOperand operand, Instruction def |
|
||||
operand = instr.getAnOperand() and
|
||||
def = operand.getAnyDef() and
|
||||
not def.isResultModeled() and
|
||||
message =
|
||||
"Memory operand definition on instruction '" + instr.toString() +
|
||||
"' has unmodeled result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(
|
||||
Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
|
||||
OptionalIRFunction defIRFunc, string defIRFuncText
|
||||
) {
|
||||
exists(Instruction useInstr, Instruction defInstr |
|
||||
operand.getUse() = useInstr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
|
||||
defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
|
||||
useIRFunc != defIRFunc and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
|
||||
"' in function '$@', but is defined on instruction '" + defInstr.toString() +
|
||||
"' in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int blockCount |
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
|
||||
" blocks in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f and
|
||||
message = "Function contains a loop consisting of only forward edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(
|
||||
IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction()) and
|
||||
message =
|
||||
"Block '" + block.toString() +
|
||||
"' is not reachable by traversing only forward edges in function '$@'." and
|
||||
irFunc = TPresentIRFunction(f) and
|
||||
irFuncText = irFunc.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
|
||||
exists(int fromInstr, int fromBlock |
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock and
|
||||
message =
|
||||
"The instruction graph for function '" + irFunc.toString() + "' contains " +
|
||||
fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
|
||||
+ " back edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(switchInstr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
|
||||
* memory.
|
||||
*/
|
||||
private predicate isOnAliasedDefinitionChain(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated() and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should not be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
overlap instanceof MayPartiallyOverlap and
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate nonUniqueEnclosingIRFunction(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int irFuncCount |
|
||||
irFuncCount = count(instr.getEnclosingIRFunction()) and
|
||||
irFuncCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
|
||||
" results for `getEnclosingIRFunction()` in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
|
||||
* address type.
|
||||
*/
|
||||
query predicate fieldAddressOnNonPointer(
|
||||
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
|
||||
message =
|
||||
"FieldAddress instruction '" + instr.toString() +
|
||||
"' has an object address operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
|
||||
* type.
|
||||
*/
|
||||
query predicate thisArgumentIsNonPointer(
|
||||
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
|
||||
not thisOperand.getIRType() instanceof IRAddressType
|
||||
) and
|
||||
message =
|
||||
"Call instruction '" + instr.toString() +
|
||||
"' has a `this` argument operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,527 +0,0 @@
|
||||
private import IR
|
||||
import InstructionConsistency // module is below
|
||||
import IRTypeConsistency // module is in IRType.qll
|
||||
|
||||
module InstructionConsistency {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
private newtype TOptionalIRFunction =
|
||||
TPresentIRFunction(IRFunction irFunc) or
|
||||
TMissingIRFunction()
|
||||
|
||||
/**
|
||||
* An `IRFunction` that might not exist. This is used so that we can produce consistency failures
|
||||
* for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
|
||||
*/
|
||||
abstract private class OptionalIRFunction extends TOptionalIRFunction {
|
||||
abstract string toString();
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
}
|
||||
|
||||
private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
|
||||
private IRFunction irFunc;
|
||||
|
||||
PresentIRFunction() { this = TPresentIRFunction(irFunc) }
|
||||
|
||||
override string toString() {
|
||||
result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
|
||||
}
|
||||
|
||||
override Language::Location getLocation() {
|
||||
// To avoid an overwhelming number of results when the extractor merges functions with the
|
||||
// same name, just pick a single location.
|
||||
result =
|
||||
min(Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
|
||||
override string toString() { result = "<Missing IRFunction>" }
|
||||
|
||||
override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
|
||||
result = TPresentIRFunction(instr.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
|
||||
result = getInstructionIRFunction(instr) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand) {
|
||||
result = TPresentIRFunction(operand.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
|
||||
result = getOperandIRFunction(operand) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getBlockIRFunction(IRBlock block) {
|
||||
result = TPresentIRFunction(block.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
|
||||
"' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock pred |
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
|
||||
pred.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(
|
||||
Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' is missing a type in function '$@'." and
|
||||
irFunc = getOperandIRFunction(operand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(chi, irFuncText)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message =
|
||||
"Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction and
|
||||
message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple edges of the same kind from `source`.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(
|
||||
Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(EdgeKind kind, int n |
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
message =
|
||||
"Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
|
||||
kind.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(source, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Language::Function f |
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int n |
|
||||
n = count(instr.getBlock().getAPredecessor()) and
|
||||
n < 2 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
|
||||
" predecessors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a memory operand is connected to a definition with an unmodeled result.
|
||||
*/
|
||||
query predicate memoryOperandDefinitionIsUnmodeled(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(MemoryOperand operand, Instruction def |
|
||||
operand = instr.getAnOperand() and
|
||||
def = operand.getAnyDef() and
|
||||
not def.isResultModeled() and
|
||||
message =
|
||||
"Memory operand definition on instruction '" + instr.toString() +
|
||||
"' has unmodeled result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(
|
||||
Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
|
||||
OptionalIRFunction defIRFunc, string defIRFuncText
|
||||
) {
|
||||
exists(Instruction useInstr, Instruction defInstr |
|
||||
operand.getUse() = useInstr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
|
||||
defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
|
||||
useIRFunc != defIRFunc and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
|
||||
"' in function '$@', but is defined on instruction '" + defInstr.toString() +
|
||||
"' in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int blockCount |
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
|
||||
" blocks in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f and
|
||||
message = "Function contains a loop consisting of only forward edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(
|
||||
IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction()) and
|
||||
message =
|
||||
"Block '" + block.toString() +
|
||||
"' is not reachable by traversing only forward edges in function '$@'." and
|
||||
irFunc = TPresentIRFunction(f) and
|
||||
irFuncText = irFunc.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
|
||||
exists(int fromInstr, int fromBlock |
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock and
|
||||
message =
|
||||
"The instruction graph for function '" + irFunc.toString() + "' contains " +
|
||||
fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
|
||||
+ " back edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(switchInstr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
|
||||
* memory.
|
||||
*/
|
||||
private predicate isOnAliasedDefinitionChain(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated() and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should not be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
overlap instanceof MayPartiallyOverlap and
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate nonUniqueEnclosingIRFunction(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int irFuncCount |
|
||||
irFuncCount = count(instr.getEnclosingIRFunction()) and
|
||||
irFuncCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
|
||||
" results for `getEnclosingIRFunction()` in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
|
||||
* address type.
|
||||
*/
|
||||
query predicate fieldAddressOnNonPointer(
|
||||
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
|
||||
message =
|
||||
"FieldAddress instruction '" + instr.toString() +
|
||||
"' has an object address operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
|
||||
* type.
|
||||
*/
|
||||
query predicate thisArgumentIsNonPointer(
|
||||
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
|
||||
not thisOperand.getIRType() instanceof IRAddressType
|
||||
) and
|
||||
message =
|
||||
"Call instruction '" + instr.toString() +
|
||||
"' has a `this` argument operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,527 +0,0 @@
|
||||
private import IR
|
||||
import InstructionConsistency // module is below
|
||||
import IRTypeConsistency // module is in IRType.qll
|
||||
|
||||
module InstructionConsistency {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
private newtype TOptionalIRFunction =
|
||||
TPresentIRFunction(IRFunction irFunc) or
|
||||
TMissingIRFunction()
|
||||
|
||||
/**
|
||||
* An `IRFunction` that might not exist. This is used so that we can produce consistency failures
|
||||
* for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
|
||||
*/
|
||||
abstract private class OptionalIRFunction extends TOptionalIRFunction {
|
||||
abstract string toString();
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
}
|
||||
|
||||
private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
|
||||
private IRFunction irFunc;
|
||||
|
||||
PresentIRFunction() { this = TPresentIRFunction(irFunc) }
|
||||
|
||||
override string toString() {
|
||||
result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
|
||||
}
|
||||
|
||||
override Language::Location getLocation() {
|
||||
// To avoid an overwhelming number of results when the extractor merges functions with the
|
||||
// same name, just pick a single location.
|
||||
result =
|
||||
min(Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
|
||||
override string toString() { result = "<Missing IRFunction>" }
|
||||
|
||||
override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
|
||||
result = TPresentIRFunction(instr.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
|
||||
result = getInstructionIRFunction(instr) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand) {
|
||||
result = TPresentIRFunction(operand.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
|
||||
result = getOperandIRFunction(operand) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getBlockIRFunction(IRBlock block) {
|
||||
result = TPresentIRFunction(block.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
|
||||
"' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock pred |
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
|
||||
pred.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(
|
||||
Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' is missing a type in function '$@'." and
|
||||
irFunc = getOperandIRFunction(operand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(chi, irFuncText)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message =
|
||||
"Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction and
|
||||
message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple edges of the same kind from `source`.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(
|
||||
Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(EdgeKind kind, int n |
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
message =
|
||||
"Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
|
||||
kind.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(source, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Language::Function f |
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int n |
|
||||
n = count(instr.getBlock().getAPredecessor()) and
|
||||
n < 2 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
|
||||
" predecessors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a memory operand is connected to a definition with an unmodeled result.
|
||||
*/
|
||||
query predicate memoryOperandDefinitionIsUnmodeled(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(MemoryOperand operand, Instruction def |
|
||||
operand = instr.getAnOperand() and
|
||||
def = operand.getAnyDef() and
|
||||
not def.isResultModeled() and
|
||||
message =
|
||||
"Memory operand definition on instruction '" + instr.toString() +
|
||||
"' has unmodeled result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(
|
||||
Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
|
||||
OptionalIRFunction defIRFunc, string defIRFuncText
|
||||
) {
|
||||
exists(Instruction useInstr, Instruction defInstr |
|
||||
operand.getUse() = useInstr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
|
||||
defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
|
||||
useIRFunc != defIRFunc and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
|
||||
"' in function '$@', but is defined on instruction '" + defInstr.toString() +
|
||||
"' in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int blockCount |
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
|
||||
" blocks in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f and
|
||||
message = "Function contains a loop consisting of only forward edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(
|
||||
IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction()) and
|
||||
message =
|
||||
"Block '" + block.toString() +
|
||||
"' is not reachable by traversing only forward edges in function '$@'." and
|
||||
irFunc = TPresentIRFunction(f) and
|
||||
irFuncText = irFunc.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
|
||||
exists(int fromInstr, int fromBlock |
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock and
|
||||
message =
|
||||
"The instruction graph for function '" + irFunc.toString() + "' contains " +
|
||||
fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
|
||||
+ " back edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(switchInstr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
|
||||
* memory.
|
||||
*/
|
||||
private predicate isOnAliasedDefinitionChain(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated() and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should not be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
overlap instanceof MayPartiallyOverlap and
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate nonUniqueEnclosingIRFunction(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int irFuncCount |
|
||||
irFuncCount = count(instr.getEnclosingIRFunction()) and
|
||||
irFuncCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
|
||||
" results for `getEnclosingIRFunction()` in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
|
||||
* address type.
|
||||
*/
|
||||
query predicate fieldAddressOnNonPointer(
|
||||
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
|
||||
message =
|
||||
"FieldAddress instruction '" + instr.toString() +
|
||||
"' has an object address operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
|
||||
* type.
|
||||
*/
|
||||
query predicate thisArgumentIsNonPointer(
|
||||
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
|
||||
not thisOperand.getIRType() instanceof IRAddressType
|
||||
) and
|
||||
message =
|
||||
"Call instruction '" + instr.toString() +
|
||||
"' has a `this` argument operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling the MySql C API.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.models.interfaces.Sql
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
|
||||
|
||||
/**
|
||||
* The `mysql_query` family of functions from the MySQL C API.
|
||||
*/
|
||||
private class MySqlExecutionFunction extends SqlExecutionFunction {
|
||||
MySqlExecutionFunction() {
|
||||
this.hasName(["mysql_query", "mysql_real_query", "mysql_real_query_nonblocking"])
|
||||
}
|
||||
|
||||
override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `mysql_real_escape_string` family of functions from the MySQL C API.
|
||||
*/
|
||||
private class MySqlBarrierFunction extends SqlBarrierFunction {
|
||||
MySqlBarrierFunction() {
|
||||
this.hasName(["mysql_real_escape_string", "mysql_real_escape_string_quote"])
|
||||
}
|
||||
|
||||
override predicate barrierSqlArgument(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(2) and
|
||||
output.isParameterDeref(1)
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
private import semmle.code.cpp.models.interfaces.Sql
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
|
||||
|
||||
private predicate pqxxTransactionSqlArgument(string function, int arg) {
|
||||
function = "exec" and arg = 0
|
||||
or
|
||||
function = "exec0" and arg = 0
|
||||
or
|
||||
function = "exec1" and arg = 0
|
||||
or
|
||||
function = "exec_n" and arg = 1
|
||||
or
|
||||
function = "exec_params" and arg = 0
|
||||
or
|
||||
function = "exec_params0" and arg = 0
|
||||
or
|
||||
function = "exec_params1" and arg = 0
|
||||
or
|
||||
function = "exec_params_n" and arg = 1
|
||||
or
|
||||
function = "query_value" and arg = 0
|
||||
or
|
||||
function = "stream" and arg = 0
|
||||
}
|
||||
|
||||
private predicate pqxxConnectionSqlArgument(string function, int arg) {
|
||||
function = "prepare" and arg = 1
|
||||
}
|
||||
|
||||
private predicate pqxxTransationClassNames(string className, string namespace) {
|
||||
namespace = "pqxx" and
|
||||
className in [
|
||||
"dbtransaction", "nontransaction", "basic_robusttransaction", "robusttransaction",
|
||||
"subtransaction", "transaction", "basic_transaction", "transaction_base", "work"
|
||||
]
|
||||
}
|
||||
|
||||
private predicate pqxxConnectionClassNames(string className, string namespace) {
|
||||
namespace = "pqxx" and
|
||||
className in ["connection_base", "basic_connection", "connection"]
|
||||
}
|
||||
|
||||
private predicate pqxxEscapeArgument(string function, int arg) {
|
||||
arg = 0 and
|
||||
function in ["esc", "esc_raw", "quote", "quote_raw", "quote_name", "quote_table", "esc_like"]
|
||||
}
|
||||
|
||||
private class PostgreSqlExecutionFunction extends SqlExecutionFunction {
|
||||
PostgreSqlExecutionFunction() {
|
||||
exists(Class c |
|
||||
this.getDeclaringType() = c and
|
||||
// transaction exec and connection prepare variations
|
||||
(
|
||||
pqxxTransationClassNames(c.getName(), c.getNamespace().getName()) and
|
||||
pqxxTransactionSqlArgument(this.getName(), _)
|
||||
or
|
||||
pqxxConnectionSqlArgument(this.getName(), _) and
|
||||
pqxxConnectionClassNames(c.getName(), c.getNamespace().getName())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasSqlArgument(FunctionInput input) {
|
||||
exists(int argIndex |
|
||||
pqxxTransactionSqlArgument(this.getName(), argIndex)
|
||||
or
|
||||
pqxxConnectionSqlArgument(this.getName(), argIndex)
|
||||
|
|
||||
input.isParameterDeref(argIndex)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class PostgreSqlBarrierFunction extends SqlBarrierFunction {
|
||||
PostgreSqlBarrierFunction() {
|
||||
exists(Class c |
|
||||
this.getDeclaringType() = c and
|
||||
// transaction and connection escape functions
|
||||
(
|
||||
pqxxTransationClassNames(c.getName(), c.getNamespace().getName()) or
|
||||
pqxxConnectionClassNames(c.getName(), c.getNamespace().getName())
|
||||
) and
|
||||
pqxxEscapeArgument(this.getName(), _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate barrierSqlArgument(FunctionInput input, FunctionOutput output) {
|
||||
exists(int argIndex |
|
||||
input.isParameterDeref(argIndex) and
|
||||
output.isReturnValueDeref() and
|
||||
pqxxEscapeArgument(this.getName(), argIndex)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling the SQLite C API.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.models.interfaces.Sql
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
|
||||
|
||||
/**
|
||||
* The `sqlite3_exec` and `sqlite3_prepare` families of functions from the SQLite C API.
|
||||
*/
|
||||
private class SqLite3ExecutionFunction extends SqlExecutionFunction {
|
||||
SqLite3ExecutionFunction() {
|
||||
this.hasName([
|
||||
"sqlite3_exec", "sqlite3_prepare", "sqlite3_prepare_v2", "sqlite3_prepare_v3",
|
||||
"sqlite3_prepare16", "sqlite3_prepare16_v2", "sqlite3_prepare16_v3"
|
||||
])
|
||||
}
|
||||
|
||||
override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Provides abstract classes for modeling functions that execute and escape SQL query strings.
|
||||
* To extend this QL library, create a QL class extending `SqlExecutionFunction` or `SqlEscapeFunction`
|
||||
* with a characteristic predicate that selects the function or set of functions you are modeling.
|
||||
* Within that class, override the predicates provided by the class to match the way a
|
||||
* parameter flows into the function and, in the case of `SqlEscapeFunction`, out of the function.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
|
||||
/**
|
||||
* An abstract class that represents a function that executes an SQL query.
|
||||
*/
|
||||
abstract class SqlExecutionFunction extends Function {
|
||||
/**
|
||||
* Holds if `input` to this function represents SQL code to be executed.
|
||||
*/
|
||||
abstract predicate hasSqlArgument(FunctionInput input);
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract class that represents a function that is a barrier to an SQL query string.
|
||||
*/
|
||||
abstract class SqlBarrierFunction extends Function {
|
||||
/**
|
||||
* Holds if the `output` is a barrier to the SQL input `input` such that is it safe to pass to
|
||||
* an `SqlExecutionFunction`.
|
||||
*/
|
||||
abstract predicate barrierSqlArgument(FunctionInput input, FunctionOutput output);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Provides classes for heuristically identifying variables and functions that
|
||||
* might contain or return a password or other sensitive information.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Holds if the name `s` suggests something might contain or return a password
|
||||
* or other sensitive information.
|
||||
*/
|
||||
bindingset[s]
|
||||
private predicate suspicious(string s) {
|
||||
(
|
||||
s.matches("%password%") or
|
||||
s.matches("%passwd%") or
|
||||
s.matches("%trusted%")
|
||||
) and
|
||||
not (
|
||||
s.matches("%hash%") or
|
||||
s.matches("%crypt%") or
|
||||
s.matches("%file%") or
|
||||
s.matches("%path%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that might contain a password or other sensitive information.
|
||||
*/
|
||||
class SensitiveVariable extends Variable {
|
||||
SensitiveVariable() {
|
||||
suspicious(getName().toLowerCase()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that might return a password or other sensitive information.
|
||||
*/
|
||||
class SensitiveFunction extends Function {
|
||||
SensitiveFunction() {
|
||||
suspicious(getName().toLowerCase()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression whose value might be a password or other sensitive information.
|
||||
*/
|
||||
class SensitiveExpr extends Expr {
|
||||
SensitiveExpr() {
|
||||
this.(VariableAccess).getTarget() instanceof SensitiveVariable or
|
||||
this.(FunctionCall).getTarget() instanceof SensitiveFunction
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.3
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/static-buffer-overflow
|
||||
* @tags reliability
|
||||
* security
|
||||
@@ -55,8 +55,6 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
|
||||
loop.counter().getAnAccess() = bufaccess.getArrayOffset() and
|
||||
// Ensure that we don't have an upper bound on the array index that's less than the buffer size.
|
||||
not upperBound(bufaccess.getArrayOffset().getFullyConverted()) < bufaccess.bufferSize() and
|
||||
// The upper bounds analysis must not have been widended
|
||||
not upperBoundMayBeWidened(bufaccess.getArrayOffset().getFullyConverted()) and
|
||||
msg =
|
||||
"Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
|
||||
loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " +
|
||||
@@ -132,13 +130,11 @@ predicate outOfBounds(BufferAccess bufaccess, string msg) {
|
||||
(
|
||||
access > size
|
||||
or
|
||||
access = size and
|
||||
not exists(AddressOfExpr addof | bufaccess = addof.getOperand()) and
|
||||
not exists(BuiltInOperationBuiltInOffsetOf offsetof | offsetof.getAChild() = bufaccess)
|
||||
access = size and not exists(AddressOfExpr addof | bufaccess = addof.getOperand())
|
||||
) and
|
||||
msg =
|
||||
"Potential buffer-overflow: '" + buf + "' has size " + size.toString() + " but '" + buf + "[" +
|
||||
access.toString() + "]' may be accessed here."
|
||||
access.toString() + "]' is accessed here."
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
|
||||
import cpp
|
||||
|
||||
class AnonymousCompilation extends Compilation {
|
||||
override string toString() { result = "<compilation>" }
|
||||
}
|
||||
|
||||
string describe(Compilation c) {
|
||||
if c.getArgument(1) = "--mimic"
|
||||
then result = "compiler invocation " + concat(int i | i > 1 | c.getArgument(i), " " order by i)
|
||||
@@ -15,4 +19,4 @@ string describe(Compilation c) {
|
||||
|
||||
from Compilation c
|
||||
where not c.normalTermination()
|
||||
select "Extraction aborted for " + describe(c)
|
||||
select c, "Extraction aborted for " + describe(c), 2
|
||||
|
||||
@@ -13,15 +13,10 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from BitField fi, VariableAccess va, Type fct
|
||||
from BitField fi, VariableAccess va
|
||||
where
|
||||
(
|
||||
if va.getFullyConverted().getType() instanceof ReferenceType
|
||||
then fct = va.getFullyConverted().getType().(ReferenceType).getBaseType()
|
||||
else fct = va.getFullyConverted().getType()
|
||||
) and
|
||||
fi.getNumBits() > fct.getSize() * 8 and
|
||||
va.getExplicitlyConverted().getType().getSize() > fct.getSize() and
|
||||
fi.getNumBits() > va.getFullyConverted().getType().getSize() * 8 and
|
||||
va.getExplicitlyConverted().getType().getSize() > va.getFullyConverted().getType().getSize() and
|
||||
va.getTarget() = fi and
|
||||
not fct.getUnspecifiedType() instanceof BoolType
|
||||
not va.getActualType() instanceof BoolType
|
||||
select va, "Implicit downcast of bitfield $@", fi, fi.toString()
|
||||
|
||||
@@ -19,32 +19,28 @@ import cpp
|
||||
* Holds if the argument corresponding to the `pos` conversion specifier
|
||||
* of `ffc` is expected to have type `expected`.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate formattingFunctionCallExpectedType(
|
||||
FormattingFunctionCall ffc, int pos, Type expected
|
||||
) {
|
||||
ffc.getFormat().(FormatLiteral).getConversionType(pos) = expected
|
||||
exists(FormattingFunction f, int i, FormatLiteral fl |
|
||||
ffc instanceof FormattingFunctionCall and
|
||||
ffc.getTarget() = f and
|
||||
f.getFormatParameterIndex() = i and
|
||||
ffc.getArgument(i) = fl and
|
||||
fl.getConversionType(pos) = expected
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the argument corresponding to the `pos` conversion specifier
|
||||
* of `ffc` could alternatively have type `expected`, for example on a different
|
||||
* platform.
|
||||
*/
|
||||
private predicate formattingFunctionCallAlternateType(
|
||||
FormattingFunctionCall ffc, int pos, Type expected
|
||||
) {
|
||||
ffc.getFormat().(FormatLiteral).getConversionTypeAlternate(pos) = expected
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the argument corresponding to the `pos` conversion specifier
|
||||
* of `ffc` is `arg` and has type `actual`.
|
||||
* of `ffc` is expected to have type `expected` and the corresponding
|
||||
* argument `arg` has type `actual`.
|
||||
*/
|
||||
pragma[noopt]
|
||||
predicate formattingFunctionCallActualType(
|
||||
FormattingFunctionCall ffc, int pos, Expr arg, Type actual
|
||||
) {
|
||||
predicate formatArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual) {
|
||||
exists(Expr argConverted |
|
||||
formattingFunctionCallExpectedType(ffc, pos, expected) and
|
||||
ffc.getConversionArgument(pos) = arg and
|
||||
argConverted = arg.getFullyConverted() and
|
||||
actual = argConverted.getType()
|
||||
@@ -76,8 +72,7 @@ class ExpectedType extends Type {
|
||||
ExpectedType() {
|
||||
exists(Type t |
|
||||
(
|
||||
formattingFunctionCallExpectedType(_, _, t) or
|
||||
formattingFunctionCallAlternateType(_, _, t) or
|
||||
formatArgType(_, _, t, _, _) or
|
||||
formatOtherArgType(_, _, t, _, _)
|
||||
) and
|
||||
this = t.getUnspecifiedType()
|
||||
@@ -96,11 +91,7 @@ class ExpectedType extends Type {
|
||||
*/
|
||||
predicate trivialConversion(ExpectedType expected, Type actual) {
|
||||
exists(Type exp, Type act |
|
||||
(
|
||||
formattingFunctionCallExpectedType(_, _, exp) or
|
||||
formattingFunctionCallAlternateType(_, _, exp)
|
||||
) and
|
||||
formattingFunctionCallActualType(_, _, _, act) and
|
||||
formatArgType(_, _, exp, _, act) and
|
||||
expected = exp.getUnspecifiedType() and
|
||||
actual = act.getUnspecifiedType()
|
||||
) and
|
||||
@@ -155,13 +146,9 @@ int sizeof_IntType() { exists(IntType it | result = it.getSize()) }
|
||||
from FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual
|
||||
where
|
||||
(
|
||||
formattingFunctionCallExpectedType(ffc, n, expected) and
|
||||
formattingFunctionCallActualType(ffc, n, arg, actual) and
|
||||
formatArgType(ffc, n, expected, arg, actual) and
|
||||
not exists(Type anyExpected |
|
||||
(
|
||||
formattingFunctionCallExpectedType(ffc, n, anyExpected) or
|
||||
formattingFunctionCallAlternateType(ffc, n, anyExpected)
|
||||
) and
|
||||
formatArgType(ffc, n, anyExpected, arg, actual) and
|
||||
trivialConversion(anyExpected.getUnspecifiedType(), actual.getUnspecifiedType())
|
||||
)
|
||||
or
|
||||
|
||||
@@ -29,19 +29,11 @@ class ImproperNullTerminationReachability extends StackVariableReachabilityWithR
|
||||
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
|
||||
node = declWithNoInit(v)
|
||||
or
|
||||
exists(Call c, int bufferArg, int sizeArg |
|
||||
exists(Call c, VariableAccess va |
|
||||
c = node and
|
||||
(
|
||||
c.getTarget().hasName("readlink") and bufferArg = 1 and sizeArg = 2
|
||||
or
|
||||
c.getTarget().hasName("readlinkat") and bufferArg = 2 and sizeArg = 3
|
||||
) and
|
||||
c.getArgument(bufferArg).(VariableAccess).getTarget() = v and
|
||||
(
|
||||
// buffer size parameter likely matches the full buffer size
|
||||
c.getArgument(sizeArg) instanceof SizeofOperator or
|
||||
c.getArgument(sizeArg).getValue().toInt() = v.getType().getSize()
|
||||
)
|
||||
c.getTarget().hasName("readlink") and
|
||||
c.getArgument(1) = va and
|
||||
va.getTarget() = v
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ DeclStmt declWithNoInit(LocalVariable v) {
|
||||
result.getADeclaration() = v and
|
||||
not exists(v.getInitializer()) and
|
||||
/* The type of the variable is not stack-allocated. */
|
||||
exists(Type t | t = v.getType() | not allocatedType(t))
|
||||
not allocatedType(v.getType())
|
||||
}
|
||||
|
||||
class UninitialisedLocalReachability extends StackVariableReachability {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* @name Sum of frontend and extractor time
|
||||
* @description The sum of elapsed frontend time, and the sum of elapsed extractor time.
|
||||
* This query is for internal use only and may change without notice.
|
||||
* @kind table
|
||||
* @id cpp/frontend-and-extractor-time
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
select sum(Compilation c, float seconds | compilation_time(c, _, 2, seconds) | seconds) as sum_frontend_elapsed_seconds,
|
||||
sum(Compilation c, float seconds | compilation_time(c, _, 4, seconds) | seconds) as sum_extractor_elapsed_seconds
|
||||
@@ -9,6 +9,7 @@
|
||||
* @tags reliability
|
||||
* external/cwe/cwe-573
|
||||
* external/cwe/cwe-252
|
||||
* @opaque-id SM02344
|
||||
* @microsoft.severity Important
|
||||
*/
|
||||
|
||||
|
||||
@@ -30,15 +30,7 @@ class Configuration extends TaintTrackingConfiguration {
|
||||
}
|
||||
|
||||
override predicate isBarrier(Expr e) {
|
||||
super.isBarrier(e)
|
||||
or
|
||||
e.getUnspecifiedType() instanceof IntegralType
|
||||
or
|
||||
exists(SqlBarrierFunction sql, int arg, FunctionInput input |
|
||||
e = sql.getACallToThisFunction().getArgument(arg) and
|
||||
input.isParameterDeref(arg) and
|
||||
sql.barrierSqlArgument(input, _)
|
||||
)
|
||||
super.isBarrier(e) or e.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,122 +15,58 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
import Bounded
|
||||
|
||||
/**
|
||||
* A function that outputs random data such as `std::rand`.
|
||||
*/
|
||||
abstract class RandomFunction extends Function {
|
||||
/**
|
||||
* Gets the `FunctionOutput` that describes how this function returns the random data.
|
||||
*/
|
||||
FunctionOutput getFunctionOutput() { result.isReturnValue() }
|
||||
predicate isUnboundedRandCall(FunctionCall fc) {
|
||||
exists(Function func | func = fc.getTarget() |
|
||||
func.hasGlobalOrStdOrBslName("rand") and
|
||||
not bounded(fc) and
|
||||
func.getNumberOfParameters() = 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `std::rand`.
|
||||
*/
|
||||
private class StdRand extends RandomFunction {
|
||||
StdRand() {
|
||||
this.hasGlobalOrStdOrBslName("rand") and
|
||||
this.getNumberOfParameters() = 0
|
||||
}
|
||||
predicate isUnboundedRandCallOrParent(Expr e) {
|
||||
isUnboundedRandCall(e)
|
||||
or
|
||||
isUnboundedRandCallOrParent(e.getAChild())
|
||||
}
|
||||
|
||||
/**
|
||||
* The Unix function `rand_r`.
|
||||
*/
|
||||
private class RandR extends RandomFunction {
|
||||
RandR() {
|
||||
this.hasGlobalName("rand_r") and
|
||||
this.getNumberOfParameters() = 1
|
||||
}
|
||||
predicate isUnboundedRandValue(Expr e) {
|
||||
isUnboundedRandCall(e)
|
||||
or
|
||||
exists(MacroInvocation mi |
|
||||
e = mi.getExpr() and
|
||||
isUnboundedRandCallOrParent(e)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The Unix function `random`.
|
||||
*/
|
||||
private class Random extends RandomFunction {
|
||||
Random() {
|
||||
this.hasGlobalName("random") and
|
||||
this.getNumberOfParameters() = 1
|
||||
class SecurityOptionsArith extends SecurityOptions {
|
||||
override predicate isUserInput(Expr expr, string cause) {
|
||||
isUnboundedRandValue(expr) and
|
||||
cause = "rand"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Windows `rand_s` function.
|
||||
*/
|
||||
private class RandS extends RandomFunction {
|
||||
RandS() {
|
||||
this.hasGlobalName("rand_s") and
|
||||
this.getNumberOfParameters() = 1
|
||||
}
|
||||
|
||||
override FunctionOutput getFunctionOutput() { result.isParameterDeref(0) }
|
||||
}
|
||||
|
||||
predicate missingGuard(VariableAccess va, string effect) {
|
||||
exists(Operation op | op.getAnOperand() = va |
|
||||
// underflow - random numbers are usually non-negative, so underflow is
|
||||
// only likely if the type is unsigned. Multiplication is also unlikely to
|
||||
// cause underflow of a non-negative number.
|
||||
missingGuardAgainstUnderflow(op, va) and
|
||||
effect = "underflow" and
|
||||
op.getUnspecifiedType().(IntegralType).isUnsigned() and
|
||||
not op instanceof MulExpr
|
||||
missingGuardAgainstUnderflow(op, va) and effect = "underflow"
|
||||
or
|
||||
// overflow
|
||||
missingGuardAgainstOverflow(op, va) and effect = "overflow"
|
||||
)
|
||||
}
|
||||
|
||||
class UncontrolledArithConfiguration extends TaintTracking::Configuration {
|
||||
UncontrolledArithConfiguration() { this = "UncontrolledArithConfiguration" }
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element e) { missingGuard(e, _) }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(RandomFunction rand, Call call | call.getTarget() = rand |
|
||||
rand.getFunctionOutput().isReturnValue() and
|
||||
source.asExpr() = call
|
||||
or
|
||||
exists(int n |
|
||||
source.asDefiningArgument() = call.getArgument(n) and
|
||||
rand.getFunctionOutput().isParameterDeref(n)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { missingGuard(sink.asExpr(), _) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
bounded(node.asExpr())
|
||||
or
|
||||
// If this expression is part of bitwise 'and' or 'or' operation it's likely that the value is
|
||||
// only used as a bit pattern.
|
||||
node.asExpr() =
|
||||
any(Operation op |
|
||||
op instanceof BitwiseOrExpr or
|
||||
op instanceof BitwiseAndExpr or
|
||||
op instanceof ComplementExpr
|
||||
).getAnOperand*()
|
||||
or
|
||||
// block unintended flow to pointers
|
||||
node.asExpr().getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
override predicate isBarrier(Expr e) { super.isBarrier(e) or bounded(e) }
|
||||
}
|
||||
|
||||
/** Gets the expression that corresponds to `node`, if any. */
|
||||
Expr getExpr(DataFlow::Node node) { result = [node.asExpr(), node.asDefiningArgument()] }
|
||||
|
||||
from
|
||||
UncontrolledArithConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
VariableAccess va, string effect
|
||||
from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode
|
||||
where
|
||||
config.hasFlowPath(source, sink) and
|
||||
sink.getNode().asExpr() = va and
|
||||
taintedWithPath(origin, va, sourceNode, sinkNode) and
|
||||
missingGuard(va, effect)
|
||||
select sink.getNode(), source, sink,
|
||||
"$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".",
|
||||
getExpr(source.getNode()), "Uncontrolled value"
|
||||
select va, sourceNode, sinkNode,
|
||||
"$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin,
|
||||
"Uncontrolled value"
|
||||
|
||||
@@ -7,6 +7,25 @@ private import cpp
|
||||
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
/**
|
||||
* An operand `e` of a division expression (i.e., `e` is an operand of either a `DivExpr` or
|
||||
* a `AssignDivExpr`) is bounded when `e` is the left-hand side of the division.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedDiv(Expr e, Expr left) { e = left }
|
||||
|
||||
/**
|
||||
* An operand `e` of a remainder expression `rem` (i.e., `rem` is either a `RemExpr` or
|
||||
* an `AssignRemExpr`) with left-hand side `left` and right-ahnd side `right` is bounded
|
||||
* when `e` is `left` and `right` is upper bounded by some number that is less than the maximum integer
|
||||
* allowed by the result type of `rem`.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedRem(Expr e, Expr rem, Expr left, Expr right) {
|
||||
e = left and
|
||||
upperBound(right.getFullyConverted()) < exprMaxVal(rem.getFullyConverted())
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand `e` of a bitwise and expression `andExpr` (i.e., `andExpr` is either an `BitwiseAndExpr`
|
||||
* or an `AssignAndExpr`) with operands `operand1` and `operand2` is the operand that is not `e` is upper
|
||||
@@ -31,10 +50,19 @@ predicate bounded(Expr e) {
|
||||
) and
|
||||
not convertedExprMightOverflow(e)
|
||||
or
|
||||
// Optimitically assume that a remainder expression always yields a much smaller value.
|
||||
e = any(RemExpr rem).getLeftOperand()
|
||||
// For `%` and `&` we require that `e` is bounded by a value that is strictly smaller than the
|
||||
// maximum possible value of the result type of the operation.
|
||||
// For example, the function call `rand()` is considered bounded in the following program:
|
||||
// ```
|
||||
// int i = rand() % (UINT8_MAX + 1);
|
||||
// ```
|
||||
// but not in:
|
||||
// ```
|
||||
// unsigned char uc = rand() % (UINT8_MAX + 1);
|
||||
// ```
|
||||
exists(RemExpr rem | boundedRem(e, rem, rem.getLeftOperand(), rem.getRightOperand()))
|
||||
or
|
||||
e = any(AssignRemExpr rem).getLValue()
|
||||
exists(AssignRemExpr rem | boundedRem(e, rem, rem.getLValue(), rem.getRValue()))
|
||||
or
|
||||
exists(BitwiseAndExpr andExpr |
|
||||
boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand())
|
||||
@@ -45,11 +73,11 @@ predicate bounded(Expr e) {
|
||||
)
|
||||
or
|
||||
// Optimitically assume that a division always yields a much smaller value.
|
||||
e = any(DivExpr div).getLeftOperand()
|
||||
boundedDiv(e, any(DivExpr div).getLeftOperand())
|
||||
or
|
||||
e = any(AssignDivExpr div).getLValue()
|
||||
boundedDiv(e, any(AssignDivExpr div).getLValue())
|
||||
or
|
||||
e = any(RShiftExpr shift).getLeftOperand()
|
||||
boundedDiv(e, any(RShiftExpr shift).getLeftOperand())
|
||||
or
|
||||
e = any(AssignRShiftExpr div).getLValue()
|
||||
boundedDiv(e, any(AssignRShiftExpr div).getLValue())
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/cleartext-storage-file
|
||||
* @tags security
|
||||
* external/cwe/cwe-313
|
||||
@@ -14,40 +14,10 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.SensitiveExprs
|
||||
import semmle.code.cpp.security.FileWrite
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* An operation on a filename.
|
||||
*/
|
||||
predicate filenameOperation(FunctionCall op, Expr path) {
|
||||
exists(string name | name = op.getTarget().getName() |
|
||||
name =
|
||||
[
|
||||
"remove", "unlink", "rmdir", "rename", "fopen", "open", "freopen", "_open", "_wopen",
|
||||
"_wfopen", "_fsopen", "_wfsopen", "chmod", "chown", "stat", "lstat", "fstat", "access",
|
||||
"_access", "_waccess", "_access_s", "_waccess_s"
|
||||
] and
|
||||
path = op.getArgument(0)
|
||||
or
|
||||
name = ["fopen_s", "wfopen_s", "rename"] and
|
||||
path = op.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isFileName(GVN gvn) {
|
||||
exists(FunctionCall op, Expr path |
|
||||
filenameOperation(op, path) and
|
||||
gvn = globalValueNumber(path)
|
||||
)
|
||||
}
|
||||
|
||||
from FileWrite w, SensitiveExpr source, Expr mid, Expr dest
|
||||
from FileWrite w, SensitiveExpr source, Expr dest
|
||||
where
|
||||
DataFlow::localFlow(DataFlow::exprNode(source), DataFlow::exprNode(mid)) and
|
||||
mid = w.getASource() and
|
||||
dest = w.getDest() and
|
||||
not isFileName(globalValueNumber(source)) and // file names are not passwords
|
||||
not exists(string convChar | convChar = w.getSourceConvChar(mid) | not convChar = ["s", "S"]) // ignore things written with other conversion characters
|
||||
source = w.getASource() and
|
||||
dest = w.getDest()
|
||||
select w, "This write into file '" + dest.toString() + "' may contain unencrypted data from $@",
|
||||
source, "this source."
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.7
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/toctou-race-condition
|
||||
* @tags security
|
||||
* external/cwe/cwe-367
|
||||
@@ -16,60 +16,59 @@ import cpp
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* An operation on a filename that is likely to modify the corresponding file
|
||||
* and may return an indication of success.
|
||||
* An operation on a filename.
|
||||
*
|
||||
* Note: we're not interested in operations where the file is specified by a
|
||||
* descriptor, rather than a filename, as they are better behaved. We are
|
||||
* interested in functions that take a filename and return a file descriptor,
|
||||
* however.
|
||||
* Note: we're not interested in operations on file descriptors, as they
|
||||
* are better behaved.
|
||||
*/
|
||||
FunctionCall filenameOperation(Expr path) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name =
|
||||
[
|
||||
"remove", "unlink", "rmdir", "rename", "fopen", "open", "freopen", "_open", "_wopen",
|
||||
"_wfopen", "_fsopen", "_wfsopen"
|
||||
] and
|
||||
(
|
||||
name = "remove" or
|
||||
name = "unlink" or
|
||||
name = "rmdir" or
|
||||
name = "rename" or
|
||||
name = "chmod" or
|
||||
name = "chown" or
|
||||
name = "fopen" or
|
||||
name = "open" or
|
||||
name = "freopen" or
|
||||
name = "_open" or
|
||||
name = "_wopen" or
|
||||
name = "_wfopen"
|
||||
) and
|
||||
result.getArgument(0) = path
|
||||
or
|
||||
name = ["fopen_s", "wfopen_s", "rename"] and
|
||||
(
|
||||
name = "fopen_s" or
|
||||
name = "wfopen_s"
|
||||
) and
|
||||
result.getArgument(1) = path
|
||||
)
|
||||
or
|
||||
result = sensitiveFilenameOperation(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation on a filename that is likely to modify the security properties
|
||||
* of the corresponding file and may return an indication of success.
|
||||
*/
|
||||
FunctionCall sensitiveFilenameOperation(Expr path) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name = ["chmod", "chown"] and
|
||||
result.getArgument(0) = path
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation on a filename that returns information in the return value but
|
||||
* does not modify the corresponding file. For example, `access`.
|
||||
* A use of `access` (or similar) on a filename.
|
||||
*/
|
||||
FunctionCall accessCheck(Expr path) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name = ["access", "_access", "_waccess", "_access_s", "_waccess_s"]
|
||||
name = "access" or
|
||||
name = "_access" or
|
||||
name = "_waccess" or
|
||||
name = "_access_s" or
|
||||
name = "_waccess_s"
|
||||
) and
|
||||
path = result.getArgument(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation on a filename that returns information via a pointer argument
|
||||
* and any return value, but does not modify the corresponding file. For
|
||||
* example, `stat`.
|
||||
* A use of `stat` (or similar) on a filename.
|
||||
*/
|
||||
FunctionCall stat(Expr path, Expr buf) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name = ["stat", "lstat", "fstat"] or
|
||||
name = "stat" or
|
||||
name = "lstat" or
|
||||
name = "fstat" or
|
||||
name.matches("\\_stat%") or
|
||||
name.matches("\\_wstat%")
|
||||
) and
|
||||
@@ -78,7 +77,7 @@ FunctionCall stat(Expr path, Expr buf) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` refers to `source`, either by being the same or by
|
||||
* Holds if `use` points to `source`, either by being the same or by
|
||||
* one step of variable indirection.
|
||||
*/
|
||||
predicate referenceTo(Expr source, Expr use) {
|
||||
@@ -89,45 +88,36 @@ predicate referenceTo(Expr source, Expr use) {
|
||||
)
|
||||
}
|
||||
|
||||
from Expr check, Expr checkPath, FunctionCall use, Expr usePath
|
||||
from FunctionCall fc, Expr check, Expr checkUse, Expr opUse
|
||||
where
|
||||
// `check` looks like a check on a filename
|
||||
// checkUse looks like a check on a filename
|
||||
(
|
||||
(
|
||||
// either:
|
||||
// an access check
|
||||
check = accessCheck(checkPath)
|
||||
or
|
||||
// a stat
|
||||
check = stat(checkPath, _)
|
||||
or
|
||||
// access to a member variable on the stat buf
|
||||
// (morally, this should be a use-use pair, but it seems unlikely
|
||||
// that this variable will get reused in practice)
|
||||
exists(Expr call, Expr e, Variable v |
|
||||
call = stat(checkPath, e) and
|
||||
e.getAChild*().(VariableAccess).getTarget() = v and
|
||||
check.(VariableAccess).getTarget() = v and
|
||||
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
|
||||
)
|
||||
) and
|
||||
// `op` looks like an operation on a filename
|
||||
use = filenameOperation(usePath)
|
||||
// either:
|
||||
// an access check
|
||||
check = accessCheck(checkUse)
|
||||
or
|
||||
// a stat
|
||||
check = stat(checkUse, _)
|
||||
or
|
||||
// another filename operation (null pointers can indicate errors)
|
||||
check = filenameOperation(checkPath) and
|
||||
// `op` looks like a sensitive operation on a filename
|
||||
use = sensitiveFilenameOperation(usePath)
|
||||
check = filenameOperation(checkUse)
|
||||
or
|
||||
// access to a member variable on the stat buf
|
||||
// (morally, this should be a use-use pair, but it seems unlikely
|
||||
// that this variable will get reused in practice)
|
||||
exists(Variable buf | exists(stat(checkUse, buf.getAnAccess())) |
|
||||
check.(VariableAccess).getQualifier() = buf.getAnAccess()
|
||||
)
|
||||
) and
|
||||
// `checkPath` and `usePath` refer to the same SSA variable
|
||||
exists(SsaDefinition def, StackVariable v |
|
||||
def.getAUse(v) = checkPath and def.getAUse(v) = usePath
|
||||
) and
|
||||
// the return value of `check` is used (possibly with one step of
|
||||
// variable indirection) in a guard which controls `use`
|
||||
// checkUse and opUse refer to the same SSA variable
|
||||
exists(SsaDefinition def, StackVariable v | def.getAUse(v) = checkUse and def.getAUse(v) = opUse) and
|
||||
// opUse looks like an operation on a filename
|
||||
fc = filenameOperation(opUse) and
|
||||
// the return value of check is used (possibly with one step of
|
||||
// variable indirection) in a guard which controls fc
|
||||
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
|
||||
guard.controls(use.(ControlFlowNode).getBasicBlock(), _)
|
||||
guard.controls(fc.(ControlFlowNode).getBasicBlock(), _)
|
||||
)
|
||||
select use,
|
||||
select fc,
|
||||
"The $@ being operated upon was previously $@, but the underlying file may have been changed since then.",
|
||||
usePath, "filename", check, "checked"
|
||||
opUse, "filename", check, "checked"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.8
|
||||
* @opaque-id SM02313
|
||||
* @id cpp/conditionally-uninitialized-variable
|
||||
* @tags security
|
||||
* external/cwe/cwe-457
|
||||
|
||||
@@ -182,7 +182,7 @@ class ThrowingAllocator extends Function {
|
||||
// 3. the allocator isn't marked with `throw()` or `noexcept`.
|
||||
not exists(this.getBlock()) and
|
||||
not exists(Parameter p | p = this.getAParameter() |
|
||||
p.getUnspecifiedType().stripType() instanceof NoThrowType
|
||||
p.getUnspecifiedType() instanceof NoThrowType
|
||||
) and
|
||||
not this.isNoExcept() and
|
||||
not this.isNoThrow()
|
||||
|
||||
@@ -26,7 +26,7 @@ can use their own storage.</p>
|
||||
<p>Similarly replace calls to <code>localtime</code> with
|
||||
<code>localtime_r</code>, calls to <code>ctime</code> with
|
||||
<code>ctime_r</code> and calls to <code>asctime</code> with
|
||||
<code>asctime_r</code> (if those functions exist on your platform).</p>
|
||||
<code>asctime_r</code>.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 10.0
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @id cpp/potentially-dangerous-function
|
||||
* @tags reliability
|
||||
* security
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- description: Standard Code Scanning queries for C and C++
|
||||
- queries: .
|
||||
- qlpack: codeql-cpp
|
||||
- apply: code-scanning-selectors.yml
|
||||
from: codeql/suite-helpers
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql/cpp-queries
|
||||
from: codeql-cpp
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
- description: Standard LGTM queries for C/C++, including ones not displayed by default
|
||||
- queries: .
|
||||
- qlpack: codeql-cpp
|
||||
- apply: lgtm-selectors.yml
|
||||
from: codeql/suite-helpers
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql/cpp-queries
|
||||
from: codeql-cpp
|
||||
# These are only for IDE use.
|
||||
- exclude:
|
||||
tags contain:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- description: Standard LGTM queries for C/C++
|
||||
- apply: codeql-suites/cpp-lgtm-full.qls
|
||||
- apply: lgtm-displayed-only.yml
|
||||
from: codeql/suite-helpers
|
||||
from: codeql-suite-helpers
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- description: Security-and-quality queries for C and C++
|
||||
- queries: .
|
||||
- qlpack: codeql-cpp
|
||||
- apply: security-and-quality-selectors.yml
|
||||
from: codeql/suite-helpers
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql/cpp-queries
|
||||
from: codeql-cpp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- description: Security-extended queries for C and C++
|
||||
- queries: .
|
||||
- qlpack: codeql-cpp
|
||||
- apply: security-extended-selectors.yml
|
||||
from: codeql/suite-helpers
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql/cpp-queries
|
||||
from: codeql-cpp
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <pqxx/pqxx>
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
|
||||
if (argc != 2) {
|
||||
throw std::runtime_error("Give me a string!");
|
||||
}
|
||||
|
||||
pqxx::connection c;
|
||||
pqxx::work w(c);
|
||||
|
||||
// BAD
|
||||
char *userName = argv[1];
|
||||
char query1[1000] = {0};
|
||||
sprintf(query1, "SELECT UID FROM USERS where name = \"%s\"", userName);
|
||||
pqxx::row r = w.exec1(query1);
|
||||
w.commit();
|
||||
std::cout << r[0].as<int>() << std::endl;
|
||||
|
||||
// GOOD
|
||||
pqxx::result r2 = w.exec("SELECT " + w.quote(argv[1]));
|
||||
w.commit();
|
||||
std::cout << r2[0][0].c_str() << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The code passes user input as part of a SQL query without escaping special elements.
|
||||
It generates a SQL query to Postgres using <code>sprintf</code>,
|
||||
with the user-supplied data directly passed as an argument
|
||||
to <code>sprintf</code>. This leaves the code vulnerable to attack by SQL Injection.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Use a library routine to escape characters in the user-supplied
|
||||
string before converting it to SQL. Use <code>esc</code> and <code>quote</code> pqxx library functions.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="SqlPqxxTainted.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>MSDN Library: <a href="https://docs.microsoft.com/en-us/sql/relational-databases/security/sql-injection">SQL Injection</a>.</li>
|
||||
|
||||
|
||||
<!-- LocalWords: SQL CWE
|
||||
-->
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
113
cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql
Normal file
113
cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @name Uncontrolled data in SQL query to Postgres
|
||||
* @description Including user-supplied data in a SQL query to Postgres
|
||||
* without neutralizing special elements can make code
|
||||
* vulnerable to SQL Injection.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id cpp/sql-injection-via-pqxx
|
||||
* @tags security
|
||||
* external/cwe/cwe-089
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
predicate pqxxTransationClassNames(string className, string namespace) {
|
||||
namespace = "pqxx" and
|
||||
className in [
|
||||
"dbtransaction", "nontransaction", "basic_robusttransaction", "robusttransaction",
|
||||
"subtransaction", "transaction", "basic_transaction", "transaction_base", "work"
|
||||
]
|
||||
}
|
||||
|
||||
predicate pqxxConnectionClassNames(string className, string namespace) {
|
||||
namespace = "pqxx" and
|
||||
className in ["connection_base", "basic_connection", "connection"]
|
||||
}
|
||||
|
||||
predicate pqxxTransactionSqlArgument(string function, int arg) {
|
||||
function = "exec" and arg = 0
|
||||
or
|
||||
function = "exec0" and arg = 0
|
||||
or
|
||||
function = "exec1" and arg = 0
|
||||
or
|
||||
function = "exec_n" and arg = 1
|
||||
or
|
||||
function = "exec_params" and arg = 0
|
||||
or
|
||||
function = "exec_params0" and arg = 0
|
||||
or
|
||||
function = "exec_params1" and arg = 0
|
||||
or
|
||||
function = "exec_params_n" and arg = 1
|
||||
or
|
||||
function = "query_value" and arg = 0
|
||||
or
|
||||
function = "stream" and arg = 0
|
||||
}
|
||||
|
||||
predicate pqxxConnectionSqlArgument(string function, int arg) { function = "prepare" and arg = 1 }
|
||||
|
||||
Expr getPqxxSqlArgument() {
|
||||
exists(FunctionCall fc, Expr e, int argIndex, UserType t |
|
||||
// examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'.
|
||||
e = fc.getQualifier() and
|
||||
// to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior
|
||||
// and return pointer to a connection/transation object
|
||||
e.getType().refersTo(t) and
|
||||
// transaction exec and connection prepare variations
|
||||
(
|
||||
pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) and
|
||||
pqxxTransactionSqlArgument(fc.getTarget().getName(), argIndex)
|
||||
or
|
||||
pqxxConnectionClassNames(t.getName(), t.getNamespace().getName()) and
|
||||
pqxxConnectionSqlArgument(fc.getTarget().getName(), argIndex)
|
||||
) and
|
||||
result = fc.getArgument(argIndex)
|
||||
)
|
||||
}
|
||||
|
||||
predicate pqxxEscapeArgument(string function, int arg) {
|
||||
arg = 0 and
|
||||
function in ["esc", "esc_raw", "quote", "quote_raw", "quote_name", "quote_table", "esc_like"]
|
||||
}
|
||||
|
||||
predicate isEscapedPqxxArgument(Expr argExpr) {
|
||||
exists(FunctionCall fc, Expr e, int argIndex, UserType t |
|
||||
// examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'.
|
||||
e = fc.getQualifier() and
|
||||
// to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior
|
||||
// and return pointer to a connection/transation object
|
||||
e.getType().refersTo(t) and
|
||||
// transaction and connection escape functions
|
||||
(
|
||||
pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) or
|
||||
pqxxConnectionClassNames(t.getName(), t.getNamespace().getName())
|
||||
) and
|
||||
pqxxEscapeArgument(fc.getTarget().getName(), argIndex) and
|
||||
// is escaped arg == argExpr
|
||||
argExpr = fc.getArgument(argIndex)
|
||||
)
|
||||
}
|
||||
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "SqlPqxxTainted" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isUserInput(source.asExpr(), _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = getPqxxSqlArgument() }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { isEscapedPqxxArgument(node.asExpr()) }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config, string taintCause
|
||||
where
|
||||
config.hasFlowPath(source, sink) and
|
||||
isUserInput(source.getNode().asExpr(), taintCause)
|
||||
select sink, source, sink, "This argument to a SQL query function is derived from $@", source,
|
||||
"user input (" + taintCause + ")"
|
||||
@@ -1,34 +0,0 @@
|
||||
...
|
||||
int i1;
|
||||
char c1;
|
||||
...
|
||||
if((c1<50)&&(c>10))
|
||||
switch(c1){
|
||||
case 300: // BAD: the code will not be executed
|
||||
...
|
||||
if((i1<5)&&(i1>0))
|
||||
switch(i1){ // BAD
|
||||
case 21: // BAD: the code will not be executed
|
||||
...
|
||||
switch(c1){
|
||||
...
|
||||
dafault: // BAD: maybe it will be right `default`
|
||||
...
|
||||
}
|
||||
|
||||
...
|
||||
switch(c1){
|
||||
i1=c1*2; // BAD: the code will not be executed
|
||||
case 12:
|
||||
...
|
||||
switch(c1){ // GOOD
|
||||
case 12:
|
||||
break;
|
||||
case 10:
|
||||
break;
|
||||
case 9:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
...
|
||||
@@ -1,24 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>A mismatch between conditionals and <code>switch</code> cases can lead to control-flow violations (CWE-691) where the developer either does not handle all combinations of conditions or unintentionally created dead code (CWE-561).</p>
|
||||
|
||||
|
||||
</overview>
|
||||
|
||||
<example>
|
||||
<p>The following example demonstrates fallacious and fixed ways of using a <code>switch</code> statement.</p>
|
||||
<sample src="FindIncorrectlyUsedSwitch.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">MSC12-C. Detect and remove code that has no effect or is never executed</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,153 +0,0 @@
|
||||
/**
|
||||
* @name Incorrect switch statement
|
||||
* @description --Finding places the dangerous use of a switch.
|
||||
* --For example, when the range of values for a condition does not cover all of the selection values..
|
||||
* @kind problem
|
||||
* @id cpp/operator-find-incorrectly-used-switch
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-561
|
||||
* external/cwe/cwe-691
|
||||
* external/cwe/cwe-478
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
/** Holds if the range contains no boundary values. */
|
||||
predicate isRealRange(Expr exp) {
|
||||
upperBound(exp).toString() != "18446744073709551616" and
|
||||
upperBound(exp).toString() != "9223372036854775807" and
|
||||
upperBound(exp).toString() != "4294967295" and
|
||||
upperBound(exp).toString() != "Infinity" and
|
||||
upperBound(exp).toString() != "NaN" and
|
||||
lowerBound(exp).toString() != "-9223372036854775808" and
|
||||
lowerBound(exp).toString() != "-4294967296" and
|
||||
lowerBound(exp).toString() != "-Infinity" and
|
||||
lowerBound(exp).toString() != "NaN" and
|
||||
upperBound(exp) != 2147483647 and
|
||||
upperBound(exp) != 268435455 and
|
||||
upperBound(exp) != 33554431 and
|
||||
upperBound(exp) != 8388607 and
|
||||
upperBound(exp) != 65535 and
|
||||
upperBound(exp) != 32767 and
|
||||
upperBound(exp) != 255 and
|
||||
upperBound(exp) != 127 and
|
||||
upperBound(exp) != 63 and
|
||||
upperBound(exp) != 31 and
|
||||
upperBound(exp) != 15 and
|
||||
upperBound(exp) != 7 and
|
||||
lowerBound(exp) != -2147483648 and
|
||||
lowerBound(exp) != -268435456 and
|
||||
lowerBound(exp) != -33554432 and
|
||||
lowerBound(exp) != -8388608 and
|
||||
lowerBound(exp) != -65536 and
|
||||
lowerBound(exp) != -32768 and
|
||||
lowerBound(exp) != -128
|
||||
}
|
||||
|
||||
/** Holds if the range of values for the condition is less than the choices. */
|
||||
predicate isNotAllSelected(SwitchStmt swtmp) {
|
||||
not swtmp.getExpr().isConstant() and
|
||||
exists(int i |
|
||||
i != 0 and
|
||||
(
|
||||
i = lowerBound(swtmp.getASwitchCase().getExpr()) and
|
||||
upperBound(swtmp.getExpr()) < i
|
||||
or
|
||||
(
|
||||
i = upperBound(swtmp.getASwitchCase().getExpr()) or
|
||||
i = upperBound(swtmp.getASwitchCase().getEndExpr())
|
||||
) and
|
||||
lowerBound(swtmp.getExpr()) > i
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the range of values for the condition is greater than the selection. */
|
||||
predicate isConditionBig(SwitchStmt swtmp) {
|
||||
not swtmp.hasDefaultCase() and
|
||||
not exists(int iu, int il |
|
||||
(
|
||||
iu = upperBound(swtmp.getASwitchCase().getExpr()) or
|
||||
iu = upperBound(swtmp.getASwitchCase().getEndExpr())
|
||||
) and
|
||||
upperBound(swtmp.getExpr()) = iu and
|
||||
(
|
||||
il = lowerBound(swtmp.getASwitchCase().getExpr()) or
|
||||
il = lowerBound(swtmp.getASwitchCase().getEndExpr())
|
||||
) and
|
||||
lowerBound(swtmp.getExpr()) = il
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if there are labels inside the block with names similar to `default` or `case`. */
|
||||
predicate isWrongLableName(SwitchStmt swtmp) {
|
||||
not swtmp.hasDefaultCase() and
|
||||
exists(LabelStmt lb |
|
||||
(
|
||||
(
|
||||
lb.getName().charAt(0) = "d" or
|
||||
lb.getName().charAt(0) = "c"
|
||||
) and
|
||||
(
|
||||
lb.getName().charAt(1) = "e" or
|
||||
lb.getName().charAt(1) = "a"
|
||||
) and
|
||||
(
|
||||
lb.getName().charAt(2) = "f" or
|
||||
lb.getName().charAt(2) = "s"
|
||||
)
|
||||
) and
|
||||
lb.getEnclosingStmt().getParentStmt*() = swtmp.getStmt() and
|
||||
not exists(GotoStmt gs | gs.getName() = lb.getName())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the block contains code before the first `case`. */
|
||||
predicate isCodeBeforeCase(SwitchStmt swtmp) {
|
||||
exists(Expr exp |
|
||||
exp.getEnclosingStmt().getParentStmt*() = swtmp.getStmt() and
|
||||
not exists(Loop lp |
|
||||
exp.getEnclosingStmt().getParentStmt*() = lp and
|
||||
lp.getEnclosingStmt().getParentStmt*() = swtmp.getStmt()
|
||||
) and
|
||||
not exists(Stmt sttmp, SwitchCase sctmp |
|
||||
sttmp = swtmp.getASwitchCase().getAStmt() and
|
||||
sctmp = swtmp.getASwitchCase() and
|
||||
(
|
||||
exp.getEnclosingStmt().getParentStmt*() = sttmp or
|
||||
exp.getEnclosingStmt() = sctmp
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from SwitchStmt sw, string msg
|
||||
where
|
||||
isRealRange(sw.getExpr()) and
|
||||
lowerBound(sw.getExpr()) != upperBound(sw.getExpr()) and
|
||||
lowerBound(sw.getExpr()) != 0 and
|
||||
not exists(Expr cexp |
|
||||
cexp = sw.getASwitchCase().getExpr() and not isRealRange(cexp)
|
||||
or
|
||||
cexp = sw.getASwitchCase().getEndExpr() and not isRealRange(cexp)
|
||||
) and
|
||||
not exists(Expr exptmp |
|
||||
exptmp = sw.getExpr().getAChild*() and
|
||||
not exptmp.isConstant() and
|
||||
not isRealRange(exptmp)
|
||||
) and
|
||||
(sw.getASwitchCase().terminatesInBreakStmt() or sw.getASwitchCase().terminatesInReturnStmt()) and
|
||||
(
|
||||
isNotAllSelected(sw) and msg = "The range of condition values is less than the selection."
|
||||
or
|
||||
isConditionBig(sw) and msg = "The range of condition values is wider than the choices."
|
||||
)
|
||||
or
|
||||
isWrongLableName(sw) and msg = "Possibly erroneous label name."
|
||||
or
|
||||
isCodeBeforeCase(sw) and msg = "Code before case will not be executed."
|
||||
select sw, msg
|
||||
@@ -1,13 +0,0 @@
|
||||
...
|
||||
fs = socket(AF_UNIX, SOCK_STREAM, 0)
|
||||
...
|
||||
close(fs);
|
||||
fs = -1; // GOOD
|
||||
...
|
||||
|
||||
...
|
||||
fs = socket(AF_UNIX, SOCK_STREAM, 0)
|
||||
...
|
||||
close(fs);
|
||||
if(fs) close(fs); // BAD
|
||||
...
|
||||
@@ -1,26 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Double release of the descriptor can lead to a crash of the program. Requires the attention of developers.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>We recommend that you exclude situations of possible double release.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates an erroneous and corrected use of descriptor deallocation.</p>
|
||||
<sample src="DoubleRelease.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO46-C.+Do+not+access+a+closed+file">FIO46-C. Do not access a closed file</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,142 +0,0 @@
|
||||
/**
|
||||
* @name Errors When Double Release
|
||||
* @description Double release of the descriptor can lead to a crash of the program.
|
||||
* @kind problem
|
||||
* @id cpp/double-release
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-675
|
||||
* external/cwe/cwe-666
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.File
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
|
||||
/**
|
||||
* A function call that potentially does not return (such as `exit`).
|
||||
*/
|
||||
class CallMayNotReturn extends FunctionCall {
|
||||
CallMayNotReturn() {
|
||||
// call that is known to not return
|
||||
not exists(this.(ControlFlowNode).getASuccessor())
|
||||
or
|
||||
// call to another function that may not return
|
||||
exists(CallMayNotReturn exit | getTarget() = exit.getEnclosingFunction())
|
||||
or
|
||||
exists(ThrowExpr tex | tex = this.(ControlFlowNode).getASuccessor())
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if there are no assignment expressions to the function argument. */
|
||||
pragma[inline]
|
||||
predicate checkChangeVariable(FunctionCall fc0, ControlFlowNode fc1, ControlFlowNode fc2) {
|
||||
not exists(Expr exptmp |
|
||||
(
|
||||
exptmp = fc0.getArgument(0).(VariableAccess).getTarget().getAnAssignedValue() or
|
||||
exptmp.(AddressOfExpr).getOperand() =
|
||||
fc0.getArgument(0).(VariableAccess).getTarget().getAnAccess()
|
||||
) and
|
||||
exptmp = fc1.getASuccessor*() and
|
||||
exptmp = fc2.getAPredecessor*()
|
||||
) and
|
||||
(
|
||||
(
|
||||
not fc0.getArgument(0) instanceof PointerFieldAccess and
|
||||
not fc0.getArgument(0) instanceof ValueFieldAccess
|
||||
or
|
||||
fc0.getArgument(0).(VariableAccess).getQualifier() instanceof ThisExpr
|
||||
)
|
||||
or
|
||||
not exists(Expr exptmp |
|
||||
(
|
||||
exptmp =
|
||||
fc0.getArgument(0)
|
||||
.(VariableAccess)
|
||||
.getQualifier()
|
||||
.(VariableAccess)
|
||||
.getTarget()
|
||||
.getAnAssignedValue() or
|
||||
exptmp.(AddressOfExpr).getOperand() =
|
||||
fc0.getArgument(0)
|
||||
.(VariableAccess)
|
||||
.getQualifier()
|
||||
.(VariableAccess)
|
||||
.getTarget()
|
||||
.getAnAccess()
|
||||
) and
|
||||
exptmp = fc1.getASuccessor*() and
|
||||
exptmp = fc2.getAPredecessor*()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the underlying expression is a call to the close function. Provided that the function parameter does not change after the call. */
|
||||
predicate closeReturn(FunctionCall fc) {
|
||||
fcloseCall(fc, _) and
|
||||
checkChangeVariable(fc, fc, fc.getEnclosingFunction())
|
||||
}
|
||||
|
||||
/** Holds if the underlying expression is a call to the close function. Provided that the function parameter does not change before the call. */
|
||||
predicate closeWithoutChangeBefore(FunctionCall fc) {
|
||||
fcloseCall(fc, _) and
|
||||
checkChangeVariable(fc, fc.getEnclosingFunction().getEntryPoint(), fc)
|
||||
}
|
||||
|
||||
/** Holds, if a sequential call of the specified functions is possible, via a higher-level function call. */
|
||||
predicate callInOtherFunctions(FunctionCall fc, FunctionCall fc1) {
|
||||
exists(FunctionCall fec1, FunctionCall fec2 |
|
||||
fc.getEnclosingFunction() != fc1.getEnclosingFunction() and
|
||||
fec1 = fc.getEnclosingFunction().getACallToThisFunction() and
|
||||
fec2 = fc1.getEnclosingFunction().getACallToThisFunction() and
|
||||
fec1.getASuccessor*() = fec2 and
|
||||
checkChangeVariable(fc, fec1, fec2)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if successive calls to close functions are possible. */
|
||||
predicate interDoubleCloseFunctions(FunctionCall fc, FunctionCall fc1) {
|
||||
fcloseCall(fc, _) and
|
||||
fcloseCall(fc1, _) and
|
||||
fc != fc1 and
|
||||
fc.getASuccessor*() = fc1 and
|
||||
checkChangeVariable(fc, fc, fc1)
|
||||
}
|
||||
|
||||
/** Holds if the first arguments of the two functions are similar. */
|
||||
predicate similarArguments(FunctionCall fc, FunctionCall fc1) {
|
||||
globalValueNumber(fc.getArgument(0)) = globalValueNumber(fc1.getArgument(0))
|
||||
or
|
||||
fc.getArgument(0).(VariableAccess).getTarget() = fc1.getArgument(0).(VariableAccess).getTarget() and
|
||||
(
|
||||
not fc.getArgument(0) instanceof PointerFieldAccess and
|
||||
not fc.getArgument(0) instanceof ValueFieldAccess
|
||||
or
|
||||
fc.getArgument(0).(VariableAccess).getQualifier() instanceof ThisExpr
|
||||
)
|
||||
or
|
||||
fc.getArgument(0).(VariableAccess).getTarget() = fc1.getArgument(0).(VariableAccess).getTarget() and
|
||||
(
|
||||
fc.getArgument(0) instanceof PointerFieldAccess or
|
||||
fc.getArgument(0) instanceof ValueFieldAccess
|
||||
) and
|
||||
hashCons(fc.getArgument(0)) = hashCons(fc1.getArgument(0))
|
||||
}
|
||||
|
||||
from FunctionCall fc, FunctionCall fc1
|
||||
where
|
||||
not exists(CallMayNotReturn fctmp | fctmp = fc.getASuccessor*()) and
|
||||
not exists(IfStmt ifs | ifs.getCondition().getAChild*() = fc) and
|
||||
(
|
||||
// detecting a repeated call situation within one function
|
||||
closeReturn(fc) and
|
||||
closeWithoutChangeBefore(fc1) and
|
||||
callInOtherFunctions(fc, fc1)
|
||||
or
|
||||
// detection of repeated call in different functions
|
||||
interDoubleCloseFunctions(fc, fc1)
|
||||
) and
|
||||
similarArguments(fc, fc1)
|
||||
select fc, "Second call to the $@ function is possible.", fc1, fc1.getTarget().getName()
|
||||
@@ -1,9 +0,0 @@
|
||||
...
|
||||
throw ("my exception!",546); // BAD
|
||||
...
|
||||
throw errorFunc("my exception!",546); // GOOD
|
||||
...
|
||||
std::runtime_error("msg error"); // BAD
|
||||
...
|
||||
throw std::runtime_error("msg error"); // GOOD
|
||||
...
|
||||
@@ -1,23 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Finding places for the dangerous use of exceptions.</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<example>
|
||||
<p>The following example demonstrates erroneous and fixed methods for using exceptions.</p>
|
||||
<sample src="FindIncorrectlyUsedExceptions.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT CPP Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/DCL57-CPP.+Do+not+let+exceptions+escape+from+destructors+or+deallocation+functions">DCL57-CPP. Do not let exceptions escape from destructors or deallocation functions</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* @name Operator Find Incorrectly Used Exceptions
|
||||
* @description --Finding places for the dangerous use of exceptions.
|
||||
* @kind problem
|
||||
* @id cpp/operator-find-incorrectly-used-exceptions
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-703
|
||||
* external/cwe/cwe-248
|
||||
* external/cwe/cwe-390
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from FunctionCall fc, string msg
|
||||
where
|
||||
exists(ThrowExpr texp |
|
||||
texp.getEnclosingFunction() = fc.getTarget() and
|
||||
(
|
||||
fc.getTarget().hasGlobalOrStdName("DllMain") and
|
||||
not exists(TryStmt ts |
|
||||
texp.getEnclosingStmt().getParentStmt*() = ts.getStmt() and
|
||||
not ts.getACatchClause().isEmpty()
|
||||
) and
|
||||
msg = "DllMain contains an exeption not wrapped in a try..catch block."
|
||||
or
|
||||
texp.getExpr().isParenthesised() and
|
||||
texp.getExpr().(CommaExpr).getLeftOperand().isConstant() and
|
||||
texp.getExpr().(CommaExpr).getRightOperand().isConstant() and
|
||||
msg = "There is an exception in the function that requires your attention."
|
||||
)
|
||||
)
|
||||
or
|
||||
fc.getTarget() instanceof Constructor and
|
||||
(
|
||||
fc.getTargetType().(Class).getABaseClass+().hasGlobalOrStdName("exception") or
|
||||
fc.getTargetType().(Class).getABaseClass+().hasGlobalOrStdName("CException")
|
||||
) and
|
||||
not fc.isInMacroExpansion() and
|
||||
not exists(ThrowExpr texp | fc.getEnclosingStmt() = texp.getEnclosingStmt()) and
|
||||
not exists(FunctionCall fctmp | fctmp.getAnArgument() = fc) and
|
||||
not fc instanceof ConstructorDirectInit and
|
||||
not fc.getEnclosingStmt() instanceof DeclStmt and
|
||||
not fc instanceof ConstructorDelegationInit and
|
||||
not fc.getParent() instanceof Initializer and
|
||||
not fc.getParent() instanceof AllocationExpr and
|
||||
msg = "This object does not generate an exception."
|
||||
select fc, msg
|
||||
@@ -1,12 +0,0 @@
|
||||
intA = ++intA + 1; // BAD: undefined behavior when changing variable `intA`
|
||||
...
|
||||
intA++;
|
||||
intA = intA + 1; // GOOD: correct design
|
||||
...
|
||||
char * buff;
|
||||
...
|
||||
if(funcAdd(buff)+fucDel(buff)>0) return 1; // BAD: undefined behavior when calling functions to change the `buff` variable
|
||||
...
|
||||
intA = funcAdd(buff);
|
||||
intB = funcDel(buff);
|
||||
if(intA+intB>0) return 1; // GOOD: correct design
|
||||
@@ -1,28 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>In some situations, the code constructs used may be executed in the wrong order in which the developer designed them. For example, if you call multiple functions as part of a single expression, and the functions have the ability to modify a shared resource, then the sequence in which the resource is changed can be unpredictable. These code snippets look suspicious and require the developer's attention.</p>
|
||||
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend that you use more guaranteed, in terms of sequence of execution, coding techniques.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates sections of code with insufficient execution sequence definition.</p>
|
||||
<sample src="UndefinedOrImplementationDefinedBehavior.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CWE Common Weakness Enumeration:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP10-C.+Do+not+depend+on+the+order+of+evaluation+of+subexpressions+or+the+order+in+which+side+effects+take+place"> EXP10-C. Do not depend on the order of evaluation of subexpressions or the order in which side effects take place</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,167 +0,0 @@
|
||||
/**
|
||||
* @name Errors Of Undefined Program Behavior
|
||||
* @description --In some situations, the code constructs used may be executed in the wrong order in which the developer designed them.
|
||||
* --For example, if you call multiple functions as part of a single expression, and the functions have the ability to modify a shared resource, then the sequence in which the resource is changed can be unpredictable.
|
||||
* --These code snippets look suspicious and require the developer's attention.
|
||||
* @kind problem
|
||||
* @id cpp/errors-of-undefined-program-behavior
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-758
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* Threatening expressions of undefined behavior.
|
||||
*/
|
||||
class ExpressionsOfTheSameLevel extends Expr {
|
||||
Expr exp2;
|
||||
|
||||
ExpressionsOfTheSameLevel() {
|
||||
this != exp2 and
|
||||
this.getParent() = exp2.getParent()
|
||||
}
|
||||
|
||||
/** Holds if the underlying expression is a function call. */
|
||||
predicate expressionCall() {
|
||||
this instanceof FunctionCall and
|
||||
exp2.getAChild*() instanceof FunctionCall and
|
||||
not this.getParent() instanceof Operator and
|
||||
not this.(FunctionCall).hasQualifier()
|
||||
}
|
||||
|
||||
/** Holds if the underlying expression is a call to a function to free resources. */
|
||||
predicate existsCloseOrFreeCall() {
|
||||
(
|
||||
globalValueNumber(this.(FunctionCall).getAnArgument()) =
|
||||
globalValueNumber(exp2.getAChild*().(FunctionCall).getAnArgument()) or
|
||||
hashCons(this.(FunctionCall).getAnArgument()) =
|
||||
hashCons(exp2.getAChild*().(FunctionCall).getAnArgument())
|
||||
) and
|
||||
(
|
||||
this.(FunctionCall).getTarget().hasGlobalOrStdName("close") or
|
||||
this.(FunctionCall).getTarget().hasGlobalOrStdName("free") or
|
||||
this.(FunctionCall).getTarget().hasGlobalOrStdName("fclose")
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the arguments in the function can be changed. */
|
||||
predicate generalArgumentDerivedType() {
|
||||
exists(Parameter prt1, Parameter prt2, AssignExpr aet1, AssignExpr aet2, int i, int j |
|
||||
not this.(FunctionCall).getArgument(i).isConstant() and
|
||||
hashCons(this.(FunctionCall).getArgument(i)) =
|
||||
hashCons(exp2.getAChild*().(FunctionCall).getArgument(j)) and
|
||||
prt1 = this.(FunctionCall).getTarget().getParameter(i) and
|
||||
prt2 = exp2.getAChild*().(FunctionCall).getTarget().getParameter(j) and
|
||||
prt1.getType() instanceof DerivedType and
|
||||
(
|
||||
aet1 = this.(FunctionCall).getTarget().getEntryPoint().getASuccessor*() and
|
||||
(
|
||||
aet1.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() =
|
||||
prt1.getAnAccess().getTarget() or
|
||||
aet1.getLValue().(VariableAccess).getTarget() = prt1.getAnAccess().getTarget()
|
||||
)
|
||||
or
|
||||
exists(FunctionCall fc1 |
|
||||
fc1.getTarget().hasGlobalName("memcpy") and
|
||||
fc1.getArgument(0).(VariableAccess).getTarget() = prt1.getAnAccess().getTarget() and
|
||||
fc1 = this.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
)
|
||||
) and
|
||||
(
|
||||
aet2 = exp2.getAChild*().(FunctionCall).getTarget().getEntryPoint().getASuccessor*() and
|
||||
(
|
||||
aet2.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() =
|
||||
prt2.getAnAccess().getTarget() or
|
||||
aet2.getLValue().(VariableAccess).getTarget() = prt2.getAnAccess().getTarget()
|
||||
)
|
||||
or
|
||||
exists(FunctionCall fc1 |
|
||||
fc1.getTarget().hasGlobalName("memcpy") and
|
||||
fc1.getArgument(0).(VariableAccess).getTarget() = prt2.getAnAccess().getTarget() and
|
||||
fc1 = exp2.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if functions have a common global argument. */
|
||||
predicate generalGlobalArgument() {
|
||||
exists(Declaration dl, AssignExpr aet1, AssignExpr aet2 |
|
||||
dl instanceof GlobalVariable and
|
||||
(
|
||||
(
|
||||
aet1.getLValue().(Access).getTarget() = dl or
|
||||
aet1.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = dl
|
||||
) and
|
||||
aet1 = this.(FunctionCall).getTarget().getEntryPoint().getASuccessor*() and
|
||||
not aet1.getRValue().isConstant()
|
||||
or
|
||||
exists(FunctionCall fc1 |
|
||||
fc1.getTarget().hasGlobalName("memcpy") and
|
||||
fc1.getArgument(0).(VariableAccess).getTarget() = dl and
|
||||
fc1 = this.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
)
|
||||
) and
|
||||
(
|
||||
(
|
||||
aet2.getLValue().(Access).getTarget() = dl or
|
||||
aet2.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = dl
|
||||
) and
|
||||
aet2 = exp2.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
or
|
||||
exists(FunctionCall fc1 |
|
||||
fc1.getTarget().hasGlobalName("memcpy") and
|
||||
fc1.getArgument(0).(VariableAccess).getTarget() = dl and
|
||||
fc1 = exp2.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if sequence point is not present in expression. */
|
||||
predicate orderOfActionExpressions() {
|
||||
not this.getParent() instanceof BinaryLogicalOperation and
|
||||
not this.getParent() instanceof ConditionalExpr and
|
||||
not this.getParent() instanceof Loop and
|
||||
not this.getParent() instanceof CommaExpr
|
||||
}
|
||||
|
||||
/** Holds if expression is crement. */
|
||||
predicate dangerousCrementChanges() {
|
||||
hashCons(this.(CrementOperation).getOperand()) = hashCons(exp2.(CrementOperation).getOperand())
|
||||
or
|
||||
hashCons(this.(CrementOperation).getOperand()) = hashCons(exp2)
|
||||
or
|
||||
hashCons(this.(CrementOperation).getOperand()) = hashCons(exp2.(ArrayExpr).getArrayOffset())
|
||||
or
|
||||
hashCons(this.(Assignment).getLValue()) = hashCons(exp2.(Assignment).getLValue())
|
||||
or
|
||||
not this.getAChild*() instanceof Call and
|
||||
(
|
||||
hashCons(this.getAChild*().(CrementOperation).getOperand()) = hashCons(exp2) or
|
||||
hashCons(this.getAChild*().(CrementOperation).getOperand()) =
|
||||
hashCons(exp2.(Assignment).getLValue())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from ExpressionsOfTheSameLevel eots
|
||||
where
|
||||
eots.orderOfActionExpressions() and
|
||||
(
|
||||
eots.expressionCall() and
|
||||
(
|
||||
eots.generalArgumentDerivedType() or
|
||||
eots.generalGlobalArgument() or
|
||||
eots.existsCloseOrFreeCall()
|
||||
)
|
||||
or
|
||||
eots.dangerousCrementChanges()
|
||||
)
|
||||
select eots,
|
||||
"This expression may have undefined behavior, because the order of evaluation is not specified."
|
||||
@@ -1,7 +0,0 @@
|
||||
bool a=1,b=0,c=1,res;
|
||||
...
|
||||
res = a||b^c; // BAD: possible priority error `res==1`
|
||||
...
|
||||
res = a||(b^c); // GOOD: `res==1`
|
||||
...
|
||||
res = (a||b)^c; // GOOD: `res==0`
|
||||
@@ -1,28 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Find places of confusing use of logical and bitwise operations.</p>
|
||||
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend using parentheses to explicitly emphasize priority.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates fallacious and fixed methods of using logical and bitwise operations.</p>
|
||||
<sample src="OperatorPrecedenceLogicErrorWhenUseBitwiseOrLogicalOperations.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,195 +0,0 @@
|
||||
/**
|
||||
* @name Operator Precedence Logic Error When Use Bitwise Or Logical Operations
|
||||
* @description --Finding places to use bit and logical operations, without explicit priority allocation.
|
||||
* --For example, `a || b ^ c` and `(a || b) ^ c` give different results when `b` is zero.
|
||||
* @kind problem
|
||||
* @id cpp/operator-precedence-logic-error-when-use-bitwise-logical-operations
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @tags maintainability
|
||||
* readability
|
||||
* external/cwe/cwe-783
|
||||
* external/cwe/cwe-480
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
/** Holds if `exptmp` equals expression logical or followed by logical and. */
|
||||
predicate isLogicalOrAndExpr(LogicalOrExpr exptmp) {
|
||||
not exptmp.getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.getRightOperand().isParenthesised() and
|
||||
exptmp.getRightOperand() instanceof LogicalAndExpr
|
||||
}
|
||||
|
||||
/** Holds if `exptmp` equals expression logical or followed by bit operation. */
|
||||
predicate isLogicalOrandBitwise(Expr exptmp) {
|
||||
not exptmp.(LogicalOrExpr).getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.(LogicalOrExpr).getRightOperand().isParenthesised() and
|
||||
(
|
||||
exptmp.(LogicalOrExpr).getRightOperand().(BinaryBitwiseOperation).getLeftOperand().getType()
|
||||
instanceof BoolType and
|
||||
// The essence of these lines is to improve the quality of detection by eliminating the situation
|
||||
// of processing a logical type by bit operations. In fact, the predicate looks for a situation
|
||||
// when the left operand of a bit operation has a boolean type, which already suggests that the priority is not correct.
|
||||
// But if the right-hand operand is 0 or 1, then there is a possibility that the author intended so.
|
||||
not exptmp
|
||||
.(LogicalOrExpr)
|
||||
.getRightOperand()
|
||||
.(BinaryBitwiseOperation)
|
||||
.getRightOperand()
|
||||
.getValue() = "0" and
|
||||
not exptmp
|
||||
.(LogicalOrExpr)
|
||||
.getRightOperand()
|
||||
.(BinaryBitwiseOperation)
|
||||
.getRightOperand()
|
||||
.getValue() = "1"
|
||||
)
|
||||
or
|
||||
not exptmp.(LogicalAndExpr).getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.(LogicalAndExpr).getRightOperand().isParenthesised() and
|
||||
(
|
||||
exptmp.(LogicalAndExpr).getRightOperand().(BinaryBitwiseOperation).getLeftOperand().getType()
|
||||
instanceof BoolType and
|
||||
// Looking for a situation in which the right-hand operand of a bit operation is not limited to 0 or 1.
|
||||
// In this case, the logical operation will be performed with the result of a binary operation that is not a Boolean type.
|
||||
// In my opinion this indicates a priority error. after all, it will be quite difficult for a developer
|
||||
// to evaluate the conversion of the results of a bit operation to a boolean type.
|
||||
not exptmp
|
||||
.(LogicalAndExpr)
|
||||
.getRightOperand()
|
||||
.(BinaryBitwiseOperation)
|
||||
.getRightOperand()
|
||||
.getValue() = "0" and
|
||||
not exptmp
|
||||
.(LogicalAndExpr)
|
||||
.getRightOperand()
|
||||
.(BinaryBitwiseOperation)
|
||||
.getRightOperand()
|
||||
.getValue() = "1"
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `exptmp` equals expression bit operations in reverse priority order. */
|
||||
predicate isBitwiseandBitwise(Expr exptmp) {
|
||||
not exptmp.(BitwiseOrExpr).getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.(BitwiseOrExpr).getRightOperand().isParenthesised() and
|
||||
(
|
||||
exptmp.(BitwiseOrExpr).getRightOperand() instanceof BitwiseAndExpr or
|
||||
exptmp.(BitwiseOrExpr).getRightOperand() instanceof BitwiseXorExpr
|
||||
)
|
||||
or
|
||||
not exptmp.(BitwiseXorExpr).getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.(BitwiseXorExpr).getRightOperand().isParenthesised() and
|
||||
exptmp.(BitwiseXorExpr).getRightOperand() instanceof BitwiseAndExpr
|
||||
}
|
||||
|
||||
/** Holds if the range contains no boundary values. */
|
||||
predicate isRealRange(Expr exp) {
|
||||
upperBound(exp).toString() != "18446744073709551616" and
|
||||
upperBound(exp).toString() != "9223372036854775807" and
|
||||
upperBound(exp).toString() != "4294967295" and
|
||||
upperBound(exp).toString() != "Infinity" and
|
||||
upperBound(exp).toString() != "NaN" and
|
||||
lowerBound(exp).toString() != "-9223372036854775808" and
|
||||
lowerBound(exp).toString() != "-4294967296" and
|
||||
lowerBound(exp).toString() != "-Infinity" and
|
||||
lowerBound(exp).toString() != "NaN" and
|
||||
upperBound(exp) != 2147483647 and
|
||||
upperBound(exp) != 268435455 and
|
||||
upperBound(exp) != 33554431 and
|
||||
upperBound(exp) != 8388607 and
|
||||
upperBound(exp) != 65535 and
|
||||
upperBound(exp) != 32767 and
|
||||
upperBound(exp) != 255 and
|
||||
upperBound(exp) != 127 and
|
||||
lowerBound(exp) != -2147483648 and
|
||||
lowerBound(exp) != -268435456 and
|
||||
lowerBound(exp) != -33554432 and
|
||||
lowerBound(exp) != -8388608 and
|
||||
lowerBound(exp) != -65536 and
|
||||
lowerBound(exp) != -32768 and
|
||||
lowerBound(exp) != -128
|
||||
or
|
||||
lowerBound(exp) = 0 and
|
||||
upperBound(exp) = 1
|
||||
}
|
||||
|
||||
/** Holds if expressions are of different size or range */
|
||||
pragma[inline]
|
||||
predicate isDifferentSize(Expr exp1, Expr exp2, Expr exp3) {
|
||||
exp1.getType().getSize() = exp2.getType().getSize() and
|
||||
exp1.getType().getSize() != exp3.getType().getSize()
|
||||
or
|
||||
(
|
||||
isRealRange(exp1) and
|
||||
isRealRange(exp2) and
|
||||
isRealRange(exp3)
|
||||
) and
|
||||
upperBound(exp1).maximum(upperBound(exp2)) - upperBound(exp1).minimum(upperBound(exp2)) < 16 and
|
||||
lowerBound(exp1).maximum(lowerBound(exp2)) - lowerBound(exp1).minimum(lowerBound(exp2)) < 16 and
|
||||
(
|
||||
upperBound(exp1).maximum(upperBound(exp3)) - upperBound(exp1).minimum(upperBound(exp3)) > 256 or
|
||||
lowerBound(exp1).maximum(lowerBound(exp2)) - lowerBound(exp1).minimum(lowerBound(exp2)) > 256
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if different values of the expression obtained from the parameters of the predicate can be obtained. */
|
||||
pragma[inline]
|
||||
predicate isDifferentResults(
|
||||
Expr exp1, Expr exp2, Expr exp3, BinaryBitwiseOperation op1, BinaryBitwiseOperation op2
|
||||
) {
|
||||
(
|
||||
isRealRange(exp1) and
|
||||
isRealRange(exp2) and
|
||||
isRealRange(exp3)
|
||||
) and
|
||||
exists(int i1, int i2, int i3 |
|
||||
i1 in [lowerBound(exp1).floor() .. upperBound(exp1).floor()] and
|
||||
i2 in [lowerBound(exp2).floor() .. upperBound(exp2).floor()] and
|
||||
i3 in [lowerBound(exp3).floor() .. upperBound(exp3).floor()] and
|
||||
(
|
||||
op1 instanceof BitwiseOrExpr and
|
||||
op2 instanceof BitwiseAndExpr and
|
||||
i1.bitOr(i2).bitAnd(i3) != i2.bitAnd(i3).bitOr(i1)
|
||||
or
|
||||
op1 instanceof BitwiseOrExpr and
|
||||
op2 instanceof BitwiseXorExpr and
|
||||
i1.bitOr(i2).bitXor(i3) != i2.bitXor(i3).bitOr(i1)
|
||||
or
|
||||
op1 instanceof BitwiseXorExpr and
|
||||
op2 instanceof BitwiseAndExpr and
|
||||
i1.bitXor(i2).bitAnd(i3) != i2.bitAnd(i3).bitXor(i1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from Expr exp, string msg
|
||||
where
|
||||
isLogicalOrAndExpr(exp) and
|
||||
msg = "Logical AND has a higher priority."
|
||||
or
|
||||
isLogicalOrandBitwise(exp) and
|
||||
msg = "Binary operations have higher priority."
|
||||
or
|
||||
// Looking for a situation where the equality of the sizes of the first operands
|
||||
// might indicate that the developer planned to perform an operation between them.
|
||||
// However, the absence of parentheses means that the rightmost operation will be performed initially.
|
||||
isBitwiseandBitwise(exp) and
|
||||
isDifferentSize(exp.(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getRightOperand()) and
|
||||
msg = "Expression ranges do not match operation precedence."
|
||||
or
|
||||
// Looking for a out those expressions that, as a result of identifying the priority with parentheses,
|
||||
// will give different values. As a consequence, this piece of code was supposed to find errors associated
|
||||
// with possible outcomes of operations.
|
||||
isBitwiseandBitwise(exp) and
|
||||
isDifferentResults(exp.(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getRightOperand(),
|
||||
exp.(BinaryBitwiseOperation),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation)) and
|
||||
msg = "specify the priority with parentheses."
|
||||
select exp, msg
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @name unsigned to signed used in pointer arithmetic
|
||||
* @description finds unsigned to signed conversions used in pointer arithmetic, potentially causing an out-of-bound access
|
||||
* @id cpp/sign-conversion-pointer-arithmetic
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-787
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.security.Overflow
|
||||
|
||||
from FunctionCall call, Function f, Parameter p, DataFlow::Node sink, PointerArithmeticOperation pao
|
||||
where
|
||||
f = call.getTarget() and
|
||||
p = f.getAParameter() and
|
||||
p.getUnspecifiedType().(IntegralType).isSigned() and
|
||||
call.getArgument(p.getIndex()).getUnspecifiedType().(IntegralType).isUnsigned() and
|
||||
pao.getAnOperand() = sink.asExpr() and
|
||||
not exists(Operation a | guardedLesser(a, sink.asExpr())) and
|
||||
not exists(Operation b | guardedGreater(b, call.getArgument(p.getIndex()))) and
|
||||
not call.getArgument(p.getIndex()).isConstant() and
|
||||
DataFlow::localFlow(DataFlow::parameterNode(p), sink) and
|
||||
p.getUnspecifiedType().getSize() < 8
|
||||
select call,
|
||||
"This call: $@ passes an unsigned int to a function that requires a signed int: $@. And then used in pointer arithmetic: $@",
|
||||
call, call.toString(), f, f.toString(), sink, sink.toString()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user