Compare commits

..

19 Commits

Author SHA1 Message Date
Asger F
52a403cc62 JS: Address review comments 2024-04-12 11:35:10 +02:00
Asger F
9153a11412 Merge branch 'js/graph-export' into js/vea-hacking-models 2024-04-09 14:41:25 +02:00
Asger F
7b3810eb8f Merge branch 'js/endpoint-naming-expose-synthetic' into js/vea-hacking 2024-03-19 14:04:00 +01:00
Asger F
ae903abb4b JS: Expose whether an endpoint name is synthetic 2024-03-19 14:03:33 +01:00
Asger F
3cd4969499 WIP: Add NoPropStep and LoadAnyProp() 2024-03-12 13:01:39 +01:00
Asger F
ba86c93e67 Revert "JS: More aggressive tracking of objects with methods"
This reverts commit 5ed2e033f1.
2024-03-11 15:33:12 +01:00
Asger F
5ed2e033f1 JS: More aggressive tracking of objects with methods 2024-03-11 10:43:15 +01:00
Asger F
ebb744311f Merge branch 'js/call-graph-improvement2' into js/vea-hacking 2024-03-07 12:54:40 +01:00
Asger F
91a0181cfb JS: More implied receiver steps 2024-03-07 12:49:34 +01:00
Asger F
6ebebc131e JS: Add test case 2024-03-07 12:49:10 +01:00
Asger F
f546383cee JS: More implied receiver steps 2024-03-07 11:51:06 +01:00
Asger F
d9482441f0 Merge branch 'js/lift-cg-restriction' into js/vea-hacking 2024-03-06 11:42:55 +01:00
Asger F
941097b639 Update ModuleInterop.qll 2024-03-05 19:09:22 +01:00
Asger F
7ae28ceee0 More Module interop code 2024-03-04 15:46:55 +01:00
Asger F
5340a89107 JS: Remove allocation site restriction in CG 2024-03-01 21:36:29 +01:00
Asger F
c43856d8ea JS: Add steps to better handle module interop code 2024-03-01 21:30:04 +01:00
Asger F
af1382a6ca Merge branch 'js/summarised-tt-store-steps' into js/vea-hacking 2024-03-01 20:26:46 +01:00
Asger F
dc590756b5 Merge branch 'js/escaping-instance-detection' into js/vea-hacking 2024-02-29 11:19:31 +01:00
Asger F
34b48f51de Merge branch 'js/summarised-tt-store-steps' into js/vea-hacking 2024-02-29 10:30:15 +01:00
3372 changed files with 325317 additions and 109255 deletions

View File

@@ -11,15 +11,7 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub
build --repo_env=CC=clang --repo_env=CXX=clang++
build:linux --cxxopt=-std=c++20
# we currently cannot built the swift extractor for ARM
build:macos --cxxopt=-std=c++20 --copt=-arch --copt=x86_64 --linkopt=-arch --linkopt=x86_64
build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
# this requires developer mode, but is required to have pack installer functioning
startup --windows_enable_symlinks
common --enable_runfiles
common --registry=file:///%workspace%/misc/bazel/registry
common --registry=https://bcr.bazel.build
try-import %workspace%/local.bazelrc

View File

@@ -1,4 +0,0 @@
# this file should contain bazel settings required to build things from `semmle-code`
common --registry=file:///%workspace%/ql/misc/bazel/registry
common --registry=https://bcr.bazel.build

View File

@@ -1 +1 @@
7.1.2
7.1.0

10
.gitattributes vendored
View File

@@ -67,14 +67,14 @@ go/extractor/opencsv/CSVReader.java -text
# for those testing dbscheme files.
*/ql/lib/upgrades/initial/*.dbscheme -text
# Generated test files - these are synced from the standard JavaScript libraries using
# `javascript/ql/experimental/adaptivethreatmodeling/test/update_endpoint_test_files.py`.
javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/**/*.js linguist-generated=true -merge
javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/**/*.ts linguist-generated=true -merge
# Auto-generated modeling for Python
python/ql/lib/semmle/python/frameworks/data/internal/subclass-capture/*.yml linguist-generated=true
# auto-generated bazel lock file
ruby/extractor/cargo-bazel-lock.json linguist-generated=true
ruby/extractor/cargo-bazel-lock.json -merge
# auto-generated files for the C# build
csharp/paket.lock linguist-generated=true
# needs eol=crlf, as `paket` touches this file and saves it als crlf
csharp/.paket/Paket.Restore.targets linguist-generated=true eol=crlf

5
.github/labeler.yml vendored
View File

@@ -15,7 +15,7 @@ Java:
- change-notes/**/*java.*
JS:
- any: [ 'javascript/**/*' ]
- any: [ 'javascript/**/*', '!javascript/ql/experimental/adaptivethreatmodeling/**/*' ]
- change-notes/**/*javascript*
Kotlin:
@@ -46,3 +46,6 @@ documentation:
# Since these are all shared files that need to be synced, just pick _one_ copy of each.
"DataFlow Library":
- "shared/dataflow/**/*"
"ATM":
- javascript/ql/experimental/adaptivethreatmodeling/**/*

View File

@@ -1,28 +0,0 @@
name: Check bazel formatting
on:
pull_request:
paths:
- "**.bazel"
- "**.bzl"
branches:
- main
- "rc/*"
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check bazel formatting
uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507
with:
extra_args: >
buildifier --all-files 2>&1 ||
(
echo -e "In order to format all bazel files, please run:\n bazel run //misc/bazel:buildifier"; exit 1
)

View File

@@ -56,9 +56,7 @@ jobs:
# uses a compiled language
- run: |
cd csharp
dotnet tool restore
dotnet build .
dotnet build csharp
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@main

View File

@@ -81,11 +81,10 @@ jobs:
dotnet-version: 8.0.101
- name: Extractor unit tests
run: |
dotnet tool restore
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Util.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Extraction.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.1 autobuilder/Semmle.Autobuild.CSharp.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.1 autobuilder/Semmle.Autobuild.Cpp.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.1 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
shell: bash
stubgentest:
runs-on: ubuntu-latest

View File

@@ -7,6 +7,8 @@ on:
- .github/workflows/go-tests-other-os.yml
- .github/actions/**
- codeql-workspace.yml
env:
GO_VERSION: '~1.22.0'
permissions:
contents: read
@@ -16,17 +18,72 @@ jobs:
name: Test MacOS
runs-on: macos-latest
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: false
id: go
- name: Check out code
uses: actions/checkout@v4
- name: Run tests
uses: ./go/actions/test
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: go-qltest
- name: Test
run: |
cd go
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
test-win:
if: github.repository_owner == 'github'
name: Test Windows
runs-on: windows-latest-xl
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: false
id: go
- name: Check out code
uses: actions/checkout@v4
- name: Run tests
uses: ./go/actions/test
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: go-qltest
- name: Test
run: |
cd go
make test cache="${{ steps.query-cache.outputs.cache-dir }}"

View File

@@ -16,6 +16,9 @@ on:
- .github/actions/**
- codeql-workspace.yml
env:
GO_VERSION: '~1.22.0'
permissions:
contents: read
@@ -25,9 +28,51 @@ jobs:
name: Test Linux (Ubuntu)
runs-on: ubuntu-latest-xl
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: false
id: go
- name: Check out code
uses: actions/checkout@v4
- name: Run tests
uses: ./go/actions/test
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
- name: Check that all Go code is autoformatted
run: |
cd go
make check-formatting
- name: Compile qhelp files to markdown
run: |
cd go
env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
- name: Upload qhelp markdown
uses: actions/upload-artifact@v3
with:
run-code-checks: true
name: qhelp-markdown
path: go/qhelp-out/**/*.md
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: go-qltest
- name: Test
run: |
cd go
make test cache="${{ steps.query-cache.outputs.cache-dir }}"

View File

@@ -6,7 +6,6 @@ on:
- "swift/**"
- "misc/bazel/**"
- "misc/codegen/**"
- "shared/**"
- "*.bazel*"
- .github/workflows/swift.yml
- .github/actions/**
@@ -23,12 +22,10 @@ on:
- "swift/**"
- "misc/bazel/**"
- "misc/codegen/**"
- "shared/**"
- "*.bazel*"
- .github/workflows/swift.yml
- .github/actions/**
- codeql-workspace.yml
- .pre-commit-config.yaml
- "!**/*.md"
- "!**/*.qhelp"
branches:

View File

@@ -1,5 +0,0 @@
[lfs]
# codeql is publicly forked by many users, and we don't want any LFS file polluting their working
# copies. We therefore exclude everything by default.
# For files required by bazel builds, use rules in `misc/bazel/lfs.bzl` to download them on demand.
fetchinclude = /nothing

View File

@@ -20,23 +20,13 @@ repos:
- id: autopep8
files: ^misc/codegen/.*\.py
- repo: local
- repo: https://github.com/warchant/pre-commit-buildifier
rev: 0.0.2
hooks:
- id: buildifier
name: Format bazel files
files: \.(bazel|bzl)
language: system
entry: bazel run //misc/bazel:buildifier
pass_filenames: false
# DISABLED: can be enabled by copying this config and installing `pre-commit` with `--config` on the copy
# - id: go-gen
# name: Check checked in generated files in go
# files: ^go/.*
# language: system
# entry: bazel run //go:gen
# pass_filenames: false
- repo: local
hooks:
- id: codeql-format
name: Fix QL file formatting
files: \.qll?$

View File

@@ -1,6 +1,5 @@
{
"omnisharp.autoStart": false,
"cmake.sourceDirectory": "${workspaceFolder}/swift",
"cmake.buildDirectory": "${workspaceFolder}/bazel-cmake-build",
"codeQL.githubDatabase.download": "never"
"cmake.buildDirectory": "${workspaceFolder}/bazel-cmake-build"
}

View File

@@ -1,7 +1,6 @@
/cpp/ @github/codeql-c-analysis
/cpp/autobuilder/ @github/codeql-c-extractor
/csharp/ @github/codeql-csharp
/csharp/autobuilder/Semmle.Autobuild.Cpp @github/codeql-c-extractor
/csharp/autobuilder/Semmle.Autobuild.Cpp.Tests @github/codeql-c-extractor
/go/ @github/codeql-go
/java/ @github/codeql-java
/javascript/ @github/codeql-javascript
@@ -13,6 +12,9 @@
/java/ql/test-kotlin1/ @github/codeql-kotlin
/java/ql/test-kotlin2/ @github/codeql-kotlin
# ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
# CodeQL tools and associated docs
/docs/codeql/codeql-cli/ @github/codeql-cli-reviewers
/docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
@@ -35,7 +37,9 @@ MODULE.bazel @github/codeql-ci-reviewers
# Workflows
/.github/workflows/ @github/codeql-ci-reviewers
/.github/workflows/atm-* @github/codeql-ml-powered-queries-reviewers
/.github/workflows/go-* @github/codeql-go
/.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
/.github/workflows/ruby-* @github/codeql-ruby
/.github/workflows/swift.yml @github/codeql-swift

View File

@@ -4,8 +4,6 @@ We welcome contributions to our CodeQL libraries and queries. Got an idea for a
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/codeql-queries) on [codeql.github.com](https://codeql.github.com).
Note that the CodeQL for Visual Studio Code documentation has been migrated to https://docs.github.com/en/code-security/codeql-for-vs-code/, but you can still contribute to it via a different repository. For more information, see [Contributing to GitHub Docs documentation](https://docs.github.com/en/contributing)."
## Change notes
Any nontrivial user-visible change to a query pack or library pack should have a change note. For details on how to add a change note for your change, see [this guide](docs/change-notes.md).
@@ -45,7 +43,7 @@ If you have an idea for a query that you would like to share with other CodeQL u
3. **Formatting**
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://docs.github.com/en/code-security/codeql-for-vs-code/).
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-codeql-for-visual-studio-code).
If you prefer, you can either:
1. install the [pre-commit framework](https://pre-commit.com/) and install the configured hooks on this repo via `pre-commit install`, or

View File

@@ -13,18 +13,14 @@ local_path_override(
# see https://registry.bazel.build/ for a list of available packages
bazel_dep(name = "platforms", version = "0.0.9")
bazel_dep(name = "rules_go", version = "0.47.0")
bazel_dep(name = "rules_pkg", version = "0.10.1")
bazel_dep(name = "platforms", version = "0.0.8")
bazel_dep(name = "rules_pkg", version = "0.9.1")
bazel_dep(name = "rules_nodejs", version = "6.0.3")
bazel_dep(name = "rules_python", version = "0.31.0")
bazel_dep(name = "bazel_skylib", version = "1.5.0")
bazel_dep(name = "abseil-cpp", version = "20240116.0", repo_name = "absl")
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
bazel_dep(name = "fmt", version = "10.0.0")
bazel_dep(name = "gazelle", version = "0.36.0")
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip.parse(
@@ -35,8 +31,6 @@ pip.parse(
use_repo(pip, "codegen_deps")
swift_deps = use_extension("//swift/third_party:load.bzl", "swift_deps")
# following list can be kept in sync with `bazel mod tidy`
use_repo(
swift_deps,
"binlog",
@@ -54,9 +48,6 @@ node.toolchain(
)
use_repo(node, "nodejs", "nodejs_toolchains")
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.22.2")
register_toolchains(
"@nodejs_toolchains//:all",
)

View File

@@ -4,7 +4,7 @@ This open source repository contains the standard CodeQL libraries and queries t
## How do I learn CodeQL and run queries?
There is extensive documentation about the [CodeQL language](https://codeql.github.com/docs/), writing CodeQL using the [CodeQL extension for Visual Studio Code](https://docs.github.com/en/code-security/codeql-for-vs-code/) and using the [CodeQL CLI](https://docs.github.com/en/code-security/codeql-cli).
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL using the [CodeQL extension for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) and the [CodeQL CLI](https://codeql.github.com/docs/codeql-cli/).
## Contributing

View File

@@ -11,6 +11,16 @@ provide:
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
- "go/ql/config/legacy-support/qlpack.yml"
- "go/build/codeql-extractor-go/codeql-extractor.yml"
- "javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml"
# This pack is explicitly excluded from the workspace since most users
# will want to use a version of this pack from the package cache. Internal
# users can uncomment the following line and place a custom ML model
# in the corresponding pack to test a custom ML model within their local
# checkout.
# - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml"
- "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml"
- "javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml"
- "javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml"
- "csharp/ql/campaigns/Solorigate/lib/qlpack.yml"
- "csharp/ql/campaigns/Solorigate/src/qlpack.yml"
- "csharp/ql/campaigns/Solorigate/test/qlpack.yml"

View File

@@ -362,7 +362,7 @@
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
],
"Python model summaries test extension": [
"python/ql/test/library-tests/dataflow/model-summaries/InlineTaintTest.ext.yml",
"python/ql/test/library-tests/dataflow/model-summaries/NormalDataflowTest.ext.yml"
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
]
}
}

View File

@@ -1,4 +1,4 @@
load("@rules_pkg//pkg:mappings.bzl", "pkg_filegroup")
load("@rules_pkg//:mappings.bzl", "pkg_filegroup")
package(default_visibility = ["//visibility:public"])

13
cpp/autobuilder/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
obj/
TestResults/
*.manifest
*.pdb
*.suo
*.mdb
*.vsmdi
csharp.log
**/bin/Debug
**/bin/Release
*.tlog
.vs
*.user

View File

@@ -1 +0,0 @@
The Windows autobuilder that used to live in this directory moved to `csharp/autobuilder/Semmle.Autobuild.Cpp`.

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.6.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Semmle.Autobuild.Cpp\Semmle.Autobuild.Cpp.csproj" />
<ProjectReference Include="..\..\..\csharp\autobuilder\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,32 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Semmle.Autobuild.Cpp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("GitHub")]
[assembly: AssemblyProduct("CodeQL autobuilder for C++")]
[assembly: AssemblyCopyright("Copyright © GitHub 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>Semmle.Autobuild.Cpp</AssemblyName>
<RootNamespace>Semmle.Autobuild.Cpp</RootNamespace>
<ApplicationIcon />
<OutputType>Exe</OutputType>
<StartupObject />
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.8.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\csharp\extractor\Semmle.Util\Semmle.Util.csproj" />
<ProjectReference Include="..\..\..\csharp\autobuilder\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
description: Revert support for repeated initializers, which are allowed in C with designated initializers.
compatibility: full
aggregate_field_init.rel: reorder aggregate_field_init.rel (@aggregateliteral aggregate, @expr initializer, @membervariable field, int position) aggregate initializer field
aggregate_array_init.rel: reorder aggregate_array_init.rel (@aggregateliteral aggregate, @expr initializer, int element_index, int position) aggregate initializer element_index
aggregate_field_init.rel: reorder aggregate_field_init.rel (int aggregate, int initializer, int field, int position) aggregate initializer field
aggregate_array_init.rel: reorder aggregate_array_init.rel (int aggregate, int initializer, int element_index, int position) aggregate initializer element_index

View File

@@ -1,4 +1,4 @@
load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix")
load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
pkg_files(
name = "downgrades",

View File

@@ -1,4 +1,4 @@
load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
load("@rules_pkg//:mappings.bzl", "pkg_files")
package(default_visibility = ["//cpp:__pkg__"])

View File

@@ -1,28 +1,3 @@
## 0.13.1
No user-facing changes.
## 0.13.0
### Breaking Changes
* Deleted the deprecated `GlobalValueNumberingImpl.qll` implementation.
### New Features
* Models-as-Data support has been added for C/C++. This feature allows flow sources, sinks and summaries to be expressed in compact strings as an alternative to modelling each source / sink / summary with explicit QL. See `dataflow/ExternalFlow.qll` for documentation and specification of the model format, and `models/implementations/ZMQ.qll` for a simple example of models. Importing models from `.yml` is not yet supported.
### Minor Analysis Improvements
* Source models have been added for the standard library function `getc` (and variations).
* Source, sink and flow models for the ZeroMQ (ZMQ) networking library have been added.
* Parameters of functions without definitions now have `ParameterNode`s.
* The alias analysis used internally by various libraries has been improved to answer alias questions more conservatively. As a result, some queries may report fewer false positives.
## 0.12.11
No user-facing changes.
## 0.12.10
### New Features

View File

@@ -1,4 +0,0 @@
---
category: breaking
---
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.

View File

@@ -1,3 +0,0 @@
## 0.12.11
No user-facing changes.

View File

@@ -1,16 +0,0 @@
## 0.13.0
### Breaking Changes
* Deleted the deprecated `GlobalValueNumberingImpl.qll` implementation.
### New Features
* Models-as-Data support has been added for C/C++. This feature allows flow sources, sinks and summaries to be expressed in compact strings as an alternative to modelling each source / sink / summary with explicit QL. See `dataflow/ExternalFlow.qll` for documentation and specification of the model format, and `models/implementations/ZMQ.qll` for a simple example of models. Importing models from `.yml` is not yet supported.
### Minor Analysis Improvements
* Source models have been added for the standard library function `getc` (and variations).
* Source, sink and flow models for the ZeroMQ (ZMQ) networking library have been added.
* Parameters of functions without definitions now have `ParameterNode`s.
* The alias analysis used internally by various libraries has been improved to answer alias questions more conservatively. As a result, some queries may report fewer false positives.

View File

@@ -1,3 +0,0 @@
## 0.13.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.13.1
lastReleaseVersion: 0.12.10

View File

@@ -1,88 +0,0 @@
import cpp
/*
The syntax of a C++ program.
*/
signature module BuildlessASTSig
{
class Node;
predicate nodeLocation(Node node, Location location);
// Parent/child relationship between AST nodes
predicate edge(Node parent, int index, Node child);
// Include graph
predicate userInclude(Node include, string path);
predicate systemInclude(Node include, string path);
// Namespaces
predicate namespace(Node ns);
predicate namespaceName(Node ns, string name);
predicate namespaceMember(Node ns, Node member);
// Functions
predicate function(Node fn);
predicate functionBody(Node fn, Node body);
predicate functionReturn(Node fn, Node returnType);
predicate functionName(Node fn, string name);
predicate functionParameter(Node fn, int i, Node parameterDecl);
predicate functionDefinition(Node fn); // If a definition as opposed to a declaration
// Statements
predicate stmt(Node node);
predicate blockStmt(Node stmt);
predicate blockMember(Node stmt, int index, Node child);
predicate ifStmt(Node stmt, Node condition, Node thenBranch);
predicate ifStmt(Node tmt, Node condition, Node thenBranch, Node elseBranch);
predicate whileStmt(Node stmt, Node condition, Node body);
predicate doWhileStmt(Node stmt, Node condition, Node body);
predicate forStmt(Node stmt, Node init, Node condition, Node update, Node body);
predicate exprStmt(Node stmt, Node expr);
predicate returnStmt(Node stmt, Node expr);
predicate returnVoidStmt(Node stmt);
// etc
// Types
predicate type(Node type);
predicate ptrType(Node type, Node element);
predicate refType(Node type, Node element);
predicate constType(Node type, Node element);
predicate rvalueRefType(Node type, Node element);
predicate arrayType(Node type, Node element, Node size);
predicate arrayType(Node type, Node element);
predicate typename(Node node, string name); // Any named type, including built-in types
predicate templated(Node node);
predicate typeDefinition(Node node); // If a definition as opposed to a declaration
predicate classOrStructDefinition(Node node);
predicate classMember(Node classOrStruct, int child, Node member);
// Templates
predicate templateParameter(Node node, int i, Node parameter);
predicate typeParameter(Node templateParameter, Node type, Node parameter);
predicate typeParameterDefault(Node templateParameter, Node defaultTypeOrValue);
// Declarations
predicate variableDeclaration(Node decl);
predicate variableDeclarationType(Node decl, Node type);
predicate variableDeclarationEntry(Node decl, int index, Node entry);
predicate variableDeclarationEntryInitializer(Node entry, Node initializer);
predicate variableName(Node entry, string name);
predicate ptrEntry(Node entry, Node element);
predicate refEntry(Node entry, Node element);
predicate rvalueRefEntry(Node entry, Node element);
predicate arrayEntry(Node entry, Node element); // ?? Size
// Expressions
predicate expression(Node node);
predicate prefixExpr(Node expr, string operator, Node operand);
predicate postfixExpr(Node expr, Node operand, string operator);
predicate binaryExpr(Node expr, Node lhs, string operator, Node rhs);
predicate castExpr(Node expr, Node type, Node operand);
predicate callExpr(Node call);
predicate callArgument(Node call, int i, Node arg);
predicate callReceiver(Node call, Node receiver);
predicate accessExpr(Node expr, string name);
predicate literal(Node expr, string value);
predicate stringLiteral(Node expr, string value);
}

View File

@@ -1,304 +0,0 @@
import cpp
import ASTSig
module CompiledAST implements BuildlessASTSig {
private class SourceLocation extends Location {
SourceLocation() { not this.hasLocationInfo(_, 0, 0, 0, 0) }
}
private Type reachableType(Type type) {
result = type or
result = reachableType(type).stripTopLevelSpecifiers() or
result = reachableType(type).(PointerType).getBaseType() or
result = reachableType(type).(ReferenceType).getBaseType()
}
private class SourceDeclEntry extends DeclarationEntry {
SourceDeclEntry() {
not this.isAffectedByMacro() and
not this.isFromTemplateInstantiation(_) and
not this.isInMacroExpansion() and
not this.getDeclaration().isInMacroExpansion() and
this.getLocation() instanceof SourceLocation and
not this.(FunctionDeclarationEntry).getDeclaration().isCompilerGenerated()
}
}
private newtype TNode =
// TFunction(SourceLocation loc) { exists(Function f | f.getLocation() = loc) } or
TStatement(SourceLocation loc) { exists(Stmt s | s.getLocation() = loc) } or
TDeclaration(SourceDeclEntry decl) or
TExpression(SourceLocation loc) { exists(Expr e | e.getLocation() = loc) } or
TFunctionCallName(SourceLocation loc) { exists(FunctionCall c | c.getLocation() = loc) } or
TDeclarationType(SourceDeclEntry decl, Type type) { type = reachableType(decl.getType()) } or
TNamespaceDeclaration(NamespaceDeclarationEntry ns) { any() } or
TInclude(Include i)
class Node extends TNode {
string toString() { result = "node" }
SourceLocation getLocation() {
this = TStatement(result) or
result = this.getDeclaration().getLocation() or
this = TExpression(result) or
this = TFunctionCallName(result) or
result = this.getVariableDeclaration().getLocation() or
result = this.getNamespaceDeclaration().getLocation() or
result = this.getInclude().getLocation()
}
Include getInclude() { this = TInclude(result) }
Stmt getStmt() { this = TStatement(result.getLocation()) }
Function getFunction() {
result = this.getDeclaration().getDeclaration() and
not result.isFromTemplateInstantiation(_) and
not result.isCompilerGenerated()
}
SourceDeclEntry getDeclaration() { this = TDeclaration(result) }
NamespaceDeclarationEntry getNamespaceDeclaration() { this = TNamespaceDeclaration(result) }
Type getType() { this = TDeclarationType(_, result) }
SourceDeclEntry getVariableDeclaration() { this = TDeclarationType(result, _) }
Expr getExpr() { this = TExpression(result.getLocation()) }
FunctionCall getFunctionCallName() { this = TFunctionCallName(result.getLocation()) }
}
predicate nodeLocation(Node node, Location location) { location = node.getLocation() }
// Include graph
predicate userInclude(Node include, string path) {
exists(string head | head = include.getInclude().getHead() |
path = head.substring(1, head.length() - 1) and head.charAt(0) = "\""
)
}
predicate systemInclude(Node include, string path) {
exists(string head | head = include.getInclude().getHead() |
path = head.substring(1, head.length() - 1) and head.charAt(0) = "<"
)
}
// Functions
predicate function(Node fn) { exists(fn.getFunction()) }
predicate functionBody(Node fn, Node body) { body.getStmt() = fn.getFunction().getBlock() }
predicate functionReturn(Node fn, Node returnType) {
returnType.getVariableDeclaration() = fn.getDeclaration() and
fn.getDeclaration().(FunctionDeclarationEntry).getDeclaration().getType() = returnType.getType()
}
predicate functionName(Node fn, string name) { name = fn.getFunction().getName() }
predicate functionParameter(Node fn, int i, Node parameterDecl) {
functionParameter0(fn, i, parameterDecl) and
not exists(Node param2 | functionParameter0(fn, i, param2) |
param2.getLocation().getStartLine() < parameterDecl.getLocation().getStartLine()
)
}
pragma[nomagic]
private predicate functionParameter0(Node fn, int i, Node parameterDecl) {
fn.getFunction().getParameter(i).getADeclarationEntry() = parameterDecl.getDeclaration() and
fn.getLocation().getFile() = parameterDecl.getLocation().getFile() and
fn.getLocation().getStartLine() <= parameterDecl.getLocation().getStartLine()
}
// Statements
predicate stmt(Node node) { exists(node.getStmt()) }
predicate blockStmt(Node stmt) { stmt.getStmt() instanceof BlockStmt }
predicate blockMember(Node stmt, int index, Node child) {
child.getStmt() = stmt.getStmt().(BlockStmt).getChild(index)
}
predicate ifStmt(Node stmt, Node condition, Node thenBranch) { none() }
predicate ifStmt(Node tmt, Node condition, Node thenBranch, Node elseBranch) { none() }
predicate whileStmt(Node stmt, Node condition, Node body) { none() }
predicate doWhileStmt(Node stmt, Node condition, Node body) { none() }
predicate forStmt(Node stmt, Node init, Node condition, Node update, Node body) { none() }
predicate exprStmt(Node stmt, Node expr) { none() }
predicate returnStmt(Node stmt, Node expr) { none() }
predicate returnVoidStmt(Node stmt) { none() }
// etc
// Types
predicate ptrType(Node node, Node element) {
exists(PointerType type, SourceDeclEntry e |
node = TDeclarationType(e, type) and
element = TDeclarationType(e, type.getBaseType())
)
}
predicate refType(Node node, Node element) {
exists(ReferenceType type, SourceDeclEntry e |
node = TDeclarationType(e, type) and
element = TDeclarationType(e, type.getBaseType())
)
}
predicate rvalueRefType(Node type, Node element) { none() }
predicate arrayType(Node type, Node element, Node size) { none() }
predicate arrayType(Node type, Node element) { none() }
predicate typename(Node node, string name) {
exists(Class c | c = node.getDeclaration().getDeclaration() |
not c.isAnonymous() and c.getName() = name
)
or
name = node.getType().getName()
}
predicate templated(Node node) { none() }
predicate classOrStructDefinition(Node node) {
node.getDeclaration().getDeclaration() instanceof Class
}
predicate classMember(Node classOrStruct, int child, Node member) {
classOrStruct.getDeclaration().getDeclaration().(Class).getAMember() =
member.getDeclaration().getDeclaration() and
child = 0 and
classOrStruct.getLocation().getFile() = member.getLocation().getFile() // TODO: Disambiguate
// and not member.getDeclaration().getDeclaration() instanceof FriendDecl
}
// Templates
predicate templateParameter(Node node, int i, Node parameter) { none() }
predicate typeParameter(Node templateParameter, Node type, Node parameter) { none() }
predicate typeParameterDefault(Node templateParameter, Node defaultTypeOrValue) { none() }
// Declarations
predicate variableDeclaration(Node decl) {
decl.getDeclaration() instanceof VariableDeclarationEntry
}
pragma[nomagic]
private predicate variableDeclarationType2(Node decl, Node type) {
decl.getDeclaration() = type.getVariableDeclaration()
}
predicate variableDeclarationType(Node decl, Node type) {
variableDeclarationType2(decl, type) and
type.getType() = decl.getDeclaration().getType()
}
predicate variableDeclarationEntry(Node decl, int index, Node entry) { none() }
predicate variableDeclarationEntryInitializer(Node entry, Node initializer) { none() }
predicate variableName(Node decl, string name) {
decl.getDeclaration().(VariableDeclarationEntry).getName() = name
}
predicate ptrEntry(Node entry, Node element) { none() }
predicate refEntry(Node entry, Node element) { none() }
predicate rvalueRefEntry(Node entry, Node element) { none() }
predicate arrayEntry(Node entry, Node element) { none() }
// Expressions
predicate expression(Node node) { exists(node.getExpr()) or exists(node.getFunctionCallName()) }
predicate prefixExpr(Node expr, string operator, Node operand) { none() }
predicate postfixExpr(Node expr, Node operand, string operator) { none() }
predicate binaryExpr(Node expr, Node lhs, string operator, Node rhs) { none() }
predicate castExpr(Node expr, Node type, Node operand) { none() }
predicate callExpr(Node call) { call.getExpr() instanceof Call }
predicate callArgument(Node call, int i, Node arg) {
arg.getExpr() = call.getExpr().(Call).getArgument(i)
}
predicate callReceiver(Node call, Node receiver) {
receiver.getFunctionCallName() = call.getExpr()
}
predicate accessExpr(Node expr, string name) {
expr.getExpr().(VariableAccess).getTarget().getName() = name or
expr.getFunctionCallName().getTarget().getName() = name
}
predicate literal(Node expr, string value) { expr.getExpr().(Literal).toString() = value }
predicate stringLiteral(Node expr, string value) {
expr.getExpr().(StringLiteral).getValue() = value
}
predicate type(Node node) { node = TDeclarationType(_, _) }
predicate constType(Node node, Node element) {
exists(SpecifiedType type, SourceDeclEntry e |
node = TDeclarationType(e, type) and
element = TDeclarationType(e, type.getBaseType()) and
type.isConst()
)
}
predicate namespace(Node ns) { exists(ns.getNamespaceDeclaration()) }
predicate namespaceName(Node ns, string name) {
ns.getNamespaceDeclaration().getNamespace().getName() = name
}
pragma[nomagic]
private predicate namespaceNamespace(Node ns, Node child) {
ns.getNamespaceDeclaration().getNamespace().getAChildNamespace() =
child.getNamespaceDeclaration().getNamespace() and
ns.getLocation().getFile() = child.getLocation().getFile()
}
pragma[nomagic]
private predicate namespaceDecl(Node ns, Node child) {
child.getDeclaration().getDeclaration() =
ns.getNamespaceDeclaration().getNamespace().getADeclaration() and
ns.getLocation().getFile() = child.getLocation().getFile() and
ns.getLocation().getStartLine() <= child.getLocation().getStartLine()
}
predicate namespaceMember(Node ns, Node member) {
namespaceNamespace(ns, member) or
namespaceDecl(ns, member)
}
predicate edge(Node parent, int index, Node child) {
namespaceMember(parent, child) and index = 0
or
classMember(parent, index, child)
or
blockMember(parent, index, child)
}
predicate typeDefinition(Node node) {
node.getDeclaration().(TypeDeclarationEntry).isDefinition()
}
predicate functionDefinition(Node fn) {
fn.getDeclaration().(FunctionDeclarationEntry).isDefinition()
}
}

View File

@@ -1,282 +0,0 @@
import AST
module BuildlessModel<BuildlessASTSig Sig> {
module AST = BuildlessAST<Sig>;
private string getQualifiedName(AST::SourceNamespace ns) {
not exists(AST::SourceNamespace p | ns = p.getAChild()) and result = ns.getName()
or
exists(AST::SourceNamespace p | ns = p.getAChild() and ns != p |
result = getQualifiedName(p) + "::" + ns.getName()
)
}
private newtype TElement =
TNamespace(string fqn) { fqn = getQualifiedName(_) } or
TASTNode(AST::SourceElement node) { any() }
/*
Any compile-time concept that can be named.
*/
class Entity extends string
{
bindingset[this] Entity() { any() }
}
/*
An entity that contains named members.
*/
class Scope extends Entity
{
bindingset[this] Scope() { any() }
Member getAMember() { result = this.getAMember(_) }
abstract Member getAMember(string name);
}
/*
An entity that is a member of a scope.
*/
class Member extends Entity
{
bindingset[this] Member() { any() }
abstract string getName();
abstract Scope getParent();
}
class Namespace2 extends Member, Scope
{
AST::SourceNamespace ns;
Namespace2() { this = "::" + getQualifiedName(ns) }
AST::SourceNamespace getAstNode() { result = ns }
override Namespace2 getParent() { result.getAstNode() = ns.getParent() }
override string getName() { result = ns.getName() }
override Member getAMember(string name) {
result = this.getMemberNamespace(name)
// !! Types
}
Namespace2 getMemberNamespace(string name) {
result.getParent() = this and result.getName() = name
}
}
class Type extends Member, Scope {
Type() { exists(SourceTypeDeclaration t | t.getMangledName() = this) }
AST::SourceType getAstNode() { result = this.getADeclaration().getSourceNode() }
// string toString() { result = "i am a type" }
override string getName() { result = this.getADeclaration().getName() }
Location getLocation() { result = this.getADefinition().getLocation() }
SourceTypeDeclaration getADeclaration() { result.getMangledName() = this }
SourceTypeDefinition getADefinition() { result.getMangledName() = this }
override Member getAMember(string name)
{
result = getMemberType(name)
}
Type getMemberType(string name)
{
none()
}
Namespace2 getParentNamespace() {
result.getAstNode() = this.getADeclaration().getParentNamespace()
}
Type getParentType() { result.getADeclaration() = this.getADeclaration().getParentType() }
override Scope getParent() { result = this.getParentNamespace() or result = this.getParentType() }
string getFullyQualifiedName() { result = this.getADeclaration().getFullyQualifiedName() }
Field getAField() { result.getParentType() = this }
}
private Type lookupNameInType(Type type, string name)
{
// Is the name a member of this type?
// Is the name in the same scope as the type?
// Is the name in global scope?
// TODO!
none()
}
class Field extends string {
Type containingType;
SourceTypeDefinition containingTypeDef;
AST::SourceVariableDeclaration fieldDef;
Field() {
containingType.getADefinition() = containingTypeDef and
fieldDef = containingTypeDef.getAField() and
this = containingTypeDef.getMangledName() + "::" + fieldDef.getName()
}
Location getLocation() { result = fieldDef.getLocation() }
Type getParentType() { result = containingType }
string getName() { result = fieldDef.getName() }
// TODO: The type of the field
}
class Element extends TElement {
string toString() { result = "element" }
}
class Namespace extends Element, TNamespace {
string getFullyQualifiedName() { this = TNamespace(result) }
override string toString() { result = "namespace " + this.getFullyQualifiedName() }
NamespaceDeclaration getADeclaration() { result.getNamespace() = this }
}
class SourceElement extends Element, TASTNode {
AST::SourceElement node;
SourceElement() { this = TASTNode(node) }
Location getLocation() { result = node.getLocation() }
AST::SourceElement getSourceNode() { result = node }
}
class SourceDeclaration extends SourceElement, TASTNode {
abstract SourceDeclaration getParent();
abstract string getName();
abstract string getMangledName();
abstract predicate isDefinition();
}
abstract class SourceDefinition extends SourceDeclaration { }
class NamespaceDeclaration extends SourceDeclaration {
AST::SourceNamespace ns;
NamespaceDeclaration() { ns = node }
override string getName() { result = ns.getName() }
Namespace getNamespace() { result.getFullyQualifiedName() = this.getFullyQualifiedName() }
override string toString() { result = "namespace " + this.getName() + " { ... }" }
override NamespaceDeclaration getParent() { result.getSourceNode() = node.getParent() }
string getFullyQualifiedName() {
if exists(this.getParent())
then result = this.getParent().getFullyQualifiedName() + "::" + this.getName()
else result = this.getName()
}
override string getMangledName() { result = "::" + this.getFullyQualifiedName() }
override predicate isDefinition() { any() }
}
class SourceTypeDeclaration extends SourceDeclaration {
AST::SourceTypeDefinition def;
SourceTypeDeclaration() { def = node }
override Location getLocation() { result = def.getLocation() }
override string toString() { result = "typename " + def.getName() }
string getFullyQualifiedName() {
if exists(this.getParentNamespace())
then result = this.getParentNamespace().getFullyQualifiedName() + "::" + this.getName()
else
if exists(this.getParentType())
then result = this.getParentType() + "::" + this.getName()
else result = this.getName()
}
NamespaceDeclaration getParentNamespace() { result.getSourceNode() = def.getParent() }
SourceTypeDeclaration getParentType() { result.getSourceNode() = def.getParent() }
override SourceDeclaration getParent() {
result = this.getParentNamespace() or result = this.getParentType()
}
override string getName() { result = def.getName() }
// Mangled name
override string getMangledName() {
if exists(this.getParent())
then result = this.getParent().getMangledName() + "::" + this.getName()
else result = "::" + this.getName()
}
override predicate isDefinition() { def.isDefinition() }
AST::SourceVariableDeclaration getAField() {
result = def.getAMember()
}
}
class SourceTypeDefinition extends SourceTypeDeclaration, SourceDefinition {
SourceTypeDefinition() { this.isDefinition() }
}
class SourceFunctionDeclaration extends SourceDeclaration {
AST::SourceFunction fn;
SourceFunctionDeclaration() { fn = node }
SourceTypeDeclaration getParentType() { result.getSourceNode() = fn.getParent() }
NamespaceDeclaration getParentNamespace() { result.getSourceNode() = fn.getParent() }
override SourceDeclaration getParent() {
result = this.getParentType() or result = this.getParentNamespace()
}
override string toString() { result = fn.getName() + "()" }
override Location getLocation() { result = fn.getLocation() }
override string getName() { result = fn.getName() }
override string getMangledName() {
if exists(this.getParent())
then result = this.getParent().getMangledName() + "::" + this.getName() + "()"
else result = "::" + this.getName() + "()"
}
override predicate isDefinition() { fn.isDefinition() }
}
class SourceFunctionDefinition extends SourceFunctionDeclaration, SourceDefinition {
SourceFunctionDefinition() { this.isDefinition() }
}
predicate invalidParent(SourceDeclaration decl) { decl.getParent+() = decl }
}
// For debugging in context
module TestModel = BuildlessModel<CompiledAST>;

View File

@@ -1,46 +0,0 @@
Next:
- [x] Namespaces
- [ ] Unnamed class/struct/union and name mangling
- [ ] Linker awareness is creating duplicates
- Resolve the type of a local variable
- [ ] Parent scopes - where are things declared
Parent class
Parent namespace
Parent function (for variable declarations and parameters)
Parent block (for blocks and statements)
- [ ] Return type nodes
- [ ] Construction of types
- [ ] Construction of functions
- [ ] Construction of Variables
Names:
- [x] Identify all function names
- [x] Identify all parameter names
- [x] Identify all variable declaration names
- [x] Identify variable accesses
Types:
- [x] Locate user type definitions and typedefs
- [x] Get the type of the parameter
- [ ] Assign types to variable accesses
Calls:
- [x] Identify function call expressions
- [x] Identify the "target" of a call in a trivial case.
Overloads:
Types:
- [ ] Identify return types
- [x] Identify parameter types and other declarations

View File

@@ -1,196 +0,0 @@
import CompiledAST
import ASTSig
module BuildlessAST<BuildlessASTSig AST> {
final class Node = AST::Node;
// Any node in the abstract syntax tree
class SourceElement extends Node {
Location getLocation() { AST::nodeLocation(this, result) }
string toString() { result = "element" }
SourceElement getParent() { AST::edge(result, _, this) }
SourceElement getChild(int i) { AST::edge(this, i, result) }
SourceElement getAChild() { result = this.getChild(_) }
}
bindingset[path]
private File getAnIncludeTarget(string path) {
exists(string p | p = result.toString() | path = p.suffix(p.length() - path.length()))
}
class Include extends SourceElement {
string path;
Include() { AST::userInclude(this, path) or AST::systemInclude(this, path) }
File getATarget() {
result = getAnIncludeTarget(path)
or
path.prefix(3) = "../" and result = getAnIncludeTarget(path.suffix(3))
or
path.prefix(2) = "./" and result = getAnIncludeTarget(path.suffix(2))
}
predicate isSystemInclude() { AST::systemInclude(this, _) }
predicate isUserInclude() { AST::userInclude(this, _) }
override string toString() {
this.isSystemInclude() and result = "#include <" + path + ">"
or
this.isUserInclude() and result = "#include \"" + path + "\""
}
}
abstract class SourceScope extends SourceElement { }
class SourceNamespace extends SourceScope, SourceDeclaration {
SourceNamespace() { AST::namespace(this) }
override string getName() { AST::namespaceName(this, result) }
override string toString() { result = "namespace " + this.getName() }
}
// Any syntax node that is a declaration
abstract class SourceDeclaration extends SourceElement {
abstract string getName();
}
// A syntax node that declares or defines a function
class SourceFunction extends SourceDeclaration {
SourceFunction() { AST::function(this) }
override string getName() { AST::functionName(this, result) }
override string toString() { result = this.getName() }
BlockStmt getBody() { AST::functionBody(this, result) }
SourceParameter getParameter(int i) { AST::functionParameter(this, i, result) }
SourceType getReturnType() { AST::functionReturn(this, result) }
predicate isDefinition() { AST::functionDefinition(this) }
}
// A syntax node that declares a variable (including fields and parameters)
class SourceVariableDeclaration extends SourceDeclaration {
SourceVariableDeclaration() { AST::variableDeclaration(this) }
override string getName() { AST::variableName(this, result) }
override string toString() { result = this.getName() }
SourceType getType() { AST::variableDeclarationType(this, result) }
}
// A syntax node that declares a parameter
class SourceParameter extends SourceVariableDeclaration {
SourceFunction fn;
int index;
SourceParameter() { AST::functionParameter(fn, index, this) }
}
class Stmt extends SourceElement {
Stmt() { AST::stmt(this) }
override string toString() { result = "stmt" }
}
class BlockStmt extends Stmt {
BlockStmt() { AST::blockStmt(this) }
override string toString() { result = "{ ... }" }
}
class Expr extends SourceElement {
Expr() { AST::expression(this) }
}
class AccessExpr extends Expr {
string identifier;
AccessExpr() { AST::accessExpr(this, identifier) }
string getName() { result = identifier }
override string toString() { result = this.getName() }
}
class CallExpr extends Expr {
CallExpr() { AST::callExpr(this) }
Expr getReceiver() { AST::callReceiver(this, result) }
Expr getArgument(int i) { AST::callArgument(this, i, result) }
override string toString() { result = "...(...)" }
}
class Literal extends Expr {
string value;
Literal() { AST::literal(this, value) }
override string toString() { result = value }
string getValue() { result = value }
}
class StringLiteral extends Literal {
StringLiteral() { AST::stringLiteral(this, _) }
}
abstract class SourceDefinition extends SourceDeclaration { }
class SourceTypeDefinition extends SourceDefinition {
SourceTypeDefinition() { AST::classOrStructDefinition(this) }
override string getName() { AST::typename(this, result) }
override string toString() { result = this.getName() }
SourceElement getAMember() { AST::classMember(this, _, result) }
predicate isDefinition() { AST::typeDefinition(this) }
}
// A node that contains a type of some kind
class SourceType extends SourceElement {
SourceType() { AST::type(this) }
override string toString() { AST::typename(this, result) }
}
class SourcePointer extends SourceType {
SourceType pointee;
SourcePointer() { AST::ptrType(this, pointee) }
SourceType getType() { result = pointee }
}
class SourceConst extends SourceType {
SourceType type;
SourceConst() { AST::constType(this, type) }
SourceType getType() { result = type }
}
class SourceReference extends SourceType {
SourceType type;
SourceReference() { AST::refType(this, type) }
SourceType getType() { result = type }
}
}
module TestAST = BuildlessAST<CompiledAST>;

View File

@@ -1,3 +0,0 @@
import Model
select 1

View File

@@ -1,31 +0,0 @@
import ast_sig
import ast
import compiled_ast // For debugging in context
module BuildlessIdentifiers<BuildlessASTSig A> {
module AST = Buildless<A>;
string getQualifiedName(AST::SourceNamespace ns) {
not exists(AST::SourceNamespace p | ns = p.getAChild()) and result = ns.getName()
or
exists(AST::SourceNamespace p | ns = p.getAChild() and ns !=p|
result = getQualifiedName(p) + "::" + ns.getName()
)
}
predicate namespace(string ns) {
ns = ["", getQualifiedName(_)]
}
// What are the identifiers in scope at a given point in the program?
// Give a potential object that the identifier refers to
AST::SourceDeclaration nameLookup(AST::SourceScope scope) {
result = scope
or
result = scope.(AST::SourceNamespace).getAChild()
or
result = scope.(AST::SourceTypeDefinition).getAMember()
}
}
module TestIdentifiers = BuildlessIdentifiers<CompiledAST>;

View File

@@ -1,26 +0,0 @@
import AST
import types
query TestAST::SourceFunction lua_copy() { result.getName() = "lua_copy" }
query int lua_copy_count() { result = count(lua_copy()) }
query predicate variables(TestAST::SourceVariableDeclaration decl, TestAST::SourceType sourceType) {
sourceType = decl.getType()
}
query predicate naiveCallTargets(TestAST::CallExpr call, TestAST::SourceFunction target) {
call.getReceiver().(TestAST::AccessExpr).getName() = target.getName() and
target.getName() = "max"
}
query predicate fnParents(TestAST::SourceFunction fn, TestAST::SourceNamespace parent) {
parent = fn.getParent()
}
query predicate includes(TestAST::Include include, File target) { target = include.getATarget() }
query predicate unresolvedIncludes(TestAST::Include include) { not exists(include.getATarget()) }
from TestAST::SourceFunction fn
select fn, fn.getReturnType(), count(fn.getReturnType()), fn.getReturnType().getAQlClass()

View File

@@ -1,13 +0,0 @@
import Model
query predicate multipleDefinitions(TestModel::Type type, TestModel::SourceTypeDefinition def1, TestModel::SourceTypeDefinition def2)
{
def1 = type.getADefinition() and
def2 = type.getADefinition() and
def1.getLocation() != def2.getLocation() and
def1 != def2
}
from TestModel::SourceDeclaration decl
where decl.getParent+()=decl
select decl, "This has an invalid parent $@", decl.getParent()

View File

@@ -1,34 +0,0 @@
import types
query predicate constPointers(TestAST::SourceType t, TestAST::SourceConst c, TestAST::SourcePointer p)
{
p.getType() = c and
t = c.getType()
}
query predicate constRefs(TestAST::SourceType t, TestAST::SourceConst c, TestAST::SourceReference p)
{
p.getType() = c and
t = c.getType()
}
query predicate nestedNamespaces(TestAST::SourceNamespace parent, TestAST::SourceNamespace child)
{
child = parent.getAChild()
}
query predicate recursiveNamespace(TestAST::SourceNamespace ns, TestAST::SourceNamespace descendents)
{
ns = ns.getAChild+() and
descendents = ns.getAChild+()
}
query predicate usertypes(TestAST::SourceNamespace ns, TestAST::SourceTypeDefinition td)
{
td = ns.getAChild()
}
// Let's try to resolve the type of a local variable
from TestTypes::Type t
select t

View File

@@ -1,34 +0,0 @@
import AST
module BuildlessTypes<BuildlessASTSig Sig> {
module AST = BuildlessAST<Sig>;
private newtype TType =
TBuiltinType(string name) { name = ["int", "char"] }
or
TUserType(string fqn) { exists(AST::SourceTypeDefinition d | d.getName() = fqn) }
//or
//TPointerType(Type type) { exists(A::SourcePointer p | p.getType() = type) }
//or
//TConstType(Type type) { exists(A::SourceConst c | c.getType() = type) }
class Type extends TType {
string toString() { result = this.getName() }
abstract string getName();
Location getLocation() { none() }
}
class BuiltinType extends Type, TBuiltinType {
override string getName() { this = TBuiltinType(result) }
}
class UserType extends Type, TUserType
{
override string getName() { this = TUserType(result) }
override Location getLocation() { exists(AST::SourceTypeDefinition d | this.getName() = d.getName() | result = d.getLocation()) }
}
}
module TestTypes = BuildlessTypes<CompiledAST>;

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 1.0.0-dev
version: 0.12.11-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
@@ -7,10 +7,8 @@ library: true
upgrades: upgrades
dependencies:
codeql/dataflow: ${workspace}
codeql/mad: ${workspace}
codeql/rangeanalysis: ${workspace}
codeql/ssa: ${workspace}
codeql/typeflow: ${workspace}
codeql/tutorial: ${workspace}
codeql/util: ${workspace}
codeql/xml: ${workspace}

View File

@@ -364,8 +364,6 @@ class ConversionNode extends ExprNode {
childIndex = 0 and
result.getAst() = conv.getExpr() and
conv.getExpr() instanceof Conversion
or
result.getAst() = expr.getImplicitDestructorCall(childIndex - 1)
}
}
@@ -463,25 +461,6 @@ class StmtNode extends AstNode {
}
}
/**
* A node representing a child of a `Stmt` that is itself a `Stmt`.
*/
class ChildStmtNode extends StmtNode {
Stmt childStmt;
ChildStmtNode() { exists(Stmt parent | parent.getAChild() = childStmt and childStmt = ast) }
override BaseAstNode getChildInternal(int childIndex) {
result = super.getChildInternal(childIndex)
or
exists(int destructorIndex |
result.getAst() = childStmt.getImplicitDestructorCall(destructorIndex) and
childIndex =
destructorIndex + max(int index | exists(childStmt.getChild(index)) or index = 0) + 1
)
}
}
/**
* A node representing a `DeclStmt`.
*/
@@ -693,13 +672,6 @@ class FunctionNode extends FunctionOrGlobalOrNamespaceVariableNode {
private string getChildAccessorWithoutConversions(Locatable parent, Element child) {
shouldPrintDeclaration(getAnEnclosingDeclaration(parent)) and
(
exists(Stmt s, int i | s.getChild(i) = parent |
exists(int n |
s.getChild(i).(Stmt).getImplicitDestructorCall(n) = child and
result = "getImplicitDestructorCall(" + n + ")"
)
)
or
exists(Stmt s | s = parent |
namedStmtChildPredicates(s, child, result)
or

View File

@@ -565,7 +565,7 @@ class IRGuardCondition extends Instruction {
/** Holds if (determined by this guard) `op == k` evaluates to `areEqual` if this expression evaluates to `value`. */
cached
predicate comparesEq(Operand op, int k, boolean areEqual, AbstractValue value) {
unary_compares_eq(this, op, k, areEqual, false, value)
compares_eq(this, op, k, areEqual, value)
}
/**
@@ -586,7 +586,7 @@ class IRGuardCondition extends Instruction {
cached
predicate ensuresEq(Operand op, int k, IRBlock block, boolean areEqual) {
exists(AbstractValue value |
unary_compares_eq(this, op, k, areEqual, false, value) and this.valueControls(block, value)
compares_eq(this, op, k, areEqual, value) and this.valueControls(block, value)
)
}
@@ -611,7 +611,7 @@ class IRGuardCondition extends Instruction {
cached
predicate ensuresEqEdge(Operand op, int k, IRBlock pred, IRBlock succ, boolean areEqual) {
exists(AbstractValue value |
unary_compares_eq(this, op, k, areEqual, false, value) and
compares_eq(this, op, k, areEqual, value) and
this.valueControlsEdge(pred, succ, value)
)
}
@@ -737,66 +737,26 @@ private predicate compares_eq(
)
}
/**
* Holds if `op == k` is `areEqual` given that `test` is equal to `value`.
*
* Many internal predicates in this file have a `inNonZeroCase` column.
* Ideally, the `k` column would be a type such as `Option<int>::Option`, to
* represent whether we have a concrete value `k` such that `op == k`, or whether
* we only know that `op != 0`.
* However, cannot instantiate `Option` with an infinite type. Thus the boolean
* `inNonZeroCase` is used to distinquish the `Some` (where we have a concrete
* value `k`) and `None` cases (where we only know that `op != 0`).
*
* Thus, if `inNonZeroCase = true` then `op != 0` and the value of `k` is
* meaningless.
*
* To see why `inNonZeroCase` is needed consider the following C program:
* ```c
* char* p = ...;
* if(p) {
* use(p);
* }
* ```
* in C++ there would be an int-to-bool conversion on `p`. However, since C
* does not have booleans there is no conversion. We want to be able to
* conclude that `p` is non-zero in the true branch, so we need to give `k`
* some value. However, simply setting `k = 1` would make the rest of the
* analysis think that `k == 1` holds inside the branch. So we distinquish
* between the above case and
* ```c
* if(p == 1) {
* use(p)
* }
* ```
* by setting `inNonZeroCase` to `true` in the former case, but not in the
* latter.
*/
private predicate unary_compares_eq(
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
/** Holds if `op == k` is `areEqual` given that `test` is equal to `value`. */
private predicate compares_eq(
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
) {
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
exists(AbstractValue v | unary_simple_comparison_eq(test, op, k, inNonZeroCase, v) |
exists(AbstractValue v | simple_comparison_eq(test, op, k, v) |
areEqual = true and value = v
or
areEqual = false and value = v.getDualValue()
)
or
unary_complex_eq(test, op, k, areEqual, inNonZeroCase, value)
complex_eq(test, op, k, areEqual, value)
or
/* (x is true => (op == k)) => (!x is false => (op == k)) */
exists(AbstractValue dual, boolean inNonZeroCase0 |
value = dual.getDualValue() and
unary_compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, inNonZeroCase0, areEqual, dual)
|
k = 0 and inNonZeroCase = inNonZeroCase0
or
k != 0 and inNonZeroCase = true
exists(AbstractValue dual | value = dual.getDualValue() |
compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, areEqual, dual)
)
or
// ((test is `areEqual` => op == const + k2) and const == `k1`) =>
// test is `areEqual` => op == k1 + k2
inNonZeroCase = false and
exists(int k1, int k2, ConstantInstruction const |
compares_eq(test, op, const.getAUse(), k2, areEqual, value) and
int_value(const) = k1 and
@@ -821,53 +781,14 @@ private predicate simple_comparison_eq(
value.(BooleanValue).getValue() = false
}
/**
* Holds if `test` is an instruction that is part of test that eventually is
* used in a conditional branch.
*/
private predicate relevantUnaryComparison(Instruction test) {
not test instanceof CompareInstruction and
exists(IRType type, ConditionalBranchInstruction branch |
type instanceof IRAddressType or type instanceof IRIntegerType
|
type = test.getResultIRType() and
branch.getCondition() = test
)
or
exists(LogicalNotInstruction logicalNot |
relevantUnaryComparison(logicalNot) and
test = logicalNot.getUnary()
)
}
/**
* Rearrange various simple comparisons into `op == k` form.
*/
private predicate unary_simple_comparison_eq(
Instruction test, Operand op, int k, boolean inNonZeroCase, AbstractValue value
) {
/** Rearrange various simple comparisons into `op == k` form. */
private predicate simple_comparison_eq(Instruction test, Operand op, int k, AbstractValue value) {
exists(SwitchInstruction switch, CaseEdge case |
test = switch.getExpression() and
op.getDef() = test and
case = value.(MatchValue).getCase() and
exists(switch.getSuccessor(case)) and
case.getValue().toInt() = k and
inNonZeroCase = false
)
or
// There's no implicit CompareInstruction in files compiled as C since C
// doesn't have implicit boolean conversions. So instead we check whether
// there's a branch on a value of pointer or integer type.
relevantUnaryComparison(test) and
op.getDef() = test and
(
k = 1 and
value.(BooleanValue).getValue() = true and
inNonZeroCase = true
or
k = 0 and
value.(BooleanValue).getValue() = false and
inNonZeroCase = false
case.getValue().toInt() = k
)
}
@@ -879,12 +800,12 @@ private predicate complex_eq(
add_eq(cmp, left, right, k, areEqual, value)
}
private predicate unary_complex_eq(
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
private predicate complex_eq(
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
) {
unary_sub_eq(test, op, k, areEqual, inNonZeroCase, value)
sub_eq(test, op, k, areEqual, value)
or
unary_add_eq(test, op, k, areEqual, inNonZeroCase, value)
add_eq(test, op, k, areEqual, value)
}
/*
@@ -1148,20 +1069,16 @@ private predicate sub_eq(
}
// op - x == c => op == (c+x)
private predicate unary_sub_eq(
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
) {
inNonZeroCase = false and
private predicate sub_eq(Instruction test, Operand op, int k, boolean areEqual, AbstractValue value) {
exists(SubInstruction sub, int c, int x |
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
compares_eq(test, sub.getAUse(), c, areEqual, value) and
op = sub.getLeftOperand() and
x = int_value(sub.getRight()) and
k = c + x
)
or
inNonZeroCase = false and
exists(PointerSubInstruction sub, int c, int x |
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
compares_eq(test, sub.getAUse(), c, areEqual, value) and
op = sub.getLeftOperand() and
x = int_value(sub.getRight()) and
k = c + x
@@ -1215,13 +1132,11 @@ private predicate add_eq(
}
// left + x == right + c => left == right + (c-x)
private predicate unary_add_eq(
Instruction test, Operand left, int k, boolean areEqual, boolean inNonZeroCase,
AbstractValue value
private predicate add_eq(
Instruction test, Operand left, int k, boolean areEqual, AbstractValue value
) {
inNonZeroCase = false and
exists(AddInstruction lhs, int c, int x |
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
@@ -1230,9 +1145,8 @@ private predicate unary_add_eq(
k = c - x
)
or
inNonZeroCase = false and
exists(PointerAddInstruction lhs, int c, int x |
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
@@ -1242,14 +1156,5 @@ private predicate unary_add_eq(
)
}
private class IntegerOrPointerConstantInstruction extends ConstantInstruction {
IntegerOrPointerConstantInstruction() {
this instanceof IntegerConstantInstruction or
this instanceof PointerConstantInstruction
}
}
/** The int value of integer constant expression. */
private int int_value(Instruction i) {
result = i.(IntegerOrPointerConstantInstruction).getValue().toInt()
}
private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }

View File

@@ -1,556 +0,0 @@
/**
* INTERNAL use only. This is an experimental API subject to change without notice.
*
* Provides classes and predicates for dealing with flow models specified in CSV format.
*
* The CSV specification has the following columns:
* - Sources:
* `namespace; type; subtypes; name; signature; ext; output; kind`
* - Sinks:
* `namespace; type; subtypes; name; signature; ext; input; kind`
* - Summaries:
* `namespace; type; subtypes; name; signature; ext; input; output; kind`
*
* The interpretation of a row is similar to API-graphs with a left-to-right
* reading.
* 1. The `namespace` column selects a namespace.
* 2. The `type` column selects a type within that namespace.
* 3. The `subtypes` is a boolean that indicates whether to jump to an
* arbitrary subtype of that type. Set this to `false` if leaving the `type`
* blank (for example, a free function).
* 4. The `name` column optionally selects a specific named member of the type.
* 5. The `signature` column optionally restricts the named member. If
* `signature` is blank then no such filtering is done. The format of the
* signature is a comma-separated list of types enclosed in parentheses. The
* types can be short names or fully qualified names (mixing these two options
* is not allowed within a single signature).
* 6. The `ext` column specifies additional API-graph-like edges. Currently
* there is only one valid value: "".
* 7. The `input` column specifies how data enters the element selected by the
* first 6 columns, and the `output` column specifies how data leaves the
* element selected by the first 6 columns. An `input` can be either:
* - "": Selects a write to the selected element in case this is a field.
* - "Argument[n]": Selects an argument in a call to the selected element.
* The arguments are zero-indexed, and `-1` specifies the qualifier object,
* that is, `*this`.
* - one or more "*" can be added in front of the argument index to indicate
* indirection, for example, `Argument[*0]` indicates the first indirection
* of the 0th argument.
* - `n1..n2` syntax can be used to indicate a range of arguments, inclusive
* at both ends. One or more "*"s can be added in front of the whole range
* to indicate that every argument in the range is indirect, for example
* `*0..1` is the first indirection of both arguments 0 and 1.
* - "ReturnValue": Selects a value being returned by the selected element.
* One or more "*" can be added as an argument to indicate indirection, for
* example, "ReturnValue[*]" indicates the first indirection of the return
* value.
*
* An `output` can be either:
* - "": Selects a read of a selected field.
* - "Argument[n]": Selects the post-update value of an argument in a call to
* the selected element. That is, the value of the argument after the call
* returns. The arguments are zero-indexed, and `-1` specifies the qualifier
* object, that is, `*this`.
* - one or more "*" can be added in front of the argument index to indicate
* indirection, for example, `Argument[*0]` indicates the first indirection
* of the 0th argument.
* - `n1..n2` syntax can be used to indicate a range of arguments, inclusive
* at both ends. One or more "*"s can be added in front of the whole range
* to indicate that every argument in the range is indirect, for example
* `*0..1` is the first indirection of both arguments 0 and 1.
* - "Parameter[n]": Selects the value of a parameter of the selected element.
* The syntax is the same as for "Argument", for example "Parameter[0]",
* "Parameter[*0]", "Parameter[0..2]" etc.
* - "ReturnValue": Selects a value being returned by the selected element.
* One or more "*" can be added as an argument to indicate indirection, for
* example, "ReturnValue[*]" indicates the first indirection of the return
* value.
* 8. The `kind` column is a tag that can be referenced from QL to determine to
* which classes the interpreted elements should be added. For example, for
* sources "remote" indicates a default remote flow source, and for summaries
* "taint" indicates a default additional taint step and "value" indicates a
* globally applicable value-preserving step.
*/
import cpp
private import new.DataFlow
private import internal.FlowSummaryImpl
private import internal.FlowSummaryImpl::Public
private import internal.FlowSummaryImpl::Private
private import internal.FlowSummaryImpl::Private::External
private import codeql.mad.ModelValidation as SharedModelVal
private import codeql.util.Unit
/**
* A unit class for adding additional source model rows.
*
* Extend this class to add additional source definitions.
*/
class SourceModelCsv extends Unit {
/** Holds if `row` specifies a source definition. */
abstract predicate row(string row);
}
/**
* A unit class for adding additional sink model rows.
*
* Extend this class to add additional sink definitions.
*/
class SinkModelCsv extends Unit {
/** Holds if `row` specifies a sink definition. */
abstract predicate row(string row);
}
/**
* A unit class for adding additional summary model rows.
*
* Extend this class to add additional flow summary definitions.
*/
class SummaryModelCsv extends Unit {
/** Holds if `row` specifies a summary definition. */
abstract predicate row(string row);
}
/** Holds if `row` is a source model. */
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
/** Holds if `row` is a sink model. */
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
/** Holds if `row` is a summary model. */
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
/** Holds if a source model exists for the given parameters. */
predicate sourceModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string output, string kind, string provenance
) {
exists(string row |
sourceModel(row) and
row.splitAt(";", 0) = namespace and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = subtypes.toString() and
subtypes = [true, false] and
row.splitAt(";", 3) = name and
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = output and
row.splitAt(";", 7) = kind
) and
provenance = "manual"
}
/** Holds if a sink model exists for the given parameters. */
predicate sinkModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string kind, string provenance
) {
exists(string row |
sinkModel(row) and
row.splitAt(";", 0) = namespace and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = subtypes.toString() and
subtypes = [true, false] and
row.splitAt(";", 3) = name and
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = input and
row.splitAt(";", 7) = kind
) and
provenance = "manual"
}
/** Holds if a summary model exists for the given parameters. */
predicate summaryModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string output, string kind, string provenance
) {
exists(string row |
summaryModel(row) and
row.splitAt(";", 0) = namespace and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = subtypes.toString() and
subtypes = [true, false] and
row.splitAt(";", 3) = name and
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = input and
row.splitAt(";", 7) = output and
row.splitAt(";", 8) = kind
) and
provenance = "manual"
}
private predicate relevantNamespace(string namespace) {
sourceModel(namespace, _, _, _, _, _, _, _, _) or
sinkModel(namespace, _, _, _, _, _, _, _, _) or
summaryModel(namespace, _, _, _, _, _, _, _, _, _)
}
private predicate namespaceLink(string shortns, string longns) {
relevantNamespace(shortns) and
relevantNamespace(longns) and
longns.prefix(longns.indexOf("::")) = shortns
}
private predicate canonicalNamespace(string namespace) {
relevantNamespace(namespace) and not namespaceLink(_, namespace)
}
private predicate canonicalNamespaceLink(string namespace, string subns) {
canonicalNamespace(namespace) and
(subns = namespace or namespaceLink(namespace, subns))
}
/**
* Holds if CSV framework coverage of `namespace` is `n` api endpoints of the
* kind `(kind, part)`.
*/
predicate modelCoverage(string namespace, int namespaces, string kind, string part, int n) {
namespaces = strictcount(string subns | canonicalNamespaceLink(namespace, subns)) and
(
part = "source" and
n =
strictcount(string subns, string type, boolean subtypes, string name, string signature,
string ext, string output, string provenance |
canonicalNamespaceLink(namespace, subns) and
sourceModel(subns, type, subtypes, name, signature, ext, output, kind, provenance)
)
or
part = "sink" and
n =
strictcount(string subns, string type, boolean subtypes, string name, string signature,
string ext, string input, string provenance |
canonicalNamespaceLink(namespace, subns) and
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance)
)
or
part = "summary" and
n =
strictcount(string subns, string type, boolean subtypes, string name, string signature,
string ext, string input, string output, string provenance |
canonicalNamespaceLink(namespace, subns) and
summaryModel(subns, type, subtypes, name, signature, ext, input, output, kind, provenance)
)
)
}
/** Provides a query predicate to check the CSV data for validation errors. */
module CsvValidation {
private string getInvalidModelInput() {
exists(string pred, AccessPath input, string part |
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
or
summaryModel(_, _, _, _, _, _, input, _, _, _) and pred = "summary"
|
(
invalidSpecComponent(input, part) and
not part = "" and
not (part = "Argument" and pred = "sink") and
not parseArg(part, _)
or
part = input.getToken(_) and
parseParam(part, _)
) and
result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
)
}
private string getInvalidModelOutput() {
exists(string pred, string output, string part |
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
or
summaryModel(_, _, _, _, _, _, _, output, _, _) and pred = "summary"
|
invalidSpecComponent(output, part) and
not part = "" and
not (part = ["Argument", "Parameter"] and pred = "source") and
result = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
)
}
private module KindValConfig implements SharedModelVal::KindValidationConfigSig {
predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) }
predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) }
predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) }
}
private module KindVal = SharedModelVal::KindValidation<KindValConfig>;
private string getInvalidModelSubtype() {
exists(string pred, string row |
sourceModel(row) and pred = "source"
or
sinkModel(row) and pred = "sink"
or
summaryModel(row) and pred = "summary"
|
exists(string b |
b = row.splitAt(";", 2) and
not b = ["true", "false"] and
result = "Invalid boolean \"" + b + "\" in " + pred + " model."
)
)
}
private string getInvalidModelColumnCount() {
exists(string pred, string row, int expect |
sourceModel(row) and expect = 8 and pred = "source"
or
sinkModel(row) and expect = 8 and pred = "sink"
or
summaryModel(row) and expect = 9 and pred = "summary"
|
exists(int cols |
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
cols != expect and
result =
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
"."
)
)
}
private string getInvalidModelSignature() {
exists(string pred, string namespace, string type, string name, string signature, string ext |
sourceModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "source"
or
sinkModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "sink"
or
summaryModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "summary"
|
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
or
not type.regexpMatch("[a-zA-Z0-9_<>,\\+]+") and
result = "Dubious type \"" + type + "\" in " + pred + " model."
or
not name.regexpMatch("[a-zA-Z0-9_<>,]*") and
result = "Dubious member name \"" + name + "\" in " + pred + " model."
or
not signature.regexpMatch("|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)") and
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
or
not ext.regexpMatch("|Attribute") and
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
)
}
/** Holds if some row in a CSV-based flow model appears to contain typos. */
query predicate invalidModelRow(string msg) {
msg =
[
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind()
]
}
}
private predicate elementSpec(
string namespace, string type, boolean subtypes, string name, string signature, string ext
) {
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
}
private string paramsStringPart(Function c, int i) {
i = -1 and result = "(" and exists(c)
or
exists(int n, string p | c.getParameter(n).getType().toString() = p |
i = 2 * n and result = p
or
i = 2 * n - 1 and result = "," and n != 0
)
or
i = 2 * c.getNumberOfParameters() and result = ")"
}
/**
* Gets a parenthesized string containing all parameter types of this callable, separated by a comma.
*
* Returns the empty string if the callable has no parameters.
* Parameter types are represented by their type erasure.
*/
cached
private string paramsString(Function c) {
result = concat(int i | | paramsStringPart(c, i) order by i)
}
bindingset[func]
private predicate matchesSignature(Function func, string signature) {
signature = "" or
paramsString(func) = signature
}
/**
* Gets the element in module `namespace` that satisfies the following properties:
* 1. If the element is a member of a class-like type, then the class-like type has name `type`
* 2. If `subtypes = true` and the element is a member of a class-like type, then overrides of the element
* are also returned.
* 3. The element has name `name`
* 4. If `signature` is non-empty, then the element has a list of parameter types described by `signature`.
*
* NOTE: `namespace` is currently not used (since we don't properly extract modules yet).
*/
pragma[nomagic]
private Element interpretElement0(
string namespace, string type, boolean subtypes, string name, string signature
) {
elementSpec(namespace, type, subtypes, name, signature, _) and
(
// Non-member functions
exists(Function func |
func.hasQualifiedName(namespace, name) and
type = "" and
matchesSignature(func, signature) and
subtypes = false and
not exists(func.getDeclaringType()) and
result = func
)
or
// Member functions
exists(Class namedClass, Class classWithMethod, Function method |
classWithMethod = method.getClassAndName(name) and
namedClass.hasQualifiedName(namespace, type) and
matchesSignature(method, signature) and
result = method
|
// member declared in the named type or a subtype of it
subtypes = true and
classWithMethod = namedClass.getADerivedClass*()
or
// member declared directly in the named type
subtypes = false and
classWithMethod = namedClass
)
or
// Member variables
signature = "" and
exists(Class namedClass, Class classWithMember, MemberVariable member |
member.getName() = name and
member = classWithMember.getAMember() and
namedClass.hasQualifiedName(namespace, type) and
result = member
|
// field declared in the named type or a subtype of it (or an extension of any)
subtypes = true and
classWithMember = namedClass.getADerivedClass*()
or
// field declared directly in the named type (or an extension of it)
subtypes = false and
classWithMember = namedClass
)
or
// Global or namespace variables
signature = "" and
type = "" and
subtypes = false and
result = any(GlobalOrNamespaceVariable v | v.hasQualifiedName(namespace, name))
)
}
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
Element interpretElement(
string namespace, string type, boolean subtypes, string name, string signature, string ext
) {
elementSpec(namespace, type, subtypes, name, signature, ext) and
exists(Element e | e = interpretElement0(namespace, type, subtypes, name, signature) |
ext = "" and result = e
)
}
cached
private module Cached {
/**
* Holds if `node` is specified as a source with the given kind in a CSV flow
* model.
*/
cached
predicate sourceNode(DataFlow::Node node, string kind) {
exists(SourceSinkInterpretationInput::InterpretNode n |
isSourceNode(n, kind, _) and n.asNode() = node // TODO
)
}
/**
* Holds if `node` is specified as a sink with the given kind in a CSV flow
* model.
*/
cached
predicate sinkNode(DataFlow::Node node, string kind) {
exists(SourceSinkInterpretationInput::InterpretNode n |
isSinkNode(n, kind, _) and n.asNode() = node // TODO
)
}
}
import Cached
private predicate interpretSummary(
Function f, string input, string output, string kind, string provenance
) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
f = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
// adapter class for converting Mad summaries to `SummarizedCallable`s
private class SummarizedCallableAdapter extends SummarizedCallable {
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _) }
private predicate relevantSummaryElementManual(string input, string output, string kind) {
exists(Provenance provenance |
interpretSummary(this, input, output, kind, provenance) and
provenance.isManual()
)
}
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
exists(Provenance provenance |
interpretSummary(this, input, output, kind, provenance) and
provenance.isGenerated()
)
}
override predicate propagatesFlow(
string input, string output, boolean preservesValue, string model
) {
exists(string kind |
this.relevantSummaryElementManual(input, output, kind)
or
not this.relevantSummaryElementManual(_, _, _) and
this.relevantSummaryElementGenerated(input, output, kind)
|
if kind = "value" then preservesValue = true else preservesValue = false
) and
model = "" // TODO
}
override predicate hasProvenance(Provenance provenance) {
interpretSummary(this, _, _, _, provenance)
}
}
// adapter class for converting Mad neutrals to `NeutralCallable`s
private class NeutralCallableAdapter extends NeutralCallable {
string kind;
string provenance_;
NeutralCallableAdapter() {
// Neutral models have not been implemented for CPP.
none() and
exists(this) and
exists(kind) and
exists(provenance_)
}
override string getKind() { result = kind }
override predicate hasProvenance(Provenance provenance) { provenance = provenance_ }
}

View File

@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
predicate isBarrierOut(Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2) and
model = ""
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {

View File

@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
predicate isBarrierOut(Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2) and
model = ""
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {

View File

@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
predicate isBarrierOut(Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2) and
model = ""
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {

View File

@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
predicate isBarrierOut(Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2) and
model = ""
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {

View File

@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
predicate isBarrierOut(Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2) and
model = ""
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {

View File

@@ -286,12 +286,6 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { no
/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
predicate knownSourceModel(Node source, string model) { none() }
predicate knownSinkModel(Node sink, string model) { none() }
class DataFlowSecondLevelScope = Unit;
/**
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
* side-effect, resulting in a summary from `p` to itself.

View File

@@ -516,7 +516,7 @@ private module ThisFlow {
*/
cached
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
simpleLocalFlowStep(nodeFrom, 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
@@ -530,67 +530,64 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
* 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, string model) {
(
// 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()
)
) and
model = ""
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()
)
}
/**

View File

@@ -1,271 +0,0 @@
/**
* Provides classes and predicates for defining flow summaries.
*/
private import cpp as Cpp
private import codeql.dataflow.internal.FlowSummaryImpl
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific as DataFlowImplSpecific
private import semmle.code.cpp.dataflow.ExternalFlow
private import semmle.code.cpp.ir.IR
module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
class SummarizedCallableBase = Function;
ArgumentPosition callbackSelfParameterPosition() { result = TDirectPosition(-1) }
ReturnKind getStandardReturnValueKind() { result.(NormalReturnKind).getIndirectionIndex() = 0 }
string encodeParameterPosition(ParameterPosition pos) { result = pos.toString() }
string encodeArgumentPosition(ArgumentPosition pos) { result = pos.toString() }
string encodeReturn(ReturnKind rk, string arg) {
rk != getStandardReturnValueKind() and
result = "ReturnValue" and
arg = repeatStars(rk.(NormalReturnKind).getIndirectionIndex())
}
string encodeContent(ContentSet cs, string arg) {
exists(FieldContent c |
cs.isSingleton(c) and
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
result = "Field" and
arg = repeatStars(c.getIndirectionIndex() - 1) + c.getField().getName()
)
}
string encodeWithoutContent(ContentSet c, string arg) {
// used for type tracking, not currently used in C/C++.
result = "WithoutContent" + c and arg = ""
}
string encodeWithContent(ContentSet c, string arg) {
// used for type tracking, not currently used in C/C++.
result = "WithContent" + c and arg = ""
}
/**
* Decodes an argument / parameter position string, for example the `0` in `Argument[0]`.
* Supports ranges (`Argument[x..y]`), qualifiers (`Argument[-1]`), indirections
* (`Argument[*x]`) and combinations (such as `Argument[**0..1]`).
*/
bindingset[argString]
private TPosition decodePosition(string argString) {
exists(int indirection, string posString, int pos |
argString = repeatStars(indirection) + posString and
pos = AccessPath::parseInt(posString) and
(
pos >= 0 and indirection = 0 and result = TDirectPosition(pos)
or
pos >= 0 and indirection > 0 and result = TIndirectionPosition(pos, indirection)
or
// `Argument[-1]` / `Parameter[-1]` is the qualifier object `*this`, not the `this` pointer itself.
pos = -1 and result = TIndirectionPosition(pos, indirection + 1)
)
)
}
bindingset[token]
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
token.getName() = "Argument" and
result = decodePosition(token.getAnArgument())
}
bindingset[token]
ArgumentPosition decodeUnknownArgumentPosition(AccessPath::AccessPathTokenBase token) {
token.getName() = "Parameter" and
result = decodePosition(token.getAnArgument())
}
bindingset[token]
ContentSet decodeUnknownContent(AccessPath::AccessPathTokenBase token) {
// field content (no indirection support)
exists(FieldContent c |
result.isSingleton(c) and
token.getName() = c.getField().getName() and
not exists(token.getArgumentList()) and
c.getIndirectionIndex() = 1
)
or
// field content (with indirection support)
exists(FieldContent c |
result.isSingleton(c) and
token.getName() = c.getField().getName() and
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
token.getAnArgument() = repeatStars(c.getIndirectionIndex() - 1)
)
}
}
private import Make<Location, DataFlowImplSpecific::CppDataFlow, Input> as Impl
private module StepsInput implements Impl::Private::StepsInputSig {
DataFlowCall getACall(Public::SummarizedCallable sc) {
result.getStaticCallTarget().getUnderlyingCallable() = sc
}
}
module SourceSinkInterpretationInput implements
Impl::Private::External::SourceSinkInterpretationInputSig
{
class Element = Cpp::Element;
class SourceOrSinkElement = Element;
/**
* Holds if an external source specification exists for `e` with output specification
* `output`, kind `kind`, and provenance `provenance`.
*/
predicate sourceElement(
SourceOrSinkElement e, string output, string kind, Public::Provenance provenance, string model
) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
e = interpretElement(namespace, type, subtypes, name, signature, ext) and
model = "" // TODO
)
}
/**
* Holds if an external sink specification exists for `e` with input specification
* `input`, kind `kind` and provenance `provenance`.
*/
predicate sinkElement(
SourceOrSinkElement e, string input, string kind, Public::Provenance provenance, string model
) {
exists(
string package, string type, boolean subtypes, string name, string signature, string ext
|
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance) and
e = interpretElement(package, type, subtypes, name, signature, ext) and
model = "" // TODO
)
}
private newtype TInterpretNode =
TElement_(Element n) or
TNode_(Node n)
/** An entity used to interpret a source/sink specification. */
class InterpretNode extends TInterpretNode {
/** Gets the element that this node corresponds to, if any. */
SourceOrSinkElement asElement() { this = TElement_(result) }
/** Gets the data-flow node that this node corresponds to, if any. */
Node asNode() { this = TNode_(result) }
/** Gets the call that this node corresponds to, if any. */
DataFlowCall asCall() {
this.asElement() = result.asCallInstruction().getUnconvertedResultExpression()
}
/** Gets the callable that this node corresponds to, if any. */
DataFlowCallable asCallable() { result.getUnderlyingCallable() = this.asElement() }
/** Gets the target of this call, if any. */
Element getCallTarget() { result = this.asCall().getStaticCallTarget().getUnderlyingCallable() }
/** Gets a textual representation of this node. */
string toString() {
result = this.asElement().toString()
or
result = this.asNode().toString()
or
result = this.asCall().toString()
}
/** Gets the location of this node. */
Location getLocation() {
result = this.asElement().getLocation()
or
result = this.asNode().getLocation()
or
result = this.asCall().getLocation()
}
}
/** Provides additional sink specification logic. */
bindingset[c]
predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) {
// Allow variables to be picked as output nodes.
exists(Node n, Element ast |
n = node.asNode() and
ast = mid.asElement()
|
c = "" and
n.asExpr().(VariableAccess).getTarget() = ast
)
}
/** Provides additional source specification logic. */
bindingset[c]
predicate interpretInput(string c, InterpretNode mid, InterpretNode node) {
exists(Node n, Element ast, VariableAccess e |
n = node.asNode() and
ast = mid.asElement() and
e.getTarget() = ast
|
// Allow variables to be picked as input nodes.
// We could simply do this as `e = n.asExpr()`, but that would not allow
// us to pick `x` as a sink in an example such as `x = source()` (but
// only subsequent uses of `x`) since the variable access on `x` doesn't
// actually load the value of `x`. So instead, we pick the instruction
// node corresponding to the generated `StoreInstruction` and use the
// expression associated with the destination instruction. This means
// that the `x` in `x = source()` can be marked as an input.
c = "" and
exists(StoreInstruction store |
store.getDestinationAddress().getUnconvertedResultExpression() = e and
n.asInstruction() = store
)
)
}
}
module Private {
import Impl::Private
module Steps = Impl::Private::Steps<StepsInput>;
module External {
import Impl::Private::External
import Impl::Private::External::SourceSinkInterpretation<SourceSinkInterpretationInput>
}
/**
* Provides predicates for constructing summary components.
*/
module SummaryComponent {
private import Impl::Private::SummaryComponent as SC
predicate parameter = SC::parameter/1;
predicate argument = SC::argument/1;
predicate content = SC::content/1;
predicate withoutContent = SC::withoutContent/1;
predicate withContent = SC::withContent/1;
}
/**
* Provides predicates for constructing stacks of summary components.
*/
module SummaryComponentStack {
private import Impl::Private::SummaryComponentStack as SCS
predicate singleton = SCS::singleton/1;
predicate push = SCS::push/2;
predicate argument = SCS::argument/1;
}
}
module Public = Impl::Public;

View File

@@ -32,8 +32,8 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
* Holds if the additional step from `src` to `sink` should be included in all
* global taint flow configurations.
*/
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink, string model) {
localAdditionalTaintStep(src, sink) and model = ""
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintStep(src, sink)
}
/**

View File

@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2, _)
defaultAdditionalTaintStep(node1, node2)
}
/**

View File

@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2, _)
defaultAdditionalTaintStep(node1, node2)
}
/**

View File

@@ -63,12 +63,6 @@ class Expr extends StmtParent, @expr {
* order of destruction.
*/
DestructorCall getImplicitDestructorCall(int n) {
exists(Expr e |
e = this.(TemporaryObjectExpr).getExpr() and
synthetic_destructor_call(e, max(int i | synthetic_destructor_call(e, i, _)) - n, result)
)
or
not this = any(TemporaryObjectExpr temp).getExpr() and
synthetic_destructor_call(this, max(int i | synthetic_destructor_call(this, i, _)) - n, result)
}
@@ -1338,24 +1332,6 @@ class CoAwaitExpr extends UnaryOperation, @co_await {
override string getOperator() { result = "co_await" }
override int getPrecedence() { result = 16 }
/**
* Gets the Boolean expression that is used to decide if the enclosing
* coroutine should be suspended.
*/
Expr getAwaitReady() { result = this.getChild(1) }
/**
* Gets the expression that represents the resume point if the enclosing
* coroutine was suspended.
*/
Expr getAwaitResume() { result = this.getChild(2) }
/**
* Gets the expression that is evaluated when the enclosing coroutine is
* suspended.
*/
Expr getAwaitSuspend() { result = this.getChild(3) }
}
/**
@@ -1370,24 +1346,6 @@ class CoYieldExpr extends UnaryOperation, @co_yield {
override string getOperator() { result = "co_yield" }
override int getPrecedence() { result = 2 }
/**
* Gets the Boolean expression that is used to decide if the enclosing
* coroutine should be suspended.
*/
Expr getAwaitReady() { result = this.getChild(1) }
/**
* Gets the expression that represents the resume point if the enclosing
* coroutine was suspended.
*/
Expr getAwaitResume() { result = this.getChild(2) }
/**
* Gets the expression that is evaluated when the enclosing coroutine is
* suspended.
*/
Expr getAwaitSuspend() { result = this.getChild(3) }
}
/**
@@ -1407,7 +1365,17 @@ class ReuseExpr extends Expr, @reuseexpr {
/**
* Gets the expression that is being re-used.
*/
Expr getReusedExpr() { expr_reuse(underlyingElement(this), unresolveElement(result), _) }
Expr getReusedExpr() {
// In the case of a prvalue, the extractor outputs the expression
// before conversion, but the converted expression is intended.
if this.isPRValueCategory()
then result = this.getBaseReusedExpr().getFullyConverted()
else result = this.getBaseReusedExpr()
}
private Expr getBaseReusedExpr() {
expr_reuse(underlyingElement(this), unresolveElement(result), _)
}
override Type getType() { result = this.getReusedExpr().getType() }

View File

@@ -7,7 +7,6 @@ import cpp
private import semmle.code.cpp.ir.ValueNumbering
private import internal.DataFlowDispatch
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
/**
* Resolve potential target function(s) for `call`.
@@ -17,9 +16,8 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
* to identify the possible target(s).
*/
Function resolveCall(Call call) {
exists(DataFlowCall dataFlowCall, CallInstruction callInstruction |
exists(CallInstruction callInstruction |
callInstruction.getAst() = call and
callInstruction = dataFlowCall.asCallInstruction() and
result = viableCallable(dataFlowCall).getUnderlyingCallable()
result = viableCallable(callInstruction)
)
}

View File

@@ -23,13 +23,13 @@ DataFlowCallable defaultViableCallable(DataFlowCall call) {
// 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.asCallInstruction()) and
functionSignatureWithBody(qualifiedName, nparams, result.getUnderlyingCallable()) and
callSignatureWithoutBody(qualifiedName, nparams, call) and
functionSignatureWithBody(qualifiedName, nparams, result) and
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
)
or
// Virtual dispatch
result.asSourceCallable() = call.(VirtualDispatch::DataSensitiveCall).resolve()
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
}
/**
@@ -40,9 +40,7 @@ DataFlowCallable viableCallable(DataFlowCall call) {
result = defaultViableCallable(call)
or
// Additional call targets
result.getUnderlyingCallable() =
any(AdditionalCallTarget additional)
.viableTarget(call.asCallInstruction().getUnconvertedResultExpression())
result = any(AdditionalCallTarget additional).viableTarget(call.getUnconvertedResultExpression())
}
/**
@@ -152,7 +150,7 @@ private module VirtualDispatch {
ReturnNode node, ReturnKind kind, DataFlowCallable callable
) {
node.getKind() = kind and
node.getEnclosingCallable() = callable.getUnderlyingCallable()
node.getEnclosingCallable() = callable
}
/** Call through a function pointer. */
@@ -178,15 +176,10 @@ private module VirtualDispatch {
/** Call to a virtual function. */
private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
DataSensitiveOverriddenFunctionCall() {
exists(
this.getStaticCallTarget()
.getUnderlyingCallable()
.(VirtualFunction)
.getAnOverridingFunction()
)
exists(this.getStaticCallTarget().(VirtualFunction).getAnOverridingFunction())
}
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getArgument(-1) }
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getThisArgument() }
override MemberFunction resolve() {
exists(Class overridingClass |
@@ -201,8 +194,7 @@ private module VirtualDispatch {
*/
pragma[noinline]
private predicate overrideMayAffectCall(Class overridingClass, MemberFunction overridingFunction) {
overridingFunction.getAnOverriddenFunction+() =
this.getStaticCallTarget().getUnderlyingCallable().(VirtualFunction) and
overridingFunction.getAnOverriddenFunction+() = this.getStaticCallTarget().(VirtualFunction) and
overridingFunction.getDeclaringType() = overridingClass
}
@@ -264,12 +256,12 @@ predicate mayBenefitFromCallContext(DataFlowCall call) { mayBenefitFromCallConte
* value is given as the `arg`'th argument to `f`.
*/
private predicate mayBenefitFromCallContext(
VirtualDispatch::DataSensitiveCall call, DataFlowCallable f, int arg
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.getUnderlyingCallable() and
init.getEnclosingFunction() = f and
call.flowsFrom(DataFlow::instructionNode(init), _) and
init.getParameter().getIndex() = arg
)
@@ -281,11 +273,10 @@ private predicate mayBenefitFromCallContext(
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
result = viableCallable(call) and
exists(int i, DataFlowCallable f |
exists(int i, Function f |
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and
f = ctx.getStaticCallTarget() and
result.asSourceCallable() =
ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
result = ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
)
}

View File

@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
predicate isBarrierOut(Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2) and
model = ""
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {

View File

@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
predicate isBarrierOut(Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2) and
model = ""
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {

View File

@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
predicate isBarrierOut(Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2) and
model = ""
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {

View File

@@ -263,10 +263,9 @@ deprecated private module Config implements FullStateConfigSig {
predicate isBarrierOut(Node node, FlowState state) { none() }
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2) and
model = ""
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {

View File

@@ -22,13 +22,9 @@ module CppDataFlow implements InputSig<Location> {
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
predicate getSecondLevelScope = Private::getSecondLevelScope/1;
predicate validParameterAliasStep = Private::validParameterAliasStep/2;
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1;
}

View File

@@ -10,7 +10,6 @@ private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import DataFlowPrivate
private import ModelUtil
private import SsaInternals as Ssa
@@ -46,7 +45,6 @@ private newtype TIRDataFlowNode =
Ssa::isModifiableByCall(operand, indirectionIndex)
} or
TSsaPhiNode(Ssa::PhiNode phi) or
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
} or
@@ -61,17 +59,7 @@ private newtype TIRDataFlowNode =
)
} or
TFinalGlobalValue(Ssa::GlobalUse globalUse) or
TInitialGlobalValue(Ssa::GlobalDef globalUse) or
TBodyLessParameterNodeImpl(Parameter p, int indirectionIndex) {
// Rule out parameters of catch blocks.
not exists(p.getCatchBlock()) and
// We subtract one because `getMaxIndirectionsForType` returns the maximum
// indirection for a glvalue of a given type, and this doesn't apply to
// parameters.
indirectionIndex = [0 .. Ssa::getMaxIndirectionsForType(p.getUnspecifiedType()) - 1] and
not any(InitializeParameterInstruction init).getParameter() = p
} or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn)
TInitialGlobalValue(Ssa::GlobalDef globalUse)
/**
* An operand that is defined by a `FieldAddressInstruction`.
@@ -399,7 +387,7 @@ class Node extends TIRDataFlowNode {
index = 0 and
result = this.(ExplicitParameterNode).getParameter()
or
this.(IndirectParameterNode).getIndirectionIndex() = index and
this.(IndirectParameterNode).hasInstructionAndIndirectionIndex(_, index) and
result = this.(IndirectParameterNode).getParameter()
}
@@ -654,30 +642,6 @@ class SsaPhiNode extends Node, TSsaPhiNode {
predicate isPhiRead() { phi.isPhiRead() }
}
/**
* INTERNAL: do not use.
*
* Dataflow nodes necessary for iterator flow
*/
class SsaIteratorNode extends Node, TSsaIteratorNode {
IteratorFlow::IteratorFlowNode node;
SsaIteratorNode() { this = TSsaIteratorNode(node) }
/** Gets the phi node associated with this node. */
IteratorFlow::IteratorFlowNode getIteratorFlowNode() { result = node }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = node.getFunction() }
override DataFlowType getType() { result = node.getType() }
final override Location getLocationImpl() { result = node.getLocation() }
override string toStringImpl() { result = node.toString() }
}
/**
* INTERNAL: do not use.
*
@@ -774,65 +738,37 @@ class InitialGlobalValue extends Node, TInitialGlobalValue {
/**
* INTERNAL: do not use.
*
* A node representing a parameter for a function with no body.
* A node representing an indirection of a parameter.
*/
class BodyLessParameterNodeImpl extends Node, TBodyLessParameterNodeImpl {
Parameter p;
int indirectionIndex;
class IndirectParameterNode extends Node instanceof IndirectInstruction {
InitializeParameterInstruction init;
BodyLessParameterNodeImpl() { this = TBodyLessParameterNodeImpl(p, indirectionIndex) }
IndirectParameterNode() { IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _) }
int getArgumentIndex() { init.hasIndex(result) }
/** Gets the parameter whose indirection is initialized. */
Parameter getParameter() { result = init.getParameter() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = p.getFunction() }
override Declaration getFunction() { result = init.getEnclosingFunction() }
/** Gets the indirection index of this node. */
int getIndirectionIndex() { result = indirectionIndex }
override DataFlowType getType() {
result = getTypeImpl(p.getUnderlyingType(), this.getIndirectionIndex())
/** Gets the underlying operand and the underlying indirection index. */
predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index)
}
final override Location getLocationImpl() {
result = unique( | | p.getLocation())
or
count(p.getLocation()) != 1 and
result instanceof UnknownDefaultLocation
override Location getLocationImpl() { result = this.getParameter().getLocation() }
override string toStringImpl() {
exists(string prefix | prefix = stars(this) |
result = prefix + this.getParameter().toString()
or
not exists(this.getParameter()) and
result = prefix + "this"
)
}
final override string toStringImpl() {
exists(string prefix | prefix = stars(this) | result = prefix + p.toString())
}
}
/**
* A data-flow node used to model flow summaries. That is, a dataflow node
* that is synthesized to represent a parameter, return value, or other part
* of a models-as-data modeled function.
*/
class FlowSummaryNode extends Node, TFlowSummaryNode {
/**
* Gets the models-as-data `SummaryNode` associated with this dataflow
* `FlowSummaryNode`.
*/
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
/**
* Gets the summarized callable that this node belongs to.
*/
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() {
result = this.getSummaryNode().getSummarizedCallable()
}
/**
* Gets the enclosing callable. For a `FlowSummaryNode` this is always the
* summarized function this node is part of.
*/
override Declaration getEnclosingCallable() { result = this.getSummarizedCallable() }
override Location getLocationImpl() { result = this.getSummarizedCallable().getLocation() }
override string toStringImpl() { result = this.getSummaryNode().toString() }
}
/**
@@ -890,9 +826,6 @@ class IndirectArgumentOutNode extends PostUpdateNodeImpl {
CallInstruction getCallInstruction() { result.getAnArgumentOperand() = operand }
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
Function getStaticCallTarget() { result = this.getCallInstruction().getStaticCallTarget() }
override string toStringImpl() {
@@ -1215,11 +1148,11 @@ class UninitializedNode extends Node {
LocalVariable v;
UninitializedNode() {
exists(Ssa::Def def, Ssa::SourceVariable sv |
exists(Ssa::Def def |
def.getIndirectionIndex() = 0 and
def.getValue().asInstruction() instanceof UninitializedInstruction and
Ssa::defToNode(this, def, sv, _, _, _) and
v = sv.getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
Ssa::nodeToDefOrUse(this, def, _) and
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
)
}
@@ -1687,36 +1620,68 @@ class IndirectExprNode extends Node instanceof IndirectExprNodeBase {
}
}
abstract private class AbstractParameterNode extends Node {
/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
* and implicit parameters such as `this` in `x.f()`.
*
* To match a specific kind of parameter, consider using one of the subclasses
* `ExplicitParameterNode`, `ThisParameterNode`, or
* `ParameterIndirectionNode`.
*/
class ParameterNode extends Node {
ParameterNode() {
// To avoid making this class abstract, we enumerate its values here
this.asInstruction() instanceof InitializeParameterInstruction
or
this instanceof IndirectParameterNode
}
/**
* Holds if this node is the parameter of `f` at the specified position. The
* implicit `this` parameter is considered to have position `-1`, and
* pointer-indirection parameters are at further negative positions.
*/
abstract predicate isParameterOf(DataFlowCallable f, ParameterPosition pos);
predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses
/** Gets the `Parameter` associated with this node, if it exists. */
Parameter getParameter() { none() } // overridden by subclasses
}
abstract private class AbstractIndirectParameterNode extends AbstractParameterNode {
/** Gets the indirection index of this parameter node. */
abstract int getIndirectionIndex();
/** An explicit positional parameter, including `this`, but not `...`. */
class DirectParameterNode extends InstructionNode {
override InitializeParameterInstruction instr;
/**
* INTERNAL: Do not use.
*
* Gets the `IRVariable` that this parameter references.
*/
IRVariable getIRVariable() { result = instr.getIRVariable() }
}
/**
* INTERNAL: do not use.
*
* A node representing an indirection of a parameter.
*/
final class IndirectParameterNode = AbstractIndirectParameterNode;
/** An explicit positional parameter, not including `this` or `...`. */
private class ExplicitParameterNode extends ParameterNode, DirectParameterNode {
ExplicitParameterNode() { exists(instr.getParameter()) }
pragma[noinline]
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
IndirectInstructionParameterNode node, int argumentIndex, int indirectionIndex
) {
node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and
node.getArgumentIndex() = argumentIndex
override predicate isParameterOf(Function f, ParameterPosition pos) {
f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter()
}
override string toStringImpl() { result = instr.getParameter().toString() }
override Parameter getParameter() { result = instr.getParameter() }
}
/** An implicit `this` parameter. */
class ThisParameterNode extends ParameterNode, DirectParameterNode {
ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
override predicate isParameterOf(Function f, ParameterPosition pos) {
pos.(DirectPosition).getIndex() = -1 and instr.getEnclosingFunction() = f
}
override string toStringImpl() { result = "this" }
}
pragma[noinline]
@@ -1727,167 +1692,23 @@ private predicate indirectPositionHasArgumentIndexAndIndex(
pos.getIndirectionIndex() = indirectionIndex
}
private class IndirectInstructionParameterNode extends AbstractIndirectParameterNode instanceof IndirectInstruction
{
InitializeParameterInstruction init;
pragma[noinline]
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
IndirectParameterNode node, int argumentIndex, int indirectionIndex
) {
node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and
node.getArgumentIndex() = argumentIndex
}
IndirectInstructionParameterNode() {
IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _)
}
int getArgumentIndex() { init.hasIndex(result) }
override string toStringImpl() {
exists(string prefix | prefix = stars(this) |
result = prefix + this.getParameter().toString()
or
not exists(this.getParameter()) and
result = prefix + "this"
)
}
/** Gets the parameter whose indirection is initialized. */
override Parameter getParameter() { result = init.getParameter() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = init.getEnclosingFunction() }
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
this.getEnclosingCallable() = f.getUnderlyingCallable() and
/** A synthetic parameter to model the pointed-to object of a pointer parameter. */
class ParameterIndirectionNode extends ParameterNode instanceof IndirectParameterNode {
override predicate isParameterOf(Function f, ParameterPosition pos) {
IndirectParameterNode.super.getEnclosingCallable() = f and
exists(int argumentIndex, int indirectionIndex |
indirectPositionHasArgumentIndexAndIndex(pos, argumentIndex, indirectionIndex) and
indirectParameterNodeHasArgumentIndexAndIndex(this, argumentIndex, indirectionIndex)
)
}
/** Gets the underlying operand and the underlying indirection index. */
predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index)
}
final override int getIndirectionIndex() { this.hasInstructionAndIndirectionIndex(init, result) }
}
/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
* and implicit parameters such as `this` in `x.f()`.
*
* To match a specific kind of parameter, consider using one of the subclasses
* `ExplicitParameterNode`, `ThisParameterNode`, or
* `ParameterIndirectionNode`.
*/
final class ParameterNode = AbstractParameterNode;
abstract private class AbstractDirectParameterNode extends AbstractParameterNode { }
/** An explicit positional parameter, including `this`, but not `...`. */
final class DirectParameterNode = AbstractDirectParameterNode;
/**
* INTERNAL: Do not use.
*
* A non-indirect parameter node that is represented as an `Instruction`.
*/
abstract class InstructionDirectParameterNode extends InstructionNode, AbstractDirectParameterNode {
final override InitializeParameterInstruction instr;
/**
* INTERNAL: Do not use.
*
* Gets the `IRVariable` that this parameter references.
*/
final IRVariable getIRVariable() { result = instr.getIRVariable() }
}
abstract private class AbstractExplicitParameterNode extends AbstractDirectParameterNode { }
final class ExplicitParameterNode = AbstractExplicitParameterNode;
/** An explicit positional parameter, not including `this` or `...`. */
private class ExplicitParameterInstructionNode extends AbstractExplicitParameterNode,
InstructionDirectParameterNode
{
ExplicitParameterInstructionNode() { exists(instr.getParameter()) }
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
f.getUnderlyingCallable().(Function).getParameter(pos.(DirectPosition).getIndex()) =
instr.getParameter()
}
override string toStringImpl() { result = instr.getParameter().toString() }
override Parameter getParameter() { result = instr.getParameter() }
}
/** An implicit `this` parameter. */
class ThisParameterInstructionNode extends AbstractExplicitParameterNode,
InstructionDirectParameterNode
{
ThisParameterInstructionNode() { instr.getIRVariable() instanceof IRThisVariable }
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
pos.(DirectPosition).getIndex() = -1 and
instr.getEnclosingFunction() = f.getUnderlyingCallable()
}
override string toStringImpl() { result = "this" }
}
/**
* A parameter node that is part of a summary.
*/
class SummaryParameterNode extends AbstractParameterNode, FlowSummaryNode {
SummaryParameterNode() {
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), _)
}
private ParameterPosition getPosition() {
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), result)
}
override predicate isParameterOf(DataFlowCallable c, ParameterPosition p) {
c.getUnderlyingCallable() = this.getSummarizedCallable() and
p = this.getPosition()
}
}
private class DirectBodyLessParameterNode extends AbstractExplicitParameterNode,
BodyLessParameterNodeImpl
{
DirectBodyLessParameterNode() { indirectionIndex = 0 }
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
exists(Function func |
this.getFunction() = func and
f.asSourceCallable() = func and
func.getParameter(pos.(DirectPosition).getIndex()) = p
)
}
override Parameter getParameter() { result = p }
}
private class IndirectBodyLessParameterNode extends AbstractIndirectParameterNode,
BodyLessParameterNodeImpl
{
IndirectBodyLessParameterNode() { not this instanceof DirectBodyLessParameterNode }
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
exists(Function func, int argumentPosition |
this.getFunction() = func and
f.asSourceCallable() = func and
indirectPositionHasArgumentIndexAndIndex(pos, argumentPosition, indirectionIndex) and
func.getParameter(argumentPosition) = p
)
}
override int getIndirectionIndex() {
result = BodyLessParameterNodeImpl.super.getIndirectionIndex()
}
override Parameter getParameter() { result = p }
}
/**
@@ -1932,22 +1753,6 @@ abstract private class PartialDefinitionNode extends PostUpdateNode {
abstract Expr getDefinedExpr();
}
/**
* A `PostUpdateNode` that is part of a flow summary. These are synthesized,
* for example, when a models-as-data summary models a write to a field since
* the write needs to target a `PostUpdateNode`.
*/
class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode {
SummaryPostUpdateNode() {
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(), _)
}
override Node getPreUpdateNode() {
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(),
result.(FlowSummaryNode).getSummaryNode())
}
}
/**
* 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.
@@ -2084,20 +1889,10 @@ cached
private module Cached {
/**
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step. This relation is only used for local dataflow
* (for example `DataFlow::localFlow(source, sink)`) so it contains
* special cases that should only apply to local dataflow.
* (intra-procedural) step.
*/
cached
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
// common dataflow steps
simpleLocalFlowStep(nodeFrom, nodeTo, _)
or
// models-as-data summarized flow for local data flow (i.e. special case for flow
// through calls to modeled functions, without relying on global dataflow to join
// the dots).
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
}
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
private predicate indirectionOperandFlow(RawIndirectOperand nodeFrom, Node nodeTo) {
nodeFrom != nodeTo and
@@ -2163,56 +1958,45 @@ private module Cached {
/**
* INTERNAL: do not use.
*
* This is the local flow predicate that's used as a building block in both
* local and global data flow. It may have less flow than the `localFlowStep`
* predicate.
* 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.
*/
cached
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
(
// Post update node -> Node flow
Ssa::postUpdateFlow(nodeFrom, nodeTo)
or
// Def-use/Use-use flow
Ssa::ssaFlow(nodeFrom, nodeTo)
or
IteratorFlow::localFlowStep(nodeFrom, nodeTo)
or
// Operand -> Instruction flow
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
or
// Instruction -> Operand flow
exists(Instruction iFrom, Operand opTo |
iFrom = nodeFrom.asInstruction() and opTo = nodeTo.asOperand()
|
simpleOperandLocalFlowStep(iFrom, opTo) and
// Omit when the instruction node also represents the operand.
not iFrom = Ssa::getIRRepresentationOfOperand(opTo)
)
or
// Phi node -> Node flow
Ssa::fromPhiNode(nodeFrom, nodeTo)
or
// Indirect operand -> (indirect) instruction flow
indirectionOperandFlow(nodeFrom, nodeTo)
or
// Indirect instruction -> indirect operand flow
indirectionInstructionFlow(nodeFrom, nodeTo)
) and
model = ""
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Post update node -> Node flow
Ssa::postUpdateFlow(nodeFrom, nodeTo)
or
// Def-use/Use-use flow
Ssa::ssaFlow(nodeFrom, nodeTo)
or
// Operand -> Instruction flow
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
or
// Instruction -> Operand flow
exists(Instruction iFrom, Operand opTo |
iFrom = nodeFrom.asInstruction() and opTo = nodeTo.asOperand()
|
simpleOperandLocalFlowStep(iFrom, opTo) and
// Omit when the instruction node also represents the operand.
not iFrom = Ssa::getIRRepresentationOfOperand(opTo)
)
or
// Phi node -> Node flow
Ssa::fromPhiNode(nodeFrom, nodeTo)
or
// Indirect operand -> (indirect) instruction flow
indirectionOperandFlow(nodeFrom, nodeTo)
or
// Indirect instruction -> indirect operand flow
indirectionInstructionFlow(nodeFrom, nodeTo)
or
// Flow through modeled functions
modelFlow(nodeFrom, nodeTo, model)
modelFlow(nodeFrom, nodeTo)
or
// Reverse flow: data that flows from the definition node back into the indirection returned
// by a function. This allows data to flow 'in' through references returned by a modeled
// function such as `operator[]`.
reverseFlow(nodeFrom, nodeTo) and
model = ""
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
reverseFlow(nodeFrom, nodeTo)
}
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
@@ -2227,13 +2011,12 @@ private module Cached {
opTo.getDef() = iFrom
}
private predicate modelFlow(Node nodeFrom, Node nodeTo, string model) {
private predicate modelFlow(Node nodeFrom, Node nodeTo) {
exists(
CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut
|
call.getStaticCallTarget() = func and
func.hasDataFlow(modelIn, modelOut) and
model = "DataFlowFunction"
func.hasDataFlow(modelIn, modelOut)
|
nodeFrom = callInput(call, modelIn) and
nodeTo = callOutput(call, modelOut)
@@ -2454,8 +2237,6 @@ private Field getAFieldWithSize(Union u, int bytes) {
cached
private newtype TContent =
TFieldContent(Field f, int indirectionIndex) {
// the indirection index for field content starts at 1 (because `TFieldContent` is thought of as
// the address of the field, `FieldAddress` in the IR).
indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(f.getUnspecifiedType())] and
// Reads and writes of union fields are tracked using `UnionContent`.
not f.getDeclaringType() instanceof Union
@@ -2465,8 +2246,7 @@ private newtype TContent =
f = u.getAField() and
bytes = getFieldSize(f) and
// We key `UnionContent` by the union instead of its fields since a write to one
// field can be read by any read of the union's fields. Again, the indirection index
// is 1-based (because 0 is considered the address).
// field can be read by any read of the union's fields.
indirectionIndex =
[1 .. max(Ssa::getMaxIndirectionsForType(getAFieldWithSize(u, bytes).getUnspecifiedType()))]
)
@@ -2500,14 +2280,16 @@ class Content extends TContent {
abstract predicate impliesClearOf(Content c);
}
/**
* Gets a string consisting of `n` star characters ("*"), where n >= 0. This is
* used to represent indirection.
*/
bindingset[n]
string repeatStars(int n) { result = concat(int i | i in [1 .. n] | "*") }
private module ContentStars {
private int maxNumberOfIndirections() { result = max(any(Content c).getIndirectionIndex()) }
private string repeatStars(int n) {
n = 0 and result = ""
or
n = [1 .. maxNumberOfIndirections()] and
result = "*" + repeatStars(n - 1)
}
/**
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
* output for `c`.
@@ -2587,12 +2369,6 @@ class UnionContent extends Content, TUnionContent {
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
*/
class ContentSet instanceof Content {
/**
* Holds if this content set is the singleton `{c}`. At present, this is
* the only kind of content set supported in C/C++.
*/
predicate isSingleton(Content c) { this = c }
/** Gets a content that may be stored into when storing into this set. */
Content getAStoreContent() { result = this }
@@ -2810,5 +2586,5 @@ class AdditionalCallTarget extends Unit {
/**
* Gets a viable target for `call`.
*/
abstract Declaration viableTarget(Call call);
abstract DataFlowCallable viableTarget(Call call);
}

View File

@@ -3,26 +3,12 @@
* `toString` for `Instruction` and `Operand` dataflow nodes.
*/
private import cpp
private import semmle.code.cpp.ir.IR
private import codeql.util.Unit
private import Node0ToString
private import DataFlowUtil
private import DataFlowPrivate
/**
* Gets the string representation of the unconverted expression `loc` if
* `loc` is an `Expression`.
*
* Otherwise, this gets the string representation of `loc`.
*/
private string unconvertedAstToString(Locatable loc) {
result = loc.(Expr).getUnconverted().toString()
or
not loc instanceof Expr and
result = loc.toString()
}
private class NormalNode0ToString extends Node0ToString {
NormalNode0ToString() {
// Silence warning about `this` not being bound.
@@ -32,10 +18,14 @@ private class NormalNode0ToString extends Node0ToString {
override string instructionToString(Instruction i) {
if i.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
then result = "this"
else result = unconvertedAstToString(i.getAst())
else result = i.getAst().toString()
}
override string operandToString(Operand op) { result = this.instructionToString(op.getDef()) }
override string operandToString(Operand op) {
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
then result = "this"
else result = op.getDef().getAst().toString()
}
override string toExprString(Node n) {
result = n.asExpr(0).toString()

View File

@@ -10,7 +10,7 @@ private import PrintIRUtilities
*/
private string getFromFlow(Node node2, int order1, int order2) {
exists(Node node1 |
simpleLocalFlowStep(node1, node2, _) and
simpleLocalFlowStep(node1, node2) and
result = nodeId(node1, order1, order2)
)
}
@@ -20,7 +20,7 @@ private string getFromFlow(Node node2, int order1, int order2) {
*/
private string getToFlow(Node node1, int order1, int order2) {
exists(Node node2 |
simpleLocalFlowStep(node1, node2, _) and
simpleLocalFlowStep(node1, node2) and
result = nodeId(node2, order1, order2)
)
}

View File

@@ -546,7 +546,7 @@ module ProductFlow {
Flow1::PathGraph::edges(pred1, succ1, _, _) and
exists(ReturnKindExt returnKind |
succ1.getNode() = returnKind.getAnOutNode(call) and
paramReturnNode(_, pred1.asParameterReturnNode(), _, returnKind)
pred1.getNode().(ReturnNodeExt).getKind() = returnKind
)
}
@@ -574,7 +574,7 @@ module ProductFlow {
Flow2::PathGraph::edges(pred2, succ2, _, _) and
exists(ReturnKindExt returnKind |
succ2.getNode() = returnKind.getAnOutNode(call) and
paramReturnNode(_, pred2.asParameterReturnNode(), _, returnKind)
pred2.getNode().(ReturnNodeExt).getKind() = returnKind
)
}

View File

@@ -6,7 +6,6 @@ private import DataFlowImplCommon as DataFlowImplCommon
private import DataFlowUtil
private import semmle.code.cpp.models.interfaces.PointerWrapper
private import DataFlowPrivate
private import TypeFlow
private import semmle.code.cpp.ir.ValueNumbering
/**
@@ -246,6 +245,14 @@ private module IteratorIndirections {
baseType = super.getValueType()
}
override predicate isAdditionalDereference(Instruction deref, Operand address) {
exists(CallInstruction call |
operandForFullyConvertedCall(getAUse(deref), call) and
this = call.getStaticCallTarget().getClassAndName("operator*") and
address = call.getThisArgumentOperand()
)
}
override predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) {
exists(CallInstruction call | call.getArgumentOperand(0) = value.asOperand() |
this = call.getStaticCallTarget().getClassAndName("operator=") and
@@ -254,6 +261,16 @@ private module IteratorIndirections {
)
}
override predicate isAdditionalTaintStep(Node node1, Node node2) {
exists(CallInstruction call |
// Taint through `operator+=` and `operator-=` on iterators.
call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and
node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and
node1.(IndirectOperand).hasOperandAndIndirectionIndex(call.getArgumentOperand(0), _) and
node1.getType().getUnspecifiedType() = this
)
}
override predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
// This is a bit annoying: Consider the following snippet:
// ```
@@ -571,6 +588,230 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns
cached
private module Cached {
private import semmle.code.cpp.models.interfaces.Iterator as Interfaces
private import semmle.code.cpp.models.implementations.Iterator as Iterator
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as IO
/**
* Holds if `next` is a instruction with a memory result that potentially
* updates the memory produced by `prev`.
*/
private predicate memorySucc(Instruction prev, Instruction next) {
prev = next.(ChiInstruction).getTotal()
or
// Phi inputs can be inexact.
prev = next.(PhiInstruction).getAnInputOperand().getAnyDef()
or
prev = next.(CopyInstruction).getSourceValue()
or
exists(ReadSideEffectInstruction read |
next = read.getPrimaryInstruction() and
isAdditionalConversionFlow(_, next) and
prev = read.getSideEffectOperand().getAnyDef()
)
}
/**
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
* that is used for a write operation that writes the value `value`. The `memory` instruction
* represents the memory that the IR's SSA analysis determined was read by the call to `operator*`.
*
* The `numberOfLoads` integer represents the number of dereferences this write corresponds to
* on the underlying container that produced the iterator.
*/
private predicate isChiAfterIteratorDef(
Instruction memory, Operand iteratorDerefAddress, Node0Impl value, int numberOfLoads
) {
exists(
BaseSourceVariableInstruction iteratorBase, ReadSideEffectInstruction read,
Operand iteratorAddress
|
numberOfLoads >= 0 and
isDef(_, value, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
iteratorBase.getResultType() instanceof Interfaces::Iterator and
isDereference(iteratorAddress.getDef(), read.getArgumentDef().getAUse(), _) and
memory = read.getSideEffectOperand().getAnyDef()
)
}
private predicate isSource(Instruction instr, Operand iteratorAddress, int numberOfLoads) {
getAUse(instr) = iteratorAddress and
exists(BaseSourceVariableInstruction iteratorBase |
iteratorBase.getResultType() instanceof Interfaces::Iterator and
not iteratorBase.getResultType() instanceof Cpp::PointerType and
isUse(_, iteratorAddress, iteratorBase, numberOfLoads - 1, 0)
)
}
private predicate isSink(Instruction instr, CallInstruction call) {
getAUse(instr).(ArgumentOperand).getCall() = call and
// Only include operations that may modify the object that the iterator points to.
// The following is a non-exhaustive list of things that may modify the value of the
// iterator, but never the value of what the iterator points to.
// The more things we can exclude here, the faster the small dataflow-like analysis
// done by `convertsIntoArgument` will converge.
not exists(Function f | f = call.getStaticCallTarget() |
f instanceof Iterator::IteratorCrementOperator or
f instanceof Iterator::IteratorBinaryArithmeticOperator or
f instanceof Iterator::IteratorAssignArithmeticOperator or
f instanceof Iterator::IteratorCrementMemberOperator or
f instanceof Iterator::IteratorBinaryArithmeticMemberOperator or
f instanceof Iterator::IteratorAssignArithmeticMemberOperator or
f instanceof Iterator::IteratorAssignmentMemberOperator
)
}
private predicate convertsIntoArgumentFwd(Instruction instr) {
isSource(instr, _, _)
or
exists(Instruction prev | convertsIntoArgumentFwd(prev) |
conversionFlow(unique( | | getAUse(prev)), instr, false, _)
)
}
private predicate convertsIntoArgumentRev(Instruction instr) {
convertsIntoArgumentFwd(instr) and
(
isSink(instr, _)
or
exists(Instruction next | convertsIntoArgumentRev(next) |
conversionFlow(unique( | | getAUse(instr)), next, false, _)
)
)
}
private predicate convertsIntoArgument(
Operand iteratorAddress, CallInstruction call, int numberOfLoads
) {
exists(Instruction iteratorAddressDef |
isSource(iteratorAddressDef, iteratorAddress, numberOfLoads) and
isSink(iteratorAddressDef, call) and
convertsIntoArgumentRev(pragma[only_bind_into](iteratorAddressDef))
)
}
private predicate isChiAfterIteratorArgument(
Instruction memory, Operand iteratorAddress, int numberOfLoads
) {
// Ideally, `iteratorAddress` would be an `ArgumentOperand`, but there might be
// various conversions applied to it before it becomes an argument.
// So we do a small amount of flow to find the call that the iterator is passed to.
exists(CallInstruction call | convertsIntoArgument(iteratorAddress, call, numberOfLoads) |
exists(ReadSideEffectInstruction read |
read.getPrimaryInstruction() = call and
read.getSideEffectOperand().getAnyDef() = memory
)
or
exists(LoadInstruction load |
iteratorAddress.getDef() = load and
memory = load.getSourceValueOperand().getAnyDef()
)
)
}
/**
* Holds if `iterator` is a `StoreInstruction` that stores the result of some function
* returning an iterator into an address computed started at `containerBase`.
*
* For example, given a declaration like `std::vector<int>::iterator it = v.begin()`,
* the `iterator` will be the `StoreInstruction` generated by the write to `it`, and
* `containerBase` will be the address of `v`.
*/
private predicate isChiAfterBegin(
BaseSourceVariableInstruction containerBase, StoreInstruction iterator
) {
exists(
CallInstruction getIterator, Iterator::GetIteratorFunction getIteratorFunction,
IO::FunctionInput input, int i
|
getIterator = iterator.getSourceValue() and
getIteratorFunction = getIterator.getStaticCallTarget() and
getIteratorFunction.getsIterator(input, _) and
isDef(_, any(Node0Impl n | n.asInstruction() = iterator), _, _, 1, 0) and
input.isParameterDerefOrQualifierObject(i) and
isUse(_, getIterator.getArgumentOperand(i), containerBase, 0, 0)
)
}
/**
* Holds if `iteratorAddress` is an address of an iterator that is used for
* a read operation. The `memory` instruction represents the memory that
* the IR's SSA analysis determined was read by the call to `operator*`.
*
* Finally, the `numberOfLoads` integer represents the number of dereferences
* this read corresponds to on the underlying container that produced the iterator.
*/
private predicate isChiBeforeIteratorUse(
Operand iteratorAddress, Instruction memory, int numberOfLoads
) {
exists(
BaseSourceVariableInstruction iteratorBase, LoadInstruction load,
ReadSideEffectInstruction read, Operand iteratorDerefAddress
|
numberOfLoads >= 0 and
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
isUse(_, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
iteratorBase.getResultType() instanceof Interfaces::Iterator and
load.getSourceAddressOperand() = iteratorDerefAddress and
read.getPrimaryInstruction() = load.getSourceAddress() and
memory = read.getSideEffectOperand().getAnyDef()
)
}
/**
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
* that is used for a write operation that writes the value `value` to a container that
* created the iterator. `container` represents the base of the address of the container
* that was used to create the iterator.
*/
cached
predicate isIteratorDef(
BaseSourceVariableInstruction container, Operand iteratorDerefAddress, Node0Impl value,
int numberOfLoads, int indirectionIndex
) {
exists(Instruction memory, Instruction begin, int upper, int ind |
isChiAfterIteratorDef(memory, iteratorDerefAddress, value, numberOfLoads) and
memorySucc*(begin, memory) and
isChiAfterBegin(container, begin) and
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
ind = numberOfLoads + [1 .. upper] and
indirectionIndex = ind - (numberOfLoads + 1)
)
}
/**
* Holds if `iteratorAddress` is an address of an iterator that is used for a
* read operation to read a value from a container that created the iterator.
* `container` represents the base of the address of the container that was used
* to create the iterator.
*/
cached
predicate isIteratorUse(
BaseSourceVariableInstruction container, Operand iteratorAddress, int numberOfLoads,
int indirectionIndex
) {
// Direct use
exists(Instruction begin, Instruction memory, int upper, int ind |
isChiBeforeIteratorUse(iteratorAddress, memory, numberOfLoads) and
memorySucc*(begin, memory) and
isChiAfterBegin(container, begin) and
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
ind = numberOfLoads + [1 .. upper] and
indirectionIndex = ind - (numberOfLoads + 1)
)
or
// Use through function output
exists(Instruction memory, Instruction begin, int upper, int ind |
isChiAfterIteratorArgument(memory, iteratorAddress, numberOfLoads) and
memorySucc*(begin, memory) and
isChiAfterBegin(container, begin) and
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
ind = numberOfLoads + [1 .. upper] and
indirectionIndex = ind - (numberOfLoads - 1)
)
}
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */
private predicate isConversion(Operand op) {
exists(Instruction def, Operand use |
@@ -714,7 +955,11 @@ private module Cached {
* Holds if the address computed by `operand` is guaranteed to write
* to a specific address.
*/
private predicate isCertainAddress(Operand operand) { isPointerToSingleObject(operand.getDef()) }
private predicate isCertainAddress(Operand operand) {
valueNumberOfOperand(operand).getAnInstruction() instanceof VariableAddressInstruction
or
operand.getType() instanceof Cpp::ReferenceType
}
/**
* Holds if `address` is a use of an SSA variable rooted at `base`, and the

View File

@@ -6,26 +6,16 @@ private import semmle.code.cpp.models.interfaces.SideEffect
private import DataFlowUtil
private import DataFlowPrivate
private import SsaInternals as Ssa
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.cpp.ir.dataflow.FlowSteps
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step. This relation is only used for local taint flow
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
* special cases that should only apply to local taint flow.
* (intra-procedural) step.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// dataflow step
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
// taint flow step
localAdditionalTaintStep(nodeFrom, nodeTo, _)
or
// models-as-data summarized flow for local data flow (i.e. special case for flow
// through calls to modeled functions, without relying on global dataflow to join
// the dots).
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
localAdditionalTaintStep(nodeFrom, nodeTo)
}
/**
@@ -34,11 +24,10 @@ predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
* different objects.
*/
cached
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
model = ""
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction())
or
modeledTaintStep(nodeFrom, nodeTo, model)
modeledTaintStep(nodeFrom, nodeTo)
or
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
// indirection of the pointer arithmetic instruction. This provides flow from `source`
@@ -46,22 +35,15 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
exists(PointerArithmeticInstruction pai, int indirectionIndex |
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
) and
model = ""
)
or
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
model = ""
or
// models-as-data summarized flow
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo)
or
// object->field conflation for content that is a `TaintInheritingContent`.
exists(DataFlow::ContentSet f |
readStep(nodeFrom, f, nodeTo) and
f.getAReadContent() instanceof TaintInheritingContent
) and
model = ""
)
}
/**
@@ -138,8 +120,8 @@ predicate localExprTaint(Expr e1, Expr e2) {
* Holds if the additional step from `src` to `sink` should be included in all
* global taint flow configurations.
*/
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink, string model) {
localAdditionalTaintStep(src, sink, model)
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintStep(src, sink)
}
/**
@@ -159,7 +141,7 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
* Holds if taint can flow from `nodeIn` to `nodeOut` through a call to a
* modeled function.
*/
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string model) {
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
// Normal taint steps
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
call.getStaticCallTarget() = func and
@@ -168,8 +150,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
nodeIn = callInput(call, modelIn) and nodeOut = callOutput(call, modelOut)
or
exists(int d | nodeIn = callInput(call, modelIn, d) and nodeOut = callOutput(call, modelOut, d))
) and
model = "TaintFunction"
)
or
// Taint flow from one argument to another and data flow from an argument to a
// return value. This happens in functions like `strcat` and `memcpy`. We
@@ -186,8 +167,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and
modelMidOut.isParameterDeref(indexMid) and
modelMidIn.isParameter(indexMid) and
model = "TaintFunction"
modelMidIn.isParameter(indexMid)
)
or
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
@@ -200,11 +180,9 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
indirectArgument.hasAddressOperandAndIndirectionIndex(nodeIn.asOperand(), _) and
call.getStaticCallTarget() = func and
(
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) and
model = "DataFlowFunction"
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
or
func.(TaintFunction).hasTaintFlow(modelIn, modelOut) and
model = "TaintFunction"
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
) and
nodeOut = callOutput(call, modelOut)
)

View File

@@ -1,278 +0,0 @@
private import cpp
private import semmle.code.cpp.ir.IR
private import codeql.typeflow.TypeFlow
private module Input implements TypeFlowInput<Location> {
/** Holds if `alloc` dynamically allocates a single object. */
private predicate isSingleObjectAllocation(AllocationExpr alloc) {
// i.e., `new int`;
alloc instanceof NewExpr
or
// i.e., `malloc(sizeof(int))`
exists(SizeofTypeOperator sizeOf | sizeOf = alloc.getSizeExpr() |
not sizeOf.getTypeOperand().getUnspecifiedType() instanceof ArrayType
)
}
/**
* Holds if `i` is the result of a dynamic allocation.
*
* `isObject` is `true` if the allocation allocated a single object,
* and `false` otherwise.
*/
private predicate isAllocation(Instruction i, boolean isObject) {
exists(AllocationExpr alloc | alloc = i.getUnconvertedResultExpression() |
if isSingleObjectAllocation(alloc) then isObject = true else isObject = false
)
}
private predicate hasExactSingleType(Instruction i) {
// The address of a variable is always a single object
i instanceof VariableAddressInstruction
or
// A reference always points to a single object
i.getResultLanguageType().hasUnspecifiedType(any(ReferenceType rt), false)
or
// `this` is never an array
i instanceof InitializeThisInstruction
or
// An allocation of a non-array object
isAllocation(i, true)
}
private predicate hasExactBufferType(Instruction i) {
// Anything with an array type is a buffer
i.getResultLanguageType().hasUnspecifiedType(any(ArrayType at), false)
or
// An allocation expression that we couldn't conclude allocated a single
// expression is assigned a buffer type.
isAllocation(i, false)
}
private newtype TTypeFlowNode =
TInstructionNode(Instruction i) or
TFunctionNode(IRFunction func)
abstract class TypeFlowNode extends TTypeFlowNode {
/** Gets a textual representation of this node. */
abstract string toString();
/**
* Gets the type of this node. This type may not be the most precise
* possible type, but will be used as a starting point of the analysis.
*/
abstract Type getType();
/** Gets the location of this node. */
abstract Location getLocation();
/** Gets the underlying `Instruction` of this node, if any. */
Instruction asInstruction() { none() }
/** Gets the underlying `IRFunction` of this node, if any. */
IRFunction asFunction() { none() }
/** Holds if the value of this node is always null. */
abstract predicate isNullValue();
}
private class InstructionNode extends TypeFlowNode, TInstructionNode {
Instruction instr;
InstructionNode() { this = TInstructionNode(instr) }
override string toString() { result = instr.toString() }
override Type getType() {
if hasExactSingleType(instr) then result.isSingle() else result.isBuffer()
}
override Location getLocation() { result = instr.getLocation() }
override Instruction asInstruction() { result = instr }
override predicate isNullValue() {
instr.(ConstantInstruction).getValue() = "0" and
instr.getResultIRType() instanceof IRAddressType
}
}
/** Gets the `TypeFlowNode` corresponding to `i`. */
additional InstructionNode instructionNode(Instruction i) { result.asInstruction() = i }
private class FunctionNode extends TypeFlowNode, TFunctionNode {
IRFunction func;
FunctionNode() { this = TFunctionNode(func) }
override string toString() { result = func.toString() }
Instruction getReturnValueInstruction() {
result = func.getReturnInstruction().(ReturnValueInstruction).getReturnValue()
}
override Type getType() { result = instructionNode(this.getReturnValueInstruction()).getType() }
override Location getLocation() { result = func.getLocation() }
override IRFunction asFunction() { result = func }
override predicate isNullValue() {
instructionNode(this.getReturnValueInstruction()).isNullValue()
}
}
/**
* Gets an ultimiate definition of `phi`. That is, an input to `phi` that is
* not itself a `PhiInstruction`.
*/
private Instruction getAnUltimateLocalDefinition(PhiInstruction phi) {
result = phi.getAnInput*() and not result instanceof PhiInstruction
}
/**
* Holds if this function is private (i.e., cannot be accessed outside its
* compilation unit). This means we can use a closed-world assumption about
* calls to this function.
*/
private predicate isPrivate(Function func) {
// static functions have internal linkage
func.isStatic()
or
// anonymous namespaces have internal linkage
func.getNamespace().getParentNamespace*().isAnonymous()
or
// private member functions are only called internally from inside the class
func.(MemberFunction).isPrivate()
}
/**
* Holds if `arg` is an argument for the parameter `p` in a private callable.
*/
pragma[nomagic]
private predicate privateParamArg(InitializeParameterInstruction p, Instruction arg) {
exists(CallInstruction call, int i, Function func |
call.getArgument(pragma[only_bind_into](i)) = arg and
func = call.getStaticCallTarget() and
func.getParameter(pragma[only_bind_into](i)) = p.getParameter() and
isPrivate(func)
)
}
predicate joinStep(TypeFlowNode n1, TypeFlowNode n2) {
// instruction -> phi
getAnUltimateLocalDefinition(n2.asInstruction()) = n1.asInstruction()
or
// return value -> function
n2.(FunctionNode).getReturnValueInstruction() = n1.asInstruction()
or
// function -> call
exists(Function func | func = n1.asFunction().getFunction() |
not func.isVirtual() and
n2.asInstruction().(CallInstruction).getStaticCallTarget() = func
)
or
// Argument -> parameter where the parameter's enclosing function
// is "private".
exists(Instruction arg, Instruction p |
privateParamArg(p, arg) and
n1.asInstruction() = arg and
n2.asInstruction() = p
)
}
/**
* Holds if knowing whether `i1` points to a single object or buffer implies
* knowing whether `i2` points to a single object or buffer.
*/
private predicate instructionStep(Instruction i1, Instruction i2) {
i2.(CopyInstruction).getSourceValue() = i1
or
i2.(CopyValueInstruction).getSourceValue() = i1
or
i2.(ConvertInstruction).getUnary() = i1
or
i2.(CheckedConvertOrNullInstruction).getUnary() = i1
or
i2.(InheritanceConversionInstruction).getUnary() = i1
or
i2.(PointerArithmeticInstruction).getLeft() = i1
}
predicate step(TypeFlowNode n1, TypeFlowNode n2) {
instructionStep(n1.asInstruction(), n2.asInstruction())
}
predicate isNullValue(TypeFlowNode n) { n.isNullValue() }
private newtype TType =
TSingle() or
TBuffer()
class Type extends TType {
string toString() {
this.isSingle() and
result = "Single"
or
this.isBuffer() and
result = "Buffer"
}
/** Holds if this type is the type that represents a single object. */
predicate isSingle() { this = TSingle() }
/** Holds if this type is the type that represents a buffer. */
predicate isBuffer() { this = TBuffer() }
/**
* Gets a super type of this type, if any.
*
* The type relation is `Single <: Buffer`.
*/
Type getASupertype() {
this.isSingle() and
result.isBuffer()
}
}
predicate exactTypeBase(TypeFlowNode n, Type t) {
exists(Instruction instr | instr = n.asInstruction() |
hasExactSingleType(instr) and t.isSingle()
or
hasExactBufferType(instr) and t.isBuffer()
)
}
pragma[nomagic]
private predicate upcastCand(TypeFlowNode n, Type t1, Type t2) {
exists(TypeFlowNode next |
step(n, next)
or
joinStep(n, next)
|
n.getType() = t1 and
next.getType() = t2 and
t1 != t2
)
}
private predicate upcast(TypeFlowNode n, Type t1) {
exists(Type t2 | upcastCand(n, t1, t2) |
// No need for transitive closure since the subtyping relation is just `Single <: Buffer`
t1.getASupertype() = t2
)
}
predicate typeFlowBaseCand(TypeFlowNode n, Type t) { upcast(n, t) }
}
private module TypeFlow = Make<Location, Input>;
/**
* Holds if `i` is an instruction that computes an address that points to a
* single object (as opposed to pointing into a buffer).
*/
pragma[nomagic]
predicate isPointerToSingleObject(Instruction i) {
TypeFlow::bestTypeFlow(Input::instructionNode(i), any(Input::Type t | t.isSingle()), _)
}

View File

@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2, _)
defaultAdditionalTaintStep(node1, node2)
}
/**

View File

@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2, _)
defaultAdditionalTaintStep(node1, node2)
}
/**

View File

@@ -127,7 +127,7 @@ abstract deprecated class Configuration extends DataFlow::Configuration {
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
this.isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2, _)
defaultAdditionalTaintStep(node1, node2)
}
/**

View File

@@ -41,5 +41,5 @@ class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration {
* Holds if the escape analysis done by SSA construction should be sound. By default, the SSA is
* built assuming that no variable's address ever escapes.
*/
predicate useSoundEscapeAnalysis() { any() }
predicate useSoundEscapeAnalysis() { none() }
}

View File

@@ -17,11 +17,18 @@ private import Imports::IRType
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
* by the AST-to-IR translation (`IRTempVariable`).
*/
abstract private class AbstractIRVariable extends TIRVariable {
class IRVariable extends TIRVariable {
Language::Declaration func;
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
/** Gets a textual representation of this element. */
abstract string toString();
string toString() { none() }
/**
* Holds if this variable's value cannot be changed within a function. Currently used for string
@@ -42,13 +49,13 @@ abstract private class AbstractIRVariable extends TIRVariable {
/**
* Gets the type of the variable.
*/
abstract Language::LanguageType getLanguageType();
Language::LanguageType getLanguageType() { none() }
/**
* Gets the AST node that declared this variable, or that introduced this
* variable as part of the AST-to-IR translation.
*/
abstract Language::AST getAst();
Language::AST getAst() { none() }
/** DEPRECATED: Alias for getAst */
deprecated Language::AST getAST() { result = this.getAst() }
@@ -57,7 +64,7 @@ abstract private class AbstractIRVariable extends TIRVariable {
* Gets an identifier string for the variable. This identifier is unique
* within the function.
*/
abstract string getUniqueId();
string getUniqueId() { none() }
/**
* Gets the source location of this variable.
@@ -67,7 +74,7 @@ abstract private class AbstractIRVariable extends TIRVariable {
/**
* Gets the IR for the function that references this variable.
*/
final IRFunction getEnclosingIRFunction() { result.getFunction() = this.getEnclosingFunction() }
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
/**
* Gets the function that references this variable.
@@ -75,18 +82,10 @@ abstract private class AbstractIRVariable extends TIRVariable {
final Language::Declaration getEnclosingFunction() { result = func }
}
/**
* A variable referenced by the IR for a function.
*
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
* by the AST-to-IR translation (`IRTempVariable`).
*/
final class IRVariable = AbstractIRVariable;
/**
* A user-declared variable referenced by the IR for a function.
*/
class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
class IRUserVariable extends IRVariable, TIRUserVariable {
Language::Variable var;
Language::LanguageType type;
@@ -115,29 +114,26 @@ class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
* parameters, non-static local variables, and temporary variables.
*/
abstract private class AbstractIRAutomaticVariable extends AbstractIRVariable { }
/**
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
* parameters, non-static local variables, and temporary variables.
*/
final class IRAutomaticVariable = AbstractIRAutomaticVariable;
/**
* A user-declared variable that is allocated on the stack. This includes all parameters and
* non-static local variables.
*/
private class AbstractIRAutomaticUserVariable extends IRUserVariable, AbstractIRAutomaticVariable {
override Language::AutomaticVariable var;
final override Language::AutomaticVariable getVariable() { result = var }
class IRAutomaticVariable extends IRVariable {
IRAutomaticVariable() {
exists(Language::Variable var |
this = TIRUserVariable(var, _, func) and
Language::isVariableAutomatic(var)
)
or
this = TIRTempVariable(func, _, _, _)
}
}
/**
* A user-declared variable that is allocated on the stack. This includes all parameters and
* non-static local variables.
*/
final class IRAutomaticUserVariable = AbstractIRAutomaticUserVariable;
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
override Language::AutomaticVariable var;
final override Language::AutomaticVariable getVariable() { result = var }
}
/**
* A user-declared variable that is not allocated on the stack. This includes all global variables,
@@ -155,10 +151,16 @@ class IRStaticUserVariable extends IRUserVariable {
* A variable that is not user-declared. This includes temporary variables generated as part of IR
* construction, as well as string literals.
*/
abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
class IRGeneratedVariable extends IRVariable {
Language::AST ast;
Language::LanguageType type;
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
final override Language::AST getAst() { result = ast }
@@ -194,20 +196,12 @@ abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
string getBaseString() { none() }
}
/**
* A variable that is not user-declared. This includes temporary variables generated as part of IR
* construction, as well as string literals.
*/
final class IRGeneratedVariable = AbstractIRGeneratedVariable;
/**
* A temporary variable introduced by IR construction. The most common examples are the variable
* generated to hold the return value of a function, or the variable generated to hold the result of
* a condition operator (`a ? b : c`).
*/
class IRTempVariable extends AbstractIRGeneratedVariable, AbstractIRAutomaticVariable,
TIRTempVariable
{
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
TempVariableTag tag;
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
@@ -247,7 +241,7 @@ class IRThrowVariable extends IRTempVariable {
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
class IREllipsisVariable extends IRTempVariable, IRParameter {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
@@ -258,7 +252,7 @@ class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
/**
* A temporary variable generated to hold the `this` pointer.
*/
class IRThisVariable extends IRTempVariable, AbstractIRParameter {
class IRThisVariable extends IRTempVariable, IRParameter {
IRThisVariable() { tag = ThisTempVar() }
final override string toString() { result = "#this" }
@@ -270,7 +264,7 @@ class IRThisVariable extends IRTempVariable, AbstractIRParameter {
* A variable generated to represent the contents of a string literal. This variable acts much like
* a read-only global variable.
*/
class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
Language::StringLiteral literal;
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
@@ -294,7 +288,7 @@ class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynamicInitializationFlag {
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
@@ -320,24 +314,24 @@ class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynami
* An IR variable which acts like a function parameter, including positional parameters and the
* temporary variables generated for `this` and ellipsis parameters.
*/
abstract private class AbstractIRParameter extends AbstractIRAutomaticVariable {
class IRParameter extends IRAutomaticVariable {
IRParameter() {
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
or
this = TIRTempVariable(_, _, ThisTempVar(), _)
or
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
}
/**
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
*/
int getIndex() { none() }
}
/**
* An IR variable which acts like a function parameter, including positional parameters and the
* temporary variables generated for `this` and ellipsis parameters.
*/
final class IRParameter = AbstractIRParameter;
/**
* An IR variable representing a positional parameter.
*/
class IRPositionalParameter extends AbstractIRParameter, AbstractIRAutomaticUserVariable {
IRPositionalParameter() { this.getVariable() instanceof Language::Parameter }
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
}

View File

@@ -247,7 +247,8 @@ class Instruction extends Construction::TStageInstruction {
* Gets the type of the result produced by this instruction. If the instruction does not produce
* a result, its result type will be `IRVoidType`.
*/
final IRType getResultIRType() { result = Construction::getInstructionResultIRType(this) }
cached
final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() }
/**
* Gets the type of the result produced by this instruction. If the
@@ -994,8 +995,9 @@ class ConstantInstruction extends ConstantValueInstruction {
*/
class IntegerConstantInstruction extends ConstantInstruction {
IntegerConstantInstruction() {
exists(IRType resultType | resultType = this.getResultIRType() |
resultType instanceof IRIntegerType or resultType instanceof IRBooleanType
exists(IRType resultType |
resultType = this.getResultIRType() and
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
)
}
}
@@ -1007,17 +1009,6 @@ class FloatConstantInstruction extends ConstantInstruction {
FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType }
}
/**
* An instruction whose result is a constant value of a pointer type.
*/
class PointerConstantInstruction extends ConstantInstruction {
PointerConstantInstruction() {
exists(IRType resultType | resultType = this.getResultIRType() |
resultType instanceof IRAddressType or resultType instanceof IRFunctionAddressType
)
}
}
/**
* An instruction whose result is the address of a string literal.
*/

View File

@@ -429,11 +429,6 @@ private module Cached {
instr = unreachedInstruction(_) and result = Language::getVoidType()
}
cached
IRType getInstructionResultIRType(Instruction instr) {
result = instr.getResultLanguageType().getIRType()
}
/**
* Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
*

View File

@@ -17,11 +17,18 @@ private import Imports::IRType
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
* by the AST-to-IR translation (`IRTempVariable`).
*/
abstract private class AbstractIRVariable extends TIRVariable {
class IRVariable extends TIRVariable {
Language::Declaration func;
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
/** Gets a textual representation of this element. */
abstract string toString();
string toString() { none() }
/**
* Holds if this variable's value cannot be changed within a function. Currently used for string
@@ -42,13 +49,13 @@ abstract private class AbstractIRVariable extends TIRVariable {
/**
* Gets the type of the variable.
*/
abstract Language::LanguageType getLanguageType();
Language::LanguageType getLanguageType() { none() }
/**
* Gets the AST node that declared this variable, or that introduced this
* variable as part of the AST-to-IR translation.
*/
abstract Language::AST getAst();
Language::AST getAst() { none() }
/** DEPRECATED: Alias for getAst */
deprecated Language::AST getAST() { result = this.getAst() }
@@ -57,7 +64,7 @@ abstract private class AbstractIRVariable extends TIRVariable {
* Gets an identifier string for the variable. This identifier is unique
* within the function.
*/
abstract string getUniqueId();
string getUniqueId() { none() }
/**
* Gets the source location of this variable.
@@ -67,7 +74,7 @@ abstract private class AbstractIRVariable extends TIRVariable {
/**
* Gets the IR for the function that references this variable.
*/
final IRFunction getEnclosingIRFunction() { result.getFunction() = this.getEnclosingFunction() }
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
/**
* Gets the function that references this variable.
@@ -75,18 +82,10 @@ abstract private class AbstractIRVariable extends TIRVariable {
final Language::Declaration getEnclosingFunction() { result = func }
}
/**
* A variable referenced by the IR for a function.
*
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
* by the AST-to-IR translation (`IRTempVariable`).
*/
final class IRVariable = AbstractIRVariable;
/**
* A user-declared variable referenced by the IR for a function.
*/
class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
class IRUserVariable extends IRVariable, TIRUserVariable {
Language::Variable var;
Language::LanguageType type;
@@ -115,29 +114,26 @@ class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
* parameters, non-static local variables, and temporary variables.
*/
abstract private class AbstractIRAutomaticVariable extends AbstractIRVariable { }
/**
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
* parameters, non-static local variables, and temporary variables.
*/
final class IRAutomaticVariable = AbstractIRAutomaticVariable;
/**
* A user-declared variable that is allocated on the stack. This includes all parameters and
* non-static local variables.
*/
private class AbstractIRAutomaticUserVariable extends IRUserVariable, AbstractIRAutomaticVariable {
override Language::AutomaticVariable var;
final override Language::AutomaticVariable getVariable() { result = var }
class IRAutomaticVariable extends IRVariable {
IRAutomaticVariable() {
exists(Language::Variable var |
this = TIRUserVariable(var, _, func) and
Language::isVariableAutomatic(var)
)
or
this = TIRTempVariable(func, _, _, _)
}
}
/**
* A user-declared variable that is allocated on the stack. This includes all parameters and
* non-static local variables.
*/
final class IRAutomaticUserVariable = AbstractIRAutomaticUserVariable;
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
override Language::AutomaticVariable var;
final override Language::AutomaticVariable getVariable() { result = var }
}
/**
* A user-declared variable that is not allocated on the stack. This includes all global variables,
@@ -155,10 +151,16 @@ class IRStaticUserVariable extends IRUserVariable {
* A variable that is not user-declared. This includes temporary variables generated as part of IR
* construction, as well as string literals.
*/
abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
class IRGeneratedVariable extends IRVariable {
Language::AST ast;
Language::LanguageType type;
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
final override Language::AST getAst() { result = ast }
@@ -194,20 +196,12 @@ abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
string getBaseString() { none() }
}
/**
* A variable that is not user-declared. This includes temporary variables generated as part of IR
* construction, as well as string literals.
*/
final class IRGeneratedVariable = AbstractIRGeneratedVariable;
/**
* A temporary variable introduced by IR construction. The most common examples are the variable
* generated to hold the return value of a function, or the variable generated to hold the result of
* a condition operator (`a ? b : c`).
*/
class IRTempVariable extends AbstractIRGeneratedVariable, AbstractIRAutomaticVariable,
TIRTempVariable
{
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
TempVariableTag tag;
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
@@ -247,7 +241,7 @@ class IRThrowVariable extends IRTempVariable {
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
class IREllipsisVariable extends IRTempVariable, IRParameter {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
@@ -258,7 +252,7 @@ class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
/**
* A temporary variable generated to hold the `this` pointer.
*/
class IRThisVariable extends IRTempVariable, AbstractIRParameter {
class IRThisVariable extends IRTempVariable, IRParameter {
IRThisVariable() { tag = ThisTempVar() }
final override string toString() { result = "#this" }
@@ -270,7 +264,7 @@ class IRThisVariable extends IRTempVariable, AbstractIRParameter {
* A variable generated to represent the contents of a string literal. This variable acts much like
* a read-only global variable.
*/
class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
Language::StringLiteral literal;
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
@@ -294,7 +288,7 @@ class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynamicInitializationFlag {
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
@@ -320,24 +314,24 @@ class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynami
* An IR variable which acts like a function parameter, including positional parameters and the
* temporary variables generated for `this` and ellipsis parameters.
*/
abstract private class AbstractIRParameter extends AbstractIRAutomaticVariable {
class IRParameter extends IRAutomaticVariable {
IRParameter() {
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
or
this = TIRTempVariable(_, _, ThisTempVar(), _)
or
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
}
/**
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
*/
int getIndex() { none() }
}
/**
* An IR variable which acts like a function parameter, including positional parameters and the
* temporary variables generated for `this` and ellipsis parameters.
*/
final class IRParameter = AbstractIRParameter;
/**
* An IR variable representing a positional parameter.
*/
class IRPositionalParameter extends AbstractIRParameter, AbstractIRAutomaticUserVariable {
IRPositionalParameter() { this.getVariable() instanceof Language::Parameter }
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
}

View File

@@ -247,7 +247,8 @@ class Instruction extends Construction::TStageInstruction {
* Gets the type of the result produced by this instruction. If the instruction does not produce
* a result, its result type will be `IRVoidType`.
*/
final IRType getResultIRType() { result = Construction::getInstructionResultIRType(this) }
cached
final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() }
/**
* Gets the type of the result produced by this instruction. If the
@@ -994,8 +995,9 @@ class ConstantInstruction extends ConstantValueInstruction {
*/
class IntegerConstantInstruction extends ConstantInstruction {
IntegerConstantInstruction() {
exists(IRType resultType | resultType = this.getResultIRType() |
resultType instanceof IRIntegerType or resultType instanceof IRBooleanType
exists(IRType resultType |
resultType = this.getResultIRType() and
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
)
}
}
@@ -1007,17 +1009,6 @@ class FloatConstantInstruction extends ConstantInstruction {
FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType }
}
/**
* An instruction whose result is a constant value of a pointer type.
*/
class PointerConstantInstruction extends ConstantInstruction {
PointerConstantInstruction() {
exists(IRType resultType | resultType = this.getResultIRType() |
resultType instanceof IRAddressType or resultType instanceof IRFunctionAddressType
)
}
}
/**
* An instruction whose result is the address of a string literal.
*/

View File

@@ -377,10 +377,6 @@ CppType getInstructionResultType(TStageInstruction instr) {
result = getVoidType()
}
IRType getInstructionResultIRType(Instruction instr) {
result = instr.getResultLanguageType().getIRType()
}
predicate getInstructionOpcode(Opcode opcode, TStageInstruction instr) {
getInstructionTranslatedElement(instr).hasInstruction(opcode, getInstructionTag(instr), _)
or

View File

@@ -89,8 +89,7 @@ newtype TInstructionTag =
ImplicitDestructorTag(int index) {
exists(Expr e | exists(e.getImplicitDestructorCall(index))) or
exists(Stmt s | exists(s.getImplicitDestructorCall(index)))
} or
CoAwaitBranchTag()
}
class InstructionTag extends TInstructionTag {
final string toString() { result = getInstructionTagId(this) }
@@ -187,8 +186,6 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = BoolConversionCompareTag() and result = "BoolConvComp"
or
tag = ResultCopyTag() and result = "ResultCopy"
or
tag = LoadTag() and result = "Load" // Implicit load due to lvalue-to-rvalue conversion
or
tag = CatchTag() and result = "Catch"
@@ -266,6 +263,4 @@ string getInstructionTagId(TInstructionTag tag) {
exists(int index |
tag = ImplicitDestructorTag(index) and result = "ImplicitDestructor(" + index + ")"
)
or
tag = CoAwaitBranchTag() and result = "CoAwaitBranch"
}

View File

@@ -128,10 +128,8 @@ private predicate ignoreExprAndDescendants(Expr expr) {
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
)
or
// Do not translate implicit destructor calls for unnamed temporary variables that are
// conditionally constructed (until we have a mechanism for calling these only when the
// temporary's constructor was run)
isConditionalTemporaryDestructorCall(expr)
// suppress destructors of temporary variables until proper support is added for them.
exists(Expr parent | parent.getAnImplicitDestructorCall() = expr)
}
/**
@@ -257,42 +255,6 @@ private predicate usedAsCondition(Expr expr) {
)
}
private predicate hasThrowingChild(Expr e) {
e = any(ThrowExpr throw).getFullyConverted()
or
exists(Expr child |
e = getRealParent(child) and
hasThrowingChild(child)
)
}
private predicate isInConditionalEvaluation(Expr e) {
exists(ConditionalExpr cond |
e = cond.getThen().getFullyConverted() and not cond.isTwoOperand()
or
e = cond.getElse().getFullyConverted()
or
// If one of the operands throws then the temporaries constructed in either
// branch will also be attached to the ternary expression. We suppress
// those destructor calls as well.
hasThrowingChild([cond.getThen(), cond.getElse()]) and
e = cond.getFullyConverted()
)
or
e = any(LogicalAndExpr lae).getRightOperand().getFullyConverted()
or
e = any(LogicalOrExpr loe).getRightOperand().getFullyConverted()
or
isInConditionalEvaluation(getRealParent(e))
}
private predicate isConditionalTemporaryDestructorCall(DestructorCall dc) {
exists(TemporaryObjectExpr temp |
temp = dc.getQualifier().(ReuseExpr).getReusedExpr() and
isInConditionalEvaluation(temp)
)
}
/**
* Holds if `conv` is an `InheritanceConversion` that requires a `TranslatedLoad`, despite not being
* marked as having an lvalue-to-rvalue conversion.
@@ -544,7 +506,8 @@ private module IRDeclarationEntries {
* An entity that represents a declaration entry in the database.
*
* This class exists to work around the fact that `DeclStmt`s in some cases
* do not have `DeclarationEntry`s in older databases.
* do not have `DeclarationEntry`s. Currently, this is the case for:
* - `DeclStmt`s in template instantiations.
*
* So instead, the IR works with `IRDeclarationEntry`s that synthesize missing
* `DeclarationEntry`s when there is no result for `DeclStmt::getDeclarationEntry`.
@@ -820,22 +783,6 @@ newtype TTranslatedElement =
not ignoreSideEffects(expr) and
opcode = getCallSideEffectOpcode(expr)
} or
// The set of destructors to invoke after a `throw`. These need to be special
// cased because the edge kind following a throw is an `ExceptionEdge`, and
// we need to make sure that the edge kind is still an `ExceptionEdge` after
// all the destructors have run.
TTranslatedDestructorsAfterThrow(ThrowExpr throw) {
exists(DestructorCall dc |
dc = throw.getAnImplicitDestructorCall() and
not ignoreExpr(dc)
)
} or
// The set of destructors to invoke after a handler for a `try` statement. These
// need to be special cased because the destructors need to run following an
// `ExceptionEdge`, but not following a `GotoEdge` edge.
TTranslatedDestructorsAfterHandler(Handler handler) {
exists(handler.getAnImplicitDestructorCall())
} or
// A precise side effect of an argument to a `Call`
TTranslatedArgumentExprSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) {
not ignoreExpr(expr) and

View File

@@ -90,26 +90,10 @@ abstract class TranslatedExpr extends TranslatedElement {
final override TranslatedElement getChild(int id) {
result = this.getChildInternal(id)
or
exists(int destructorIndex |
result = this.getImplicitDestructorCall(destructorIndex) and
id = this.getFirstDestructorCallIndex() + destructorIndex
)
}
final private TranslatedExpr getImplicitDestructorCall(int index) {
result.getExpr() = expr.getImplicitDestructorCall(index)
}
final override predicate hasAnImplicitDestructorCall() {
exists(this.getImplicitDestructorCall(_))
}
final override int getFirstDestructorCallIndex() {
not this.handlesDestructorsExplicitly() and
(
result = max(int childId | exists(this.getChildInternal(childId))) + 1
or
not exists(this.getChildInternal(_)) and result = 0
exists(int maxChildId, int destructorIndex |
maxChildId = max(int childId | exists(this.getChildInternal(childId))) and
result.(TranslatedExpr).getExpr() = expr.getImplicitDestructorCall(destructorIndex) and
id = maxChildId + 1 + destructorIndex
)
}
@@ -411,14 +395,6 @@ class TranslatedLoad extends TranslatedValueCategoryAdjustment, TTranslatedLoad
result = this.getOperand().getResult()
)
}
override predicate handlesDestructorsExplicitly() {
// The class that generates IR for `e` will (implicitly or explicitly)
// handle the generation of destructor calls for `e`. Without disabling
// destructor call generation here the destructor will get multiple
// parents.
any()
}
}
/**
@@ -538,11 +514,6 @@ class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
final override predicate producesExprResult() { any() }
private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
override predicate handlesDestructorsExplicitly() {
// The destructor calls will already have been generated by the translation of `expr`.
any()
}
}
class TranslatedCommaExpr extends TranslatedNonConstantExpr {
@@ -1311,146 +1282,6 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
}
}
/**
* IR translation of a `co_await` or `co_yield` expression.
*
* The translation of `x = co_await ...` is essentially:
* ```cpp
* if (!awaiter.await_ready()) {
* awaiter.await_suspend();
* }
* x = awaiter.await_resume();
* ```
* where `awaiter` is an object constructed from programmer-supplied
* input, and for IR construction purposes these are resolved by the C/C++
* front-end.
*
* See https://en.cppreference.com/w/cpp/language/coroutines#co_await for the
* specification on how `awaiter` is obtained.
*/
abstract private class TranslatedCoExpr extends TranslatedNonConstantExpr {
/** Gets the operand of this operation. */
abstract Expr getOperand();
/**
* Gets the expression that decides if the enclosing coroutine should be
* suspended.
*/
abstract Expr getAwaitReady();
/**
* Gets the expression that is evaluated when the enclosing coroutine is
* suspended.
*/
abstract Expr getAwaitSuspend();
/**
* Gets the expression that represents the resume point if the enclosing
* coroutine was suspended.
*/
abstract Expr getAwaitResume();
final override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getTranslatedOperand().getFirstInstruction(kind)
}
override Instruction getALastInstructionInternal() {
result = this.getTranslatedAwaitResume().getALastInstruction()
}
final override TranslatedElement getChildInternal(int id) {
id = 0 and result = this.getTranslatedOperand()
or
id = 1 and result = this.getTranslatedAwaitReady()
or
id = 2 and result = this.getTranslatedAwaitResume()
or
id = 3 and result = this.getTranslatedAwaitSuspend()
}
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
tag = CoAwaitBranchTag() and
(
kind instanceof TrueEdge and
result = this.getTranslatedAwaitResume().getFirstInstruction(any(GotoEdge goto))
or
kind instanceof FalseEdge and
result = this.getTranslatedAwaitSuspend().getFirstInstruction(any(GotoEdge goto))
)
}
override Instruction getResult() { result = this.getTranslatedAwaitResume().getResult() }
final override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
child = this.getTranslatedOperand() and
result = this.getTranslatedAwaitReady().getFirstInstruction(kind)
or
child = this.getTranslatedAwaitReady() and
kind instanceof GotoEdge and
result = this.getInstruction(CoAwaitBranchTag())
or
child = this.getTranslatedAwaitSuspend() and
result = this.getTranslatedAwaitResume().getFirstInstruction(kind)
or
child = this.getTranslatedAwaitResume() and
result = this.getParent().getChildSuccessor(this, kind)
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = CoAwaitBranchTag() and
opcode instanceof Opcode::ConditionalBranch and
resultType = getVoidType()
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = CoAwaitBranchTag() and
operandTag instanceof ConditionOperandTag and
result = this.getTranslatedAwaitReady().getResult()
}
private TranslatedExpr getTranslatedOperand() {
result = getTranslatedExpr(this.getOperand().getFullyConverted())
}
private TranslatedExpr getTranslatedAwaitReady() {
result = getTranslatedExpr(this.getAwaitReady().getFullyConverted())
}
private TranslatedExpr getTranslatedAwaitResume() {
result = getTranslatedExpr(this.getAwaitResume().getFullyConverted())
}
private TranslatedExpr getTranslatedAwaitSuspend() {
result = getTranslatedExpr(this.getAwaitSuspend().getFullyConverted())
}
}
/** IR translation of `co_await`. */
class TranslatedCoAwaitExpr extends TranslatedCoExpr {
override CoAwaitExpr expr;
final override Expr getOperand() { result = expr.getOperand() }
final override Expr getAwaitReady() { result = expr.getAwaitReady() }
final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }
final override Expr getAwaitResume() { result = expr.getAwaitResume() }
}
/** IR translation of `co_yield`. */
class TranslatedCoYieldxpr extends TranslatedCoExpr {
override CoYieldExpr expr;
final override Expr getOperand() { result = expr.getOperand() }
final override Expr getAwaitReady() { result = expr.getAwaitReady() }
final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }
final override Expr getAwaitResume() { result = expr.getAwaitResume() }
}
abstract class TranslatedConversion extends TranslatedNonConstantExpr {
override Conversion expr;
@@ -1844,6 +1675,9 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr {
child = this.getRightOperand() and
result = this.getLeftOperand().getFirstInstruction(kind)
or
child = this.getRightOperand() and
result = this.getLeftOperand().getFirstInstruction(kind)
or
kind instanceof GotoEdge and
child = this.getLeftOperand() and
result = this.getInstruction(AssignmentStoreTag())
@@ -2165,13 +1999,6 @@ abstract class TranslatedAllocationSize extends TranslatedExpr, TTranslatedAlloc
final override predicate producesExprResult() { none() }
final override Instruction getResult() { result = this.getInstruction(AllocationSizeTag()) }
final override predicate handlesDestructorsExplicitly() {
// Since the enclosing `TranslatedNewOrNewArrayExpr` (implicitly) handles the destructors
// we need to disable the implicit handling here as otherwise the destructors will have
// multiple parents
any()
}
}
TranslatedAllocationSize getTranslatedAllocationSize(NewOrNewArrayExpr newExpr) {
@@ -2329,13 +2156,6 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
final override predicate producesExprResult() { none() }
final override predicate handlesDestructorsExplicitly() {
// Since the enclosing `TranslatedNewOrNewArrayExpr` (implicitly) handles the destructors
// we need to disable the implicit handling here as otherwise the destructors will have
// multiple parents
any()
}
override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = expr.getAllocator()
}
@@ -2997,68 +2817,6 @@ class TranslatedReuseExpr extends TranslatedNonConstantExpr {
}
}
/**
* The IR translation of the destructor calls of the parent `TranslatedThrow`.
*
* This object does not itself generate the destructor calls. Instead, its
* children provide the actual calls, and this object ensures that we correctly
* exit with an `ExceptionEdge` after executing all the destructor calls.
*/
class TranslatedDestructorsAfterThrow extends TranslatedElement, TTranslatedDestructorsAfterThrow {
ThrowExpr throw;
TranslatedDestructorsAfterThrow() { this = TTranslatedDestructorsAfterThrow(throw) }
override string toString() { result = "Destructor calls after throw: " + throw }
private TranslatedCall getTranslatedImplicitDestructorCall(int id) {
result.getExpr() = throw.getImplicitDestructorCall(id)
}
override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getChild(0).getFirstInstruction(kind)
}
override ThrowExpr getAst() { result = throw }
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
override TranslatedElement getChild(int id) {
result = this.getTranslatedImplicitDestructorCall(id)
}
override predicate handlesDestructorsExplicitly() { any() }
override Declaration getFunction() { result = throw.getEnclosingFunction() }
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
exists(int id | child = this.getChild(id) |
// Transition to the next child, if any.
result = this.getChild(id + 1).getFirstInstruction(kind)
or
// And otherwise, exit this element with an exceptional edge
not exists(this.getChild(id + 1)) and
kind instanceof ExceptionEdge and
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
)
}
override TranslatedElement getLastChild() {
result =
this.getTranslatedImplicitDestructorCall(max(int id |
exists(throw.getImplicitDestructorCall(id))
))
}
override Instruction getALastInstructionInternal() {
result = this.getLastChild().getALastInstruction()
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
}
/**
* IR translation of a `throw` expression.
*/
@@ -3073,22 +2831,13 @@ abstract class TranslatedThrowExpr extends TranslatedNonConstantExpr {
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
tag = ThrowTag() and
(
result = this.getDestructors().getFirstInstruction(kind)
or
not exists(this.getDestructors()) and
kind instanceof ExceptionEdge and
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
)
kind instanceof ExceptionEdge and
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
}
override Instruction getResult() { none() }
abstract Opcode getThrowOpcode();
override predicate handlesDestructorsExplicitly() { any() }
TranslatedDestructorsAfterThrow getDestructors() { result.getAst() = expr }
}
/**
@@ -3100,9 +2849,6 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, TranslatedVariableIn
final override TranslatedElement getChildInternal(int id) {
result = TranslatedVariableInitialization.super.getChildInternal(id)
or
id = max(int i | exists(TranslatedVariableInitialization.super.getChildInternal(i))) + 1 and
result = this.getDestructors()
}
final override Instruction getChildSuccessorInternal(TranslatedElement elem, EdgeKind kind) {
@@ -3168,22 +2914,14 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, TranslatedVariableIn
class TranslatedReThrowExpr extends TranslatedThrowExpr {
override ReThrowExpr expr;
override TranslatedElement getChildInternal(int id) {
id = 0 and
result = this.getDestructors()
}
override TranslatedElement getChildInternal(int id) { none() }
override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getInstruction(ThrowTag()) and
kind instanceof GotoEdge
}
override Instruction getALastInstructionInternal() {
result = this.getDestructors().getALastInstruction()
or
not this.hasAnImplicitDestructorCall() and
result = this.getInstruction(ThrowTag())
}
override Instruction getALastInstructionInternal() { result = this.getInstruction(ThrowTag()) }
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { none() }

View File

@@ -777,72 +777,6 @@ abstract class TranslatedHandler extends TranslatedStmt {
TranslatedStmt getBlock() { result = getTranslatedStmt(stmt.getBlock()) }
}
/**
* The IR translation of the destructor calls of the parent `TranslatedCatchByTypeHandler`.
*
* This object does not itself generate the destructor calls. Instead, its
* children provide the actual calls.
*/
class TranslatedDestructorsAfterHandler extends TranslatedElement,
TTranslatedDestructorsAfterHandler
{
Handler handler;
TranslatedDestructorsAfterHandler() { this = TTranslatedDestructorsAfterHandler(handler) }
override string toString() { result = "Destructor calls after handler: " + handler }
private TranslatedCall getTranslatedImplicitDestructorCall(int id) {
result.getExpr() = handler.getImplicitDestructorCall(id)
}
override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getChild(0).getFirstInstruction(kind)
}
override Handler getAst() { result = handler }
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
override TranslatedElement getChild(int id) {
result = this.getTranslatedImplicitDestructorCall(id)
}
override predicate handlesDestructorsExplicitly() { any() }
override Declaration getFunction() { result = handler.getEnclosingFunction() }
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
exists(int id | child = this.getChild(id) |
// Transition to the next child, if any.
result = this.getChild(id + 1).getFirstInstruction(kind)
or
// And otherwise go to the next handler, if any.
not exists(this.getChild(id + 1)) and
result =
getTranslatedStmt(handler)
.getParent()
.(TranslatedTryStmt)
.getNextHandler(getTranslatedStmt(handler), kind)
)
}
override TranslatedElement getLastChild() {
result =
this.getTranslatedImplicitDestructorCall(max(int id |
exists(handler.getImplicitDestructorCall(id))
))
}
override Instruction getALastInstructionInternal() {
result = this.getLastChild().getALastInstruction()
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
}
/**
* The IR translation of a C++ `catch` block that catches an exception with a
* specific type (e.g. `catch (const std::exception&)`).
@@ -856,14 +790,10 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
resultType = getVoidType()
}
override predicate handlesDestructorsExplicitly() { any() }
override TranslatedElement getChildInternal(int id) {
result = super.getChildInternal(id)
or
id = 0 and result = this.getParameter()
or
id = 1 and result = this.getDestructors()
}
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
@@ -880,9 +810,7 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
result = this.getParameter().getFirstInstruction(kind)
or
kind instanceof ExceptionEdge and
if exists(this.getDestructors())
then result = this.getDestructors().getFirstInstruction(any(GotoEdge edge))
else result = this.getParent().(TranslatedTryStmt).getNextHandler(this, any(GotoEdge edge))
result = this.getParent().(TranslatedTryStmt).getNextHandler(this, any(GotoEdge edge))
)
}
@@ -894,8 +822,6 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
private TranslatedParameter getParameter() {
result = getTranslatedParameter(stmt.getParameter())
}
private TranslatedDestructorsAfterHandler getDestructors() { result.getAst() = stmt }
}
/**
@@ -916,7 +842,9 @@ class TranslatedCatchAnyHandler extends TranslatedHandler {
}
}
abstract class TranslatedIfLikeStmt extends TranslatedStmt, ConditionContext {
class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
override IfStmt stmt;
override Instruction getFirstInstruction(EdgeKind kind) {
if this.hasInitialization()
then result = this.getInitialization().getFirstInstruction(kind)
@@ -929,8 +857,6 @@ abstract class TranslatedIfLikeStmt extends TranslatedStmt, ConditionContext {
override TranslatedElement getLastChild() { result = this.getElse() or result = this.getThen() }
override predicate handlesDestructorsExplicitly() { any() }
override TranslatedElement getChildInternal(int id) {
id = 0 and result = this.getInitialization()
or
@@ -941,21 +867,25 @@ abstract class TranslatedIfLikeStmt extends TranslatedStmt, ConditionContext {
id = 3 and result = this.getElse()
}
abstract predicate hasInitialization();
private predicate hasInitialization() { exists(stmt.getInitialization()) }
abstract TranslatedStmt getInitialization();
private TranslatedStmt getInitialization() {
result = getTranslatedStmt(stmt.getInitialization())
}
abstract TranslatedCondition getCondition();
private TranslatedCondition getCondition() {
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
}
private Instruction getFirstConditionInstruction(EdgeKind kind) {
result = this.getCondition().getFirstInstruction(kind)
}
abstract TranslatedStmt getThen();
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
abstract TranslatedStmt getElse();
private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
abstract predicate hasElse();
private predicate hasElse() { exists(stmt.getElse()) }
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
@@ -968,11 +898,7 @@ abstract class TranslatedIfLikeStmt extends TranslatedStmt, ConditionContext {
child = this.getCondition() and
if this.hasElse()
then result = this.getElse().getFirstInstruction(kind)
else (
if this.hasAnImplicitDestructorCall()
then result = this.getChild(this.getFirstDestructorCallIndex()).getFirstInstruction(kind)
else result = this.getParent().getChildSuccessor(this, kind)
)
else result = this.getParent().getChildSuccessor(this, kind)
}
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
@@ -980,24 +906,7 @@ abstract class TranslatedIfLikeStmt extends TranslatedStmt, ConditionContext {
result = this.getFirstConditionInstruction(kind)
or
(child = this.getThen() or child = this.getElse()) and
(
if this.hasAnImplicitDestructorCall()
then result = this.getChild(this.getFirstDestructorCallIndex()).getFirstInstruction(kind)
else result = this.getParent().getChildSuccessor(this, kind)
)
or
exists(int destructorId |
destructorId >= this.getFirstDestructorCallIndex() and
child = this.getChild(destructorId) and
result = this.getChild(destructorId + 1).getFirstInstruction(kind)
)
or
exists(int lastDestructorIndex |
lastDestructorIndex =
max(int n | exists(this.getChild(n)) and n >= this.getFirstDestructorCallIndex()) and
child = this.getChild(lastDestructorIndex) and
result = this.getParent().getChildSuccessor(this, kind)
)
result = this.getParent().getChildSuccessor(this, kind)
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -1005,44 +914,76 @@ abstract class TranslatedIfLikeStmt extends TranslatedStmt, ConditionContext {
}
}
class TranslatedIfStmt extends TranslatedIfLikeStmt {
override IfStmt stmt;
override predicate hasInitialization() { exists(stmt.getInitialization()) }
override TranslatedStmt getInitialization() {
result = getTranslatedStmt(stmt.getInitialization())
}
override TranslatedCondition getCondition() {
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
}
override TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
override TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
override predicate hasElse() { exists(stmt.getElse()) }
}
class TranslatedConstExprIfStmt extends TranslatedIfLikeStmt {
class TranslatedConstExprIfStmt extends TranslatedStmt, ConditionContext {
override ConstexprIfStmt stmt;
override predicate hasInitialization() { exists(stmt.getInitialization()) }
override Instruction getFirstInstruction(EdgeKind kind) {
if this.hasInitialization()
then result = this.getInitialization().getFirstInstruction(kind)
else result = this.getFirstConditionInstruction(kind)
}
override TranslatedStmt getInitialization() {
override TranslatedElement getChildInternal(int id) {
id = 0 and result = this.getInitialization()
or
id = 1 and result = this.getCondition()
or
id = 2 and result = this.getThen()
or
id = 3 and result = this.getElse()
}
private predicate hasInitialization() { exists(stmt.getInitialization()) }
private TranslatedStmt getInitialization() {
result = getTranslatedStmt(stmt.getInitialization())
}
override TranslatedCondition getCondition() {
private TranslatedCondition getCondition() {
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
}
override TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
private Instruction getFirstConditionInstruction(EdgeKind kind) {
result = this.getCondition().getFirstInstruction(kind)
}
override TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
override predicate hasElse() { exists(stmt.getElse()) }
private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
private predicate hasElse() { exists(stmt.getElse()) }
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildTrueSuccessor(TranslatedCondition child, EdgeKind kind) {
child = this.getCondition() and
result = this.getThen().getFirstInstruction(kind)
}
override Instruction getChildFalseSuccessor(TranslatedCondition child, EdgeKind kind) {
child = this.getCondition() and
if this.hasElse()
then result = this.getElse().getFirstInstruction(kind)
else result = this.getParent().getChildSuccessor(this, kind)
}
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
child = this.getInitialization() and
result = this.getFirstConditionInstruction(kind)
or
(child = this.getThen() or child = this.getElse()) and
result = this.getParent().getChildSuccessor(this, kind)
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
override Instruction getALastInstructionInternal() {
result = this.getThen().getALastInstruction()
or
result = this.getElse().getALastInstruction()
}
}
abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
@@ -1222,8 +1163,6 @@ class TranslatedForStmt extends TranslatedLoop {
class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
override RangeBasedForStmt stmt;
override predicate handlesDestructorsExplicitly() { any() }
override TranslatedElement getChildInternal(int id) {
id = 0 and result = this.getInitialization()
or
@@ -1277,19 +1216,6 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
or
child = this.getUpdate() and
result = this.getCondition().getFirstInstruction(kind)
or
exists(int destructorId |
destructorId >= this.getFirstDestructorCallIndex() and
child = this.getChild(destructorId) and
result = this.getChild(destructorId + 1).getFirstInstruction(kind)
)
or
exists(int lastDestructorIndex |
lastDestructorIndex =
max(int n | exists(this.getChild(n)) and n >= this.getFirstDestructorCallIndex()) and
child = this.getChild(lastDestructorIndex) and
result = this.getParent().getChildSuccessor(this, kind)
)
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -1305,9 +1231,7 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
override Instruction getChildFalseSuccessor(TranslatedCondition child, EdgeKind kind) {
child = this.getCondition() and
if this.hasAnImplicitDestructorCall()
then result = this.getChild(this.getFirstDestructorCallIndex()).getFirstInstruction(kind)
else result = this.getParent().getChildSuccessor(this, kind)
result = this.getParent().getChildSuccessor(this, kind)
}
private TranslatedDeclStmt getRangeVariableDeclStmt() {
@@ -1352,11 +1276,6 @@ class TranslatedJumpStmt extends TranslatedStmt {
override JumpStmt stmt;
override Instruction getFirstInstruction(EdgeKind kind) {
// The first instruction is a destructor call, if any.
result = this.getChildInternal(0).getFirstInstruction(kind)
or
// Otherwise, the first (and only) instruction is a `NoOp`
not exists(this.getChildInternal(0)) and
result = this.getInstruction(OnlyInstructionTag()) and
kind instanceof GotoEdge
}
@@ -1365,20 +1284,7 @@ class TranslatedJumpStmt extends TranslatedStmt {
result = this.getInstruction(OnlyInstructionTag())
}
private TranslatedCall getTranslatedImplicitDestructorCall(int id) {
result.getExpr() = stmt.getImplicitDestructorCall(id)
}
override TranslatedElement getLastChild() {
result =
this.getTranslatedImplicitDestructorCall(max(int id |
exists(stmt.getImplicitDestructorCall(id))
))
}
override TranslatedElement getChildInternal(int id) {
result = this.getTranslatedImplicitDestructorCall(id)
}
override TranslatedElement getChildInternal(int id) { none() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -1391,19 +1297,7 @@ class TranslatedJumpStmt extends TranslatedStmt {
result = getTranslatedStmt(stmt.getTarget()).getFirstInstruction(kind)
}
final override predicate handlesDestructorsExplicitly() { any() }
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
exists(int id | child = this.getChildInternal(id) |
// Transition to the next destructor call, if any.
result = this.getChildInternal(id + 1).getFirstInstruction(kind)
or
// And otherwise, exit this element by flowing to the target of the jump.
not exists(this.getChildInternal(id + 1)) and
kind instanceof GotoEdge and
result = this.getInstruction(OnlyInstructionTag())
)
}
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { none() }
}
private EdgeKind getCaseEdge(SwitchCase switchCase) {
@@ -1607,41 +1501,3 @@ class TranslatedVlaDeclarationStmt extends TranslatedStmt {
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { none() }
}
class TranslatedCoReturnStmt extends TranslatedStmt {
override CoReturnStmt stmt;
private TranslatedExpr getTranslatedOperand() {
result = getTranslatedExpr(stmt.getOperand().getFullyConverted())
}
override TranslatedExpr getChildInternal(int id) {
id = 0 and
result = this.getTranslatedOperand()
}
override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getTranslatedOperand().getFirstInstruction(kind)
}
override Instruction getALastInstructionInternal() {
result = this.getInstruction(OnlyInstructionTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::NoOp and
resultType = getVoidType()
}
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = this.getParent().getChildSuccessor(this, kind)
}
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
child = this.getTranslatedOperand() and
kind instanceof GotoEdge and
result = this.getInstruction(OnlyInstructionTag())
}
}

View File

@@ -17,11 +17,18 @@ private import Imports::IRType
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
* by the AST-to-IR translation (`IRTempVariable`).
*/
abstract private class AbstractIRVariable extends TIRVariable {
class IRVariable extends TIRVariable {
Language::Declaration func;
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
/** Gets a textual representation of this element. */
abstract string toString();
string toString() { none() }
/**
* Holds if this variable's value cannot be changed within a function. Currently used for string
@@ -42,13 +49,13 @@ abstract private class AbstractIRVariable extends TIRVariable {
/**
* Gets the type of the variable.
*/
abstract Language::LanguageType getLanguageType();
Language::LanguageType getLanguageType() { none() }
/**
* Gets the AST node that declared this variable, or that introduced this
* variable as part of the AST-to-IR translation.
*/
abstract Language::AST getAst();
Language::AST getAst() { none() }
/** DEPRECATED: Alias for getAst */
deprecated Language::AST getAST() { result = this.getAst() }
@@ -57,7 +64,7 @@ abstract private class AbstractIRVariable extends TIRVariable {
* Gets an identifier string for the variable. This identifier is unique
* within the function.
*/
abstract string getUniqueId();
string getUniqueId() { none() }
/**
* Gets the source location of this variable.
@@ -67,7 +74,7 @@ abstract private class AbstractIRVariable extends TIRVariable {
/**
* Gets the IR for the function that references this variable.
*/
final IRFunction getEnclosingIRFunction() { result.getFunction() = this.getEnclosingFunction() }
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
/**
* Gets the function that references this variable.
@@ -75,18 +82,10 @@ abstract private class AbstractIRVariable extends TIRVariable {
final Language::Declaration getEnclosingFunction() { result = func }
}
/**
* A variable referenced by the IR for a function.
*
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
* by the AST-to-IR translation (`IRTempVariable`).
*/
final class IRVariable = AbstractIRVariable;
/**
* A user-declared variable referenced by the IR for a function.
*/
class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
class IRUserVariable extends IRVariable, TIRUserVariable {
Language::Variable var;
Language::LanguageType type;
@@ -115,29 +114,26 @@ class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
* parameters, non-static local variables, and temporary variables.
*/
abstract private class AbstractIRAutomaticVariable extends AbstractIRVariable { }
/**
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
* parameters, non-static local variables, and temporary variables.
*/
final class IRAutomaticVariable = AbstractIRAutomaticVariable;
/**
* A user-declared variable that is allocated on the stack. This includes all parameters and
* non-static local variables.
*/
private class AbstractIRAutomaticUserVariable extends IRUserVariable, AbstractIRAutomaticVariable {
override Language::AutomaticVariable var;
final override Language::AutomaticVariable getVariable() { result = var }
class IRAutomaticVariable extends IRVariable {
IRAutomaticVariable() {
exists(Language::Variable var |
this = TIRUserVariable(var, _, func) and
Language::isVariableAutomatic(var)
)
or
this = TIRTempVariable(func, _, _, _)
}
}
/**
* A user-declared variable that is allocated on the stack. This includes all parameters and
* non-static local variables.
*/
final class IRAutomaticUserVariable = AbstractIRAutomaticUserVariable;
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
override Language::AutomaticVariable var;
final override Language::AutomaticVariable getVariable() { result = var }
}
/**
* A user-declared variable that is not allocated on the stack. This includes all global variables,
@@ -155,10 +151,16 @@ class IRStaticUserVariable extends IRUserVariable {
* A variable that is not user-declared. This includes temporary variables generated as part of IR
* construction, as well as string literals.
*/
abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
class IRGeneratedVariable extends IRVariable {
Language::AST ast;
Language::LanguageType type;
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
final override Language::AST getAst() { result = ast }
@@ -194,20 +196,12 @@ abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
string getBaseString() { none() }
}
/**
* A variable that is not user-declared. This includes temporary variables generated as part of IR
* construction, as well as string literals.
*/
final class IRGeneratedVariable = AbstractIRGeneratedVariable;
/**
* A temporary variable introduced by IR construction. The most common examples are the variable
* generated to hold the return value of a function, or the variable generated to hold the result of
* a condition operator (`a ? b : c`).
*/
class IRTempVariable extends AbstractIRGeneratedVariable, AbstractIRAutomaticVariable,
TIRTempVariable
{
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
TempVariableTag tag;
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
@@ -247,7 +241,7 @@ class IRThrowVariable extends IRTempVariable {
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
class IREllipsisVariable extends IRTempVariable, IRParameter {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
@@ -258,7 +252,7 @@ class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
/**
* A temporary variable generated to hold the `this` pointer.
*/
class IRThisVariable extends IRTempVariable, AbstractIRParameter {
class IRThisVariable extends IRTempVariable, IRParameter {
IRThisVariable() { tag = ThisTempVar() }
final override string toString() { result = "#this" }
@@ -270,7 +264,7 @@ class IRThisVariable extends IRTempVariable, AbstractIRParameter {
* A variable generated to represent the contents of a string literal. This variable acts much like
* a read-only global variable.
*/
class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
Language::StringLiteral literal;
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
@@ -294,7 +288,7 @@ class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynamicInitializationFlag {
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
@@ -320,24 +314,24 @@ class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynami
* An IR variable which acts like a function parameter, including positional parameters and the
* temporary variables generated for `this` and ellipsis parameters.
*/
abstract private class AbstractIRParameter extends AbstractIRAutomaticVariable {
class IRParameter extends IRAutomaticVariable {
IRParameter() {
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
or
this = TIRTempVariable(_, _, ThisTempVar(), _)
or
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
}
/**
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
*/
int getIndex() { none() }
}
/**
* An IR variable which acts like a function parameter, including positional parameters and the
* temporary variables generated for `this` and ellipsis parameters.
*/
final class IRParameter = AbstractIRParameter;
/**
* An IR variable representing a positional parameter.
*/
class IRPositionalParameter extends AbstractIRParameter, AbstractIRAutomaticUserVariable {
IRPositionalParameter() { this.getVariable() instanceof Language::Parameter }
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
}

View File

@@ -247,7 +247,8 @@ class Instruction extends Construction::TStageInstruction {
* Gets the type of the result produced by this instruction. If the instruction does not produce
* a result, its result type will be `IRVoidType`.
*/
final IRType getResultIRType() { result = Construction::getInstructionResultIRType(this) }
cached
final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() }
/**
* Gets the type of the result produced by this instruction. If the
@@ -994,8 +995,9 @@ class ConstantInstruction extends ConstantValueInstruction {
*/
class IntegerConstantInstruction extends ConstantInstruction {
IntegerConstantInstruction() {
exists(IRType resultType | resultType = this.getResultIRType() |
resultType instanceof IRIntegerType or resultType instanceof IRBooleanType
exists(IRType resultType |
resultType = this.getResultIRType() and
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
)
}
}
@@ -1007,17 +1009,6 @@ class FloatConstantInstruction extends ConstantInstruction {
FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType }
}
/**
* An instruction whose result is a constant value of a pointer type.
*/
class PointerConstantInstruction extends ConstantInstruction {
PointerConstantInstruction() {
exists(IRType resultType | resultType = this.getResultIRType() |
resultType instanceof IRAddressType or resultType instanceof IRFunctionAddressType
)
}
}
/**
* An instruction whose result is the address of a string literal.
*/

View File

@@ -429,11 +429,6 @@ private module Cached {
instr = unreachedInstruction(_) and result = Language::getVoidType()
}
cached
IRType getInstructionResultIRType(Instruction instr) {
result = instr.getResultLanguageType().getIRType()
}
/**
* Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
*

View File

@@ -1,6 +1,5 @@
private import implementations.Allocation
private import implementations.Deallocation
private import implementations.Fopen
private import implementations.Fread
private import implementations.Getenv
private import implementations.Gets
@@ -42,4 +41,4 @@ private import implementations.SqLite3
private import implementations.PostgreSql
private import implementations.System
private import implementations.StructuredExceptionHandling
private import implementations.ZMQ
private import implementations.Fopen

View File

@@ -112,21 +112,3 @@ private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunctio
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
}
/**
* A model for `getc` and similar functions that are flow sources.
*/
private class GetcSource extends SourceModelCsv {
override predicate row(string row) {
row =
[
";;false;getc;;;ReturnValue;remote", ";;false;getwc;;;ReturnValue;remote",
";;false;_getc_nolock;;;ReturnValue;remote", ";;false;_getwc_nolock;;;ReturnValue;remote",
";;false;getch;;;ReturnValue;local", ";;false;_getch;;;ReturnValue;local",
";;false;_getwch;;;ReturnValue;local", ";;false;_getch_nolock;;;ReturnValue;local",
";;false;_getwch_nolock;;;ReturnValue;local", ";;false;getchar;;;ReturnValue;local",
";;false;getwchar;;;ReturnValue;local", ";;false;_getchar_nolock;;;ReturnValue;local",
";;false;_getwchar_nolock;;;ReturnValue;local",
]
}
}

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